#[cfg(not(tarpaulin_include))]
mod fit {
    extern crate pretty_assertions;
    extern crate rust_topojson_client;

    use core::fmt::Debug;
    use std::fs::File;

    use geo::polygon;
    use geo::CoordFloat;
    use geo::Geometry;
    use geo_types::Coord;
    use num_traits::FloatConst;
    use rust_topojson_client::feature::feature_from_name;
    use topojson::Topology;

    use d3_geo_rs::data_object::sphere::Sphere;
    use d3_geo_rs::in_delta::coordinate as in_delta_coordinate;
    use d3_geo_rs::in_delta::in_delta;
    use d3_geo_rs::projection::azimuthal_equal_area::AzimuthalEqualArea;
    use d3_geo_rs::projection::azimuthal_equidistant::AzimuthalEquiDistant;
    use d3_geo_rs::projection::builder_conic::ParallelsSet;
    use d3_geo_rs::projection::conformal::Conformal;
    use d3_geo_rs::projection::equal_area::EqualArea;
    use d3_geo_rs::projection::equidistant::Equidistant;
    use d3_geo_rs::projection::equirectangular::Equirectangular;
    use d3_geo_rs::projection::gnomic::Gnomic;
    use d3_geo_rs::projection::mercator::Mercator;
    use d3_geo_rs::projection::mercator_transverse::MercatorTransverse;
    use d3_geo_rs::projection::orthographic::Orthographic;
    use d3_geo_rs::projection::stereographic::Stereographic;
    use d3_geo_rs::projection::ClipAngleAdjust;
    use d3_geo_rs::projection::ClipAngleSet;
    use d3_geo_rs::projection::ClipExtentGet;
    use d3_geo_rs::projection::ClipExtentSet;
    use d3_geo_rs::projection::Fit;
    use d3_geo_rs::projection::PrecisionAdjust;
    use d3_geo_rs::projection::PrecisionBypass;
    use d3_geo_rs::projection::PrecisionGet;
    use d3_geo_rs::projection::RawBase;
    use d3_geo_rs::projection::RotateSet;
    use d3_geo_rs::projection::ScaleGet;
    use d3_geo_rs::projection::TranslateGet;

    ///  Helper function to extract world geometry from file.
    fn world<T>() -> Geometry<T>
    where
        T: CoordFloat + Debug + FloatConst,
    {
        let file =
            File::open("./tests/world-atlas/world/50m.json").expect("File should open read only.");
        let topology: Topology =
            serde_json::from_reader(file).expect("File should be parse as JSON.");

        if let Some(g) = feature_from_name(&topology, "land") {
            g
        } else {
            panic!("failed to file and decode from file.");
        }
    }

    #[test]
    fn fit_extent_sphere_equirectangular() {
        println!("projection.fitExtent(…) sphere equirectangular");
        let d_object = Sphere::default();
        let projection_raw = Equirectangular::builder();

        let projection = projection_raw.fit_extent(
            [
                Coord {
                    x: 50.0_f64,
                    y: 50.0_f64,
                },
                Coord {
                    x: 950.0_f64,
                    y: 950.0_f64,
                },
            ],
            &d_object,
        );
        assert!(in_delta(
            projection.scale(),
            900. / (2_f64 * std::f64::consts::PI),
            1e-6
        ));
        let translate = projection.translate();
        assert!(in_delta_coordinate(
            &translate,
            &Coord {
                x: 500_f64,
                y: 500_f64
            },
            1e-6
        ));
    }

    #[test]
    fn fit_extent_world_equirectangular() {
        println!("projection.fitExtent(…) world equirectangular");
        let world = world();

        let projection = Equirectangular::builder();
        let projection = projection.fit_extent(
            [
                Coord {
                    x: 50.0_f64,
                    y: 50.0_f64,
                },
                Coord {
                    x: 950.0_f64,
                    y: 950.0_f64,
                },
            ],
            &world,
        );
        assert!(in_delta(projection.scale(), 143.239449, 1e-6));
        assert!(in_delta_coordinate(
            &projection.translate(),
            &Coord {
                x: 500_f64,
                y: 492.000762_f64
            },
            1e-6
        ));
    }

