extern crate pretty_assertions; use geo::Geometry; use geo_types::Coord; use pretty_assertions::assert_eq; use d3_geo_rs::data_object::sphere::Sphere; use d3_geo_rs::in_delta::coordinate as in_delta_coordinate; use d3_geo_rs::path::builder::Builder as PathBuilder; use d3_geo_rs::projection::mercator_transverse::MercatorTransverse; use d3_geo_rs::projection::Build; use d3_geo_rs::projection::CenterSet; use d3_geo_rs::projection::ClipExtentAdjust; use d3_geo_rs::projection::ClipExtentGet; use d3_geo_rs::projection::Fit; use d3_geo_rs::projection::PrecisionBypass; use d3_geo_rs::projection::RawBase; use d3_geo_rs::projection::RotateGet; use d3_geo_rs::projection::RotateSet; use d3_geo_rs::projection::ScaleGet; use d3_geo_rs::projection::ScaleSet; use d3_geo_rs::projection::TranslateGet; use d3_geo_rs::projection::TranslateSet; use d3_geo_rs::stream::DrainStub; use d3_geo_rs::Transform; // There are subtle mercator - mercatorTransverse / f32 issues // // see mt_clip_extent_defaults_to_automatic(). // and think about close to the poles ( PI /2 ) // // mercator transform looks like this // // return [log(tan((halfPi + phi) / 2)), -lambda]; // // in the test I have found problems with the limited resolution of f32 // // This causes error, looking at :- // in line_fn.rs:: line() // // let mut r = x0 - xa // r = r / dx // // The subtraction was evaluated differently. // // using f64 -> 10^9 - PI == 999999996.8584074 // using f32 -> 10^9 - PI resolved to 10^9 // // line() was whose output [&mut a] should be PI, PI // had a buggy output of 0, PI. #[test] fn mt_clip_extent_defaults_to_automatic() { println!("transverseMercator.clipExtent(null) sets the default automatic clip extent"); let mut pb = MercatorTransverse::builder(); pb.translate_set(&Coord { x: 0_f64, y: 0_f64 }); pb.scale_set(1_f64); let pb = pb.precision_bypass(); let projection = pb.build(); let path_builder = PathBuilder::pathstring(); let object = Sphere::::default(); let s = path_builder.build(projection).object(&object); assert_eq!(s, "M3.141593,3.141593L0,3.141593L-3.141593,3.141593L-3.141593,-3.141593L-3.141593,-3.141593L0,-3.141593L3.141593,-3.141593L3.141593,3.141593Z"); } #[test] fn center_set_the_automatic_clip_extent() { println!("transverseMercator.center(center) sets the correct automatic clip extent"); let mut pb = MercatorTransverse::builder(); pb.translate_set(&Coord { x: 0_f64, y: 0_f64 }); let pb = pb.scale_set(1_f64); let pb = pb .center_set(&Coord { x: 10_f64, y: 10_f64, }) .precision_bypass(); let projection = pb.build(); let path_builder = PathBuilder::pathstring(); let object = Sphere::::default(); let s = path_builder.build(projection).object(&object); // This string differs from the original javascript version a few trailing zeros have been removed. assert_eq!(s, "M2.966167,3.316126L-0.175426,3.316126L-3.317018,3.316126L-3.317019,-2.96706L-3.317019,-2.96706L-0.175426,-2.96706L2.966167,-2.96706L2.966167,3.316126Z"); } #[test] fn clip_extent_intersects() { println!("transverseMercator.clipExtent(extent) intersects the specified clip extent with the automatic clip extent"); let mut pb = MercatorTransverse::builder(); pb.translate_set(&Coord { x: 0_f64, y: 0_f64 }); pb.scale_set(1_f64); pb.clip_extent_adjust(&[ Coord { x: -10_f64, y: -10_f64, }, Coord { x: 10_f64, y: 10_f64, }, ]); let pb = pb.precision_bypass(); let projection = pb.build(); let path_builder = PathBuilder::pathstring(); let object = Sphere::::default(); let s = path_builder.build(projection).object(&object); assert_eq!(s, "M10,3.141593L0,3.141593L-10,3.141593L-10,-3.141593L-10,-3.141593L0,-3.141593L10,-3.141593L10,3.141593Z"); assert_eq!( pb.clip_extent(), [ Coord { x: -10_f64, y: -10_f64, }, Coord { x: 10_f64, y: 10_f64, }, ] ); } // it("transverseMercator.clipExtent(extent).scale(scale) updates the intersected clip extent", () => { // const projection = geoTransverseMercator().translate([0, 0]).clipExtent([[-10, -10], [10, 10]]).scale(1).precision(0); // assertPathEqual(geoPath(projection)({type: "Sphere"}), "M10,3.141593L0,3.141593L-10,3.141593L-10,-3.141593L-10,-3.141593L0,-3.141593L10,-3.141593L10,3.141593Z"); // assert.deepStrictEqual(projection.clipExtent(), [[-10, -10], [10, 10]]); // }); #[test] fn scale_updates_the_intersected_clip_extent() { println!("transverseMercator.clipExtent(extent).scale(scale) updates the intersected clip extent"); let mut pb = MercatorTransverse::builder(); pb.translate_set(&Coord { x: 0_f64, y: 0_f64 }); pb.clip_extent_adjust(&[ Coord { x: -10_f64, y: -10_f64, }, Coord { x: 10_f64, y: 10_f64, }, ]); pb.scale_set(1_f64); let pb = pb.precision_bypass(); let projection = pb.build(); let path_builder = PathBuilder::pathstring(); let object = Sphere::::default(); let s = path_builder.build(projection).object(&object); assert_eq!(s, "M10,3.141593L0,3.141593L-10,3.141593L-10,-3.141593L-10,-3.141593L0,-3.141593L10,-3.141593L10,3.141593Z"); assert_eq!( pb.clip_extent(), [ Coord { x: -10_f64, y: -10_f64, }, Coord { x: 10_f64, y: 10_f64, }, ] ); } #[test] fn translate_updates_the_intersected_clip_extent() { println!("transverseMercator.clipExtent(extent).translate(translate) updates the intersected clip extent"); let mut pb = MercatorTransverse::builder(); pb.scale_set(1_f64); pb.clip_extent_adjust(&[ Coord { x: -10_f64, y: -10_f64, }, Coord { x: 10_f64, y: 10_f64, }, ]); pb.translate_set(&Coord { x: 0_f64, y: 0_f64 }); let pb = pb.precision_bypass(); let projection = pb.build(); let path_builder = PathBuilder::pathstring(); let object = Sphere::::default(); let s = path_builder.build(projection).object(&object); assert_eq!(s, "M10,3.141593L0,3.141593L-10,3.141593L-10,-3.141593L-10,-3.141593L0,-3.141593L10,-3.141593L10,3.141593Z"); assert_eq!( pb.clip_extent(), [ Coord { x: -10_f64, y: -10_f64, }, Coord { x: 10_f64, y: 10_f64, }, ] ); } // it("transverseMercator.rotate(…) does not affect the automatic clip extent", () => { // const projection = geoTransverseMercator(), object = { // type: "MultiPoint", // coordinates: [ // [-82.35024908550241, 29.649391549778745], // [-82.35014449996858, 29.65075946917633], // [-82.34916073446641, 29.65070265688781], // [-82.3492653331286, 29.64933474064504] // ] // }; // projection.fitExtent([[0, 0], [960, 600]], object); // assert.deepStrictEqual(projection.scale(), 15724992.330511674); // assert.deepStrictEqual(projection.translate(), [20418843.897824813, 21088401.790971387]); // projection.rotate([0, 95]).fitExtent([[0, 0], [960, 600]], object); // assert.deepStrictEqual(projection.scale(), 15724992.330511674); // assert.deepStrictEqual(projection.translate(), [20418843.897824813, 47161426.43770847]); // }); #[test] fn rotate_does_not_affect_automatic_clip_extent() { println!("mercator.rotate(…) does not affect the automatic clip extent"); let object: Geometry = Geometry::MultiPoint( vec![ (-82.35024908550241, 29.649391549778745), (-82.35014449996858, 29.65075946917633), (-82.34916073446641, 29.65070265688781), (-82.3492653331286, 29.64933474064504), ] .into(), ); let pb = MercatorTransverse::builder(); let mut pb = pb.fit_extent( [ Coord { x: 0_f64, y: 0_f64 }, Coord { x: 960_f64, y: 600_f64, }, ], &object, ); assert_eq!(pb.scale(), 15724992.330511674_f64); // In the javascript original the x values differs in the least significant digit. // it was x: 20418843.897824813_f64, assert_eq!( pb.translate(), Coord { x: 20418843.897824816_f64, y: 21088401.790971387_f64 } ); pb.rotate2_set(&[0_f64, 95_f64]); let pb = pb.fit_extent( [ Coord { x: 0_f64, y: 0f64 }, Coord { x: 960_f64, y: 600_f64, }, ], &object, ); assert_eq!(pb.rotate(), [0_f64, 95_f64, 0_f64]); assert_eq!(pb.scale(), 15724992.330511674_f64); assert!(in_delta_coordinate( &pb.translate(), &Coord { x: 20418843.897824813_f64, y: 47161426.43770847_f64 }, 1e-6 )); } #[test] fn point_test() { println!( "has no direct equivalent in javascript, but this helped me debug." ); let p = MercatorTransverse::builder::>().build(); let t = p.transform(&Coord { x: 0_f64, y: 0_f64 }); assert_eq!( t, Coord { x: 480_f64, y: 250_f64 } ); let t = p.transform(&Coord { x: 55_f64, y: 3_f64, }); assert_eq!( t, Coord { x: 663.160624073884_f64, y: 235.49824637431624_f64 } ); }