Expand description
A port of d3/d3-geo into rust_d3_geo
.
A library with a wide range of geographic projections, spherical shapes and spherical trigonometry.
Features :-
-
Each projection builder supports - scaling, rotating and translation to yield the desired map view.
-
Large datasets can be resampled to reduce compute.
-
As well as displaying to a CANVAS element or SVG, various metric can be computed on the geometry such as Area, Centroids, and Bounds on polygons and lines.
TODO add note about stream pipelines and endpoints.
Available projections
AzimuthalEqualArea
AzimuthalEquiDistant
ConicEqualArea
Equirectangular
Gnomic
Orthographic
Mercator
MercatorTransverse
Stereographic
Each projection has default builder, which can be programmed.
Stereographic for example
use geo_types::Coord;
use d3_geo_rs::projection::Build;
use d3_geo_rs::projection::RawBase as ProjectionRawBase;
use d3_geo_rs::projection::stereographic::Stereographic;
use d3_geo_rs::projection::ClipAngleAdjust;
use d3_geo_rs::projection::PrecisionAdjust;
use d3_geo_rs::projection::ScaleSet;
use d3_geo_rs::projection::TranslateSet;
use d3_geo_rs::stream::DrainStub;
let stereographic = Stereographic::<DrainStub<f64>, f64>::builder()
.scale_set(100_f64)
.translate_set(&Coord {
x: 300_f64,
y: 300_f64,
})
.clip_angle(90_f64)
.precision_set(&10_f64)
.build();
Examples
The examples directory contains a large selection of applications demmonstration web applications rendering to a CANVAS or SVG elemments. It serves as a migration guide examples/projection shows each projction in turn, with the javascript and rust version drawn side by side.
examples/globe - demonstrates that this library can process larger datasets than is possible which javascript The javascript version operate on a 110m dataset of the globe while, the RUST version use a denser 50m dataset.
Here is code snippet from example/projection/globe/ showing the rendering of a globe.
extern crate js_sys;
extern crate rust_topojson_client;
extern crate topojson;
extern crate web_sys;
use geo_types::Coord;
use geo::Geometry;
use geo::MultiLineString;
use topojson::Topology;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use gloo_utils::format::JsValueSerdeExt;
use wasm_bindgen_futures::JsFuture;
use web_sys::Document;
use web_sys::*;
use d3_geo_rs::graticule::generate_mls;
use d3_geo_rs::path::builder::Builder as PathBuilder;
use d3_geo_rs::path::context::Context;
use d3_geo_rs::projection::orthographic::Orthographic;
use d3_geo_rs::projection::Build;
use d3_geo_rs::projection::RawBase as ProjectionRawBase;
use d3_geo_rs::projection::RotateSet;
use d3_geo_rs::projection::ScaleSet;
use d3_geo_rs::projection::TranslateSet;
use rust_topojson_client::feature::feature_from_name;
fn document() -> Result<Document, JsValue> {
let window = web_sys::window().unwrap();
Ok(window.document().unwrap())
}
/// Entry point
#[wasm_bindgen(start)]
pub async fn start() -> Result<(), JsValue> {
let document = document()?;
let window = web_sys::window().expect("Failed to get window");
// Get data from world map.
let mut opts = RequestInit::new();
opts.method("GET");
opts.mode(RequestMode::Cors);
let request = Request::new_with_str_and_init("/world-atlas/world/50m.json", &opts)?;
let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?;
let resp: Response = resp_value.dyn_into().unwrap();
let json = JsFuture::from(resp.json()?).await?;
let topology =
JsValueSerdeExt::into_serde::<Topology>(&json).expect("Did not get a valid Topology");
// Grab canvas.
let canvas = document
.get_element_by_id("c")
.unwrap()
.dyn_into::<web_sys::HtmlCanvasElement>()?;
let context_raw = canvas
.get_context("2d")?
.unwrap()
.dyn_into::<web_sys::CanvasRenderingContext2d>()?;
let width: f64 = canvas.width().into();
let height: f64 = canvas.height().into();
let countries = feature_from_name(&topology, "countries").expect("Did not extract geometry");
let context = Context::new(context_raw.clone());
let pb = PathBuilder::new(context);
let ortho = Orthographic::builder()
.scale_set(width as f64 / 1.3_f64 / std::f64::consts::PI)
.translate_set(&Coord {
x: width / 2_f64,
y: height / 2_f64,
})
.rotate2_set(&[270_f64, 00_f64])
.build();
let mut path = pb.build(ortho);
context_raw.set_stroke_style(&"#333".into());
context_raw.set_line_width(0.5);
path.object(&countries);
context_raw.stroke();
// Graticule
let graticule =
generate_mls::<f64>();
context_raw.begin_path();
context_raw.set_stroke_style(&"#ccc".into());
path.object(&graticule);
context_raw.stroke();
Ok(())
}