    #[test]
    fn fit_extent_world_azimuthal_equal_area() {
        println!("projection.fitExtent(…) world azimuthalEqualArea");

        let world = world();
        let projection = AzimuthalEqualArea::builder();
        let projection = projection.fit_extent(
            [
                Coord {
                    x: 50.0_f64,
                    y: 50.0_f64,
                },
                Coord {
                    x: 950.0_f64,
                    y: 950.0_f64,
                },
            ],
            &world,
        );
        assert!(in_delta(projection.scale(), 228.357229, 1e-6));
        assert!(in_delta_coordinate(
            &projection.translate(),
            &Coord {
                x: 496.353618_f64,
                y: 479.684353_f64
            },
            1e-6
        ));
    }

    #[test]
    fn fit_extent_world_azimuthal_equidistant() {
        println!("projection.fitExtent(…) world azimuthalEquidistant");

        let world = world();
        let projection = AzimuthalEquiDistant::builder();
        let projection = projection.fit_extent(
            [
                Coord {
                    x: 50.0_f64,
                    y: 50.0_f64,
                },
                Coord {
                    x: 950.0_f64,
                    y: 950.0_f64,
                },
            ],
            &world,
        );
        assert!(in_delta(projection.scale(), 153.559317, 1e-6));
        assert!(in_delta_coordinate(
            &projection.translate(),
            &Coord {
                x: 485.272493_f64,
                y: 452.093375_f64
            },
            1e-6
        ));
    }

    // 	// 	// // // tape("projection.fitExtent(…) world conicConformal", function(test) {
    // 	// 	// // //   var projection = d3.geoConicConformal().clipAngle(30).parallels([30, 60]).rotate([0, -45]);
    // 	// 	// // //   projection.fitExtent([[50, 50], [950, 950]], world);
    // 	// 	// // //   test.inDelta(projection.scale(), 626.111027, 1e-6);
    // 	// 	// // //   test.inDelta(projection.translate(), [444.395951, 410.223799], 1e-6);
    // 	// 	// // //   test.end();
    // 	// 	// // // });

    #[test]
    fn fit_extent_world_conformal() {
        println!("projection.fitExtent(…) world conicConformal");

        let world = world();
        let mut projection = Conformal::builder().clip_angle_set(30_f64);
        let projection = projection
            .parallels_set(30_f64, 60_f64)
            .rotate2_set(&[0_f64, -45_f64]);

        let projection = projection.fit_extent(
            [
                Coord {
                    x: 50_f64,
                    y: 50_f64,
                },
                Coord {
                    x: 950_f64,
                    y: 950_f64,
                },
            ],
            &world,
        );
        assert!(in_delta(projection.scale(), 626.111027, 1e-6));
        assert!(in_delta_coordinate(
            &projection.translate(),
            &Coord {
                x: 444.395951,
                y: 410.223799_f64
            },
            1e-6
        ));
    }

    #[test]
    fn fit_size_world_equal_area() {
        println!("projection.fitExtent(…) world conicEqualArea");

        let world = world();
        let projection = EqualArea::builder();
        let projection = projection.fit_extent(
            [
                Coord {
                    x: 50_f64,
                    y: 50_f64,
                },
                Coord {
                    x: 950_f64,
                    y: 950_f64,
                },
            ],
            &world,
        );
        assert!(in_delta(projection.scale(), 145.862346, 1e-6));
        assert!(in_delta_coordinate(
            &projection.translate(),
            &Coord {
                x: 500_f64,
                y: 498.0114265_f64
            },
            1e-6
        ));
    }

    #[test]
    fn fit_size_world_equidistant() {
        println!("projection.fitSize(…) world equidistant");

        let world = world();
        let projection = Equidistant::builder();
        let projection = projection.fit_extent(
            [
                Coord {
                    x: 50_f64,
                    y: 50_f64,
                },
                Coord {
                    x: 950_f64,
                    y: 950_f64,
                },
            ],
            &world,
        );
        assert!(in_delta(projection.scale(), 123.085587, 1e-6));
        assert!(in_delta_coordinate(
            &projection.translate(),
            &Coord {
                x: 500_f64,
                y: 498.598401_f64
            },
            1e-6
        ));
    }

    #[test]
    fn fit_size_world_equirectangular() {
        println!("projection.fitSize(…) world equirectangular");

        let world = world();
        let projection = Equirectangular::builder();
        let projection = projection.fit_size(
            Coord {
                x: 900_f64,
                y: 900_f64,
            },
            &world,
        );
        assert!(in_delta(projection.scale(), 143.239449, 1e-6));
        assert!(in_delta_coordinate(
            &projection.translate(),
            &Coord {
                x: 450_f64,
                y: 442.000762_f64
            },
            1e-6
        ));
    }

    // Debug the scale() is good, translation() is bad.
    #[ignore]
    #[test]
    fn fit_extent_world_gnomic() {
        println!("projection.fitExtent(…) world gnomonic");

        let world = world();
        let mut projection = Gnomic::builder();
        projection.clip_angle(45_f64);
        let projection = projection.fit_extent(
            [
                Coord {
                    x: 50_f64,
                    y: 50_f64,
                },
                Coord {
                    x: 950_f64,
                    y: 950_f64,
                },
            ],
            &world,
        );
        assert!(in_delta(projection.scale(), 450.348233_f64, 1e-6));
        // TODO Must investigate the failure below.
        assert!(in_delta_coordinate(
            &projection.translate(),
            &Coord {
                x: 500.115138_f64,
                y: 556.522620_f64
            },
            1e-6
        ));
    }

    #[test]
    fn fit_extent_world_mercator() {
        println!("projection.fitExtent(…) world mercator");

        let world = world();
        let projection = Mercator::builder();
        let projection = projection.fit_extent(
            [
                Coord {
                    x: 50.0_f64,
                    y: 50.0_f64,
                },
                Coord {
                    x: 950.0_f64,
                    y: 950.0_f64,
                },
            ],
            &world,
        );
        assert!((in_delta(projection.scale(), 143.239449, 1e-6)));

        assert!(in_delta_coordinate(
            &projection.translate(),
            &Coord {
                x: 500_f64,
                y: 481.549457_f64
            },
            1e-6
        ));
    }

    #[test]
    fn fit_extent_world_orthographic() {
        println!("projection.fitExtent(…) world orthographic");

        let world = world();
        let projection = Orthographic::builder();
        let projection = projection.fit_extent(
            [
                Coord {
                    x: 50.0_f64,
                    y: 50.0_f64,
                },
                Coord {
                    x: 950.0_f64,
                    y: 950.0_f64,
                },
            ],
            &world,
        );
        assert!((in_delta(projection.scale(), 451.406773, 1e-6)));
        assert!(in_delta_coordinate(
            &projection.translate(),
            &Coord {
                x: 503.769179_f64,
                y: 498.593227_f64
            },
            1e-6
        ));
    }

    #[test]
    fn fit_size_world_orthographic() {
        println!("projection.fitSize(…) world orthographic");

        let world = world();
        let projection = Orthographic::builder();
        let projection = projection.fit_size(
            Coord {
                x: 900.0_f64,
                y: 900.0_f64,
            },
            &world,
        );
        assert!(in_delta(projection.scale(), 451.406773, 1e-6));
        assert!(in_delta_coordinate(
            &projection.translate(),
            &Coord {
                x: 453.769179_f64,
                y: 448.593227_f64
            },
            1e-6
        ));
    }

    #[test]
    fn fit_extent_world_stereographic() {
        println!("projection.fitExtent(…) world stereographic");

        let world = world();
        let projection = Stereographic::builder();
        let projection = projection.fit_extent(
            [
                Coord {
                    x: 50.0_f64,
                    y: 50.0_f64,
                },
                Coord {
                    x: 950.0_f64,
                    y: 950.0_f64,
                },
            ],
            &world,
        );
        assert!(in_delta(projection.scale(), 162.934379_f64, 1e-6));
        assert!(in_delta_coordinate(
            &projection.translate(),
            &Coord {
                x: 478.546293_f64,
                y: 432.922534_f64
            },
            1e-6
        ));
    }

    #[test]
    fn fit_extent_world_transverse_mercator() {
        println!("projection.fitExtent(…) world transverseMercator");

        let world = world();
        let projection = MercatorTransverse::builder();
        let projection = projection.fit_extent(
            [
                Coord {
                    x: 50.0_f64,
                    y: 50.0_f64,
                },
                Coord {
                    x: 950.0_f64,
                    y: 950.0_f64,
                },
            ],
            &world,
        );
        assert!(in_delta(projection.scale(), 143.239449, 1e-6));
        let t = projection.translate();
        assert!(in_delta_coordinate(
            &t,
            &Coord {
                x: 473.829551_f64,
                y: 500_f64
            },
            1e-6
        ));
    }

    // 	// 	// // // tape("projection.fitExtent(…) USA albersUsa", function(test) {
    // 	// 	// // //   var projection = d3.geoAlbersUsa();
    // 	// 	// // //   projection.fitExtent([[50, 50], [950, 950]], us);
    // 	// 	// // //   test.inDelta(projection.scale(), 1152.889035, 1e-6);
    // 	// 	// // //   test.inDelta(projection.translate(), [533.52541, 496.232028], 1e-6);
    // 	// 	// // //   test.end();
    // 	// 	// // // });

    // 	// 	// // // tape("projection.fitExtent(…) null geometries - Feature", function(test) {
    // 	// 	// // //   var projection = d3.geoEquirectangular();
    // 	// 	// // //   projection.fitExtent([[50, 50], [950, 950]], {type: "Feature", geometry: null});
    // 	// 	// // //   var s = projection.scale(), t = projection.translate();
    // 	// 	// // //   test.assert(!s);
    // 	// 	// // //   test.assert(isNaN(t[0]));
    // 	// 	// // //   test.assert(isNaN(t[1]));
    // 	// 	// // //   test.end();
    // 	// 	// // // });

    // 	// 	// // // tape("projection.fitExtent(…) null geometries - MultiPoint", function(test) {
    // 	// 	// // //   var projection = d3.geoEquirectangular();
    // 	// 	// // //   projection.fitExtent([[50, 50], [950, 950]], {type: "MultiPoint", coordinates: []});
    // 	// 	// // //   var s = projection.scale(), t = projection.translate();
    // 	// 	// // //   test.assert(!s);
    // 	// 	// // //   test.assert(isNaN(t[0]));
    // 	// 	// // //   test.assert(isNaN(t[1]));
    // 	// 	// // //   test.end();
    // 	// 	// // // });

    // 	// 	// // // tape("projection.fitExtent(…) null geometries - MultiLineString", function(test) {
    // 	// 	// // //   var projection = d3.geoEquirectangular();
    // 	// 	// // //   projection.fitExtent([[50, 50], [950, 950]], {type: "MultiLineString", coordinates: []});
    // 	// 	// // //   var s = projection.scale(), t = projection.translate();
    // 	// 	// // //   test.assert(!s);
    // 	// 	// // //   test.assert(isNaN(t[0]));
    // 	// 	// // //   test.assert(isNaN(t[1]));
    // 	// 	// // //   test.end();
    // 	// 	// // // });

    // 	// 	// // // tape("projection.fitExtent(…) null geometries - MultiPolygon", function(test) {
    // 	// 	// // //   var projection = d3.geoEquirectangular();
    // 	// 	// // //   projection.fitExtent([[50, 50], [950, 950]], {type: "MultiPolygon", coordinates: []});
    // 	// 	// // //   var s = projection.scale(), t = projection.translate();
    // 	// 	// // //   test.assert(!s);
    // 	// 	// // //   test.assert(isNaN(t[0]));
    // 	// 	// // //   test.assert(isNaN(t[1]));
    // 	// 	// // //   test.end();
    // 	// 	// // // });

    // 	// 	// // // tape("projection.fitExtent(…) custom projection", function(test) {
    // 	// 	// // //   var projection = d3.geoProjection(function(x, y) { return [x, Math.pow(y, 3)]; });
    // 	// 	// // //   projection.fitExtent([[50, 50], [950, 950]], world);
    // 	// 	// // //   test.inDelta(projection.scale(), 128.903525, 1e-6);
    // 	// 	// // //   test.inDelta(projection.translate(), [500, 450.414357], 1e-6);
    // 	// 	// // //   test.end();
    // 	// 	// // // });

    #[test]
    fn fit_size_ignore_clip_extent() {
        println!("projection.fitSize(…) ignore clipExtent - world equirectangular");
        let world = world();
        let p1 = Equirectangular::builder();
        let p1 = p1.fit_size(
            Coord {
                x: 1000_f64,
                y: 1000_f64,
            },
            &world,
        );
        let s1 = p1.scale();
        let t1 = p1.translate();

        assert!(in_delta_coordinate(
            &t1,
            &Coord {
                x: 500_f64,
                y: 491.11195746522424_f64
            },
            1e-6
        ));
        assert!(in_delta(s1, 159.15494309189532f64, 1e-6));
        let p2 = Equirectangular::builder();
        let p2 = p2.clip_extent_set(&[
            Coord {
                x: 100_f64,
                y: 200_f64,
            },
            Coord {
                x: 700_f64,
                y: 600_f64,
            },
        ]);
        let p2 = p2.fit_size(
            Coord {
                x: 1000_f64,
                y: 1000_f64,
            },
            &world,
        );
        let s2 = p2.scale();
        let t2 = p2.translate();
        let c2 = p2.clip_extent();
        assert!(in_delta(s2, 159.15494309189532f64, 1e-6));
        assert!(in_delta(s1, s2, 1e-6));

        assert!(in_delta_coordinate(&t1, &t2, 1e-6));

        assert!(in_delta_coordinate(
            &t2,
            &Coord {
                x: 500_f64,
                y: 491.11195746522424_f64
            },
            1e-6
        ));
        assert!(in_delta_coordinate(
            &c2[0],
            &Coord {
                x: 100_f64,
                y: 200_f64
            },
            1e-6
        ));
        assert!(in_delta_coordinate(
            &c2[1],
            &Coord {
                x: 700_f64,
                y: 600_f64
            },
            1e-6
        ));
    }

    // 	// 	// // // tape("projection.fitExtent(…) chaining - world transverseMercator", function(test) {
    // 	// 	// // //   var projection = d3.geoTransverseMercator().fitExtent([[50, 50], [950, 950]], world).scale(500);
    // 	// 	// // //   test.equal(projection.scale(), 500);
    // 	// 	// // //   test.inDelta(projection.translate(), [473.829551, 500], 1e-6);
    // 	// 	// // //   test.end();
    // 	// 	// // // });

    #[test]
    fn fit_size_resampling() {
        println!("projection.fitSize(…) resampling - world mercator");
        let box_object = Geometry::Polygon(polygon![
            (x: -135f64, y: 45f64 ),
            (x: -45f64, y: 45f64 ),
            (x: -45f64, y: -45f64 ),
            (x: -135f64, y: -45f64 ),
            (x: -135f64, y: 45f64 )
        ]);
        let mut p1 = Mercator::builder();

        let p1 = p1.precision_set(&0.1_f64).fit_size(
            Coord {
                x: 1000_f64,
                y: 1000_f64,
            },
            &box_object,
        );
        let p2 = Mercator::builder();
        let p2 = p2.precision_bypass();
        let p2 = p2.fit_size(
            Coord {
                x: 1000_f64,
                y: 1000_f64,
            },
            &box_object,
        );
        let t1 = p1.translate();
        let t2 = p2.translate();
        assert_eq!(p1.precision(), 0.1_f64);
        assert!(in_delta(p1.scale(), 436.218018, 1e-6));
        assert!(in_delta(p2.scale(), 567.296328, 1e-6));
        assert!(in_delta(t1.x, 1185.209661_f64, 1e-6));
        assert!(in_delta(t2.x, 1391.106989_f64, 1e-6));
        assert!(in_delta(t1.y, 500_f64, 1e-6));
        assert!(in_delta(t1.y, t2.y, 1e-6));
    }

    #[test]
    fn fit_width_world_equirectangular() {
        println!("projection.fitWidth(…) world equirectangular");

        let world = world();
        let projection = Equirectangular::builder();
        let projection = projection.fit_width(900_f64, &world);
        assert!(in_delta(projection.scale(), 143.239449_f64, 1e-6));
        assert!(in_delta_coordinate(
            &projection.translate(),
            &Coord {
                x: 450_f64,
                y: 208.999023_f64
            },
            1e-6
        ));
    }

    // 	// 	// // // tape("projection.fitWidth(…) world transverseMercator", function(test) {
    // 	// 	// // //   var projection = d3.geoTransverseMercator();
    // 	// 	// // //   projection.fitWidth(900, world);
    // 	// 	// // //   test.inDelta(projection.scale(), 166.239257, 1e-6);
    // 	// 	// // //   test.inDelta(projection.translate(), [419.627390, 522.256029], 1e-6);
    // 	// 	// // //   test.end();
    // 	// 	// // // });
    #[test]
    fn fit_width_world_mercator_transverse() {
        println!("projection.fitWidth(…) world transverseMercator");

        let world = world();
        let projection = MercatorTransverse::builder();
        let projection = projection.fit_width(900f64, &world);
        assert!(in_delta(projection.scale(), 166.239257, 1e-6));
        assert!(in_delta_coordinate(
            &projection.translate(),
            &Coord {
                x: 419.627390_f64,
                y: 522.256029_f64
            },
            1e-6
        ));
    }

    // 	// 	// // // tape("projection.fitWidth(…) USA albersUsa", function(test) {
    // 	// 	// // //   var projection = d3.geoAlbersUsa();
    // 	// 	// // //   projection.fitWidth(900, us);
    // 	// 	// // //   test.inDelta(projection.scale(), 1152.889035, 1e-6);
    // 	// 	// // //   test.inDelta(projection.translate(), [483.52541, 257.736905], 1e-6);
    // 	// 	// // //   test.end();
    // 	// 	// // // });

    #[test]
    fn fit_height_world_equirectangular() {
        println!("projection.fitHeight(…) world equirectangular");

        let world = world();
        let projection = Equirectangular::builder();
        let projection = projection.fit_height(900f64, &world);
        assert!(in_delta(projection.scale(), 297.042711_f64, 1e-6));
        assert!(in_delta_coordinate(
            &projection.translate(),
            &Coord {
                x: 933.187199_f64,
                y: 433.411585_f64
            },
            1e-6
        ));
    }

    #[test]
    fn fit_height_world_mercator_transverse() {
        println!("projection.fitHeight(…) world transverseMercator");

        let world = world();
        let projection = MercatorTransverse::builder();
        let projection = projection.fit_height(900_f64, &world);
        assert!(in_delta(projection.scale(), 143.239449, 1e-6));
        let t = projection.translate();
        assert!(in_delta_coordinate(
            &t,
            &Coord {
                x: 361.570408_f64,
                y: 450_f64
            },
            1e-6
        ));
    }

    // 	// 	// // // tape("projection.fitHeight(…) USA albersUsa", function(test) {
    // 	// 	// // //   var projection = d3.geoAlbersUsa();
    // 	// 	// // //   projection.fitHeight(900, us);
    // 	// 	// // //   test.inDelta(projection.scale(), 1983.902059, 1e-6);
    // 	// 	// // //   test.inDelta(projection.translate(), [832.054974, 443.516038], 1e-6);
    // 	// 	// // //   test.end();
    // 	// 	// // // });
}
