diff --git a/Makefile b/Makefile index 20d1bc9cd..04de7aebf 100755 --- a/Makefile +++ b/Makefile @@ -74,6 +74,9 @@ test-python: test-cpp: ./tests/cpp_tests/run +test-cxx: + ./tests/cxx/run + check: test-local bench: diff --git a/include/mapnik/geometry_reprojection.hpp b/include/mapnik/geometry_reprojection.hpp index a533cc0be..a4e5198aa 100644 --- a/include/mapnik/geometry_reprojection.hpp +++ b/include/mapnik/geometry_reprojection.hpp @@ -27,16 +27,36 @@ #include #include -// std -#include namespace mapnik { -MAPNIK_DECL geometry::geometry reproject(geometry::geometry const& geom, proj_transform const& proj_trans, unsigned int & n_err, bool reverse = false); +namespace geometry { -MAPNIK_DECL geometry::geometry reproject(geometry::geometry const& geom, projection const& source, projection const& dest, unsigned int & n_err); +// There is a difference between reprojecting a const vs reprojecting a non const in behavior. If you reproject a +// const a new geometry (or perhaps empty geometry) will be returned no matter how many geometries fail to reproject. +// This is done this way so that large geometry collections that only have a few failing points or polygon parts could +// still be return with out the few failing projections. -MAPNIK_DECL geometry::geometry reproject(geometry::geometry const& geom, std::string const& source, std::string const& dest, unsigned int & n_err); +MAPNIK_DECL geometry reproject_copy(geometry const& geom, proj_transform const& proj_trans, unsigned int & n_err, bool reverse = false); + +template +MAPNIK_DECL T reproject_copy(T const& geom, proj_transform const& proj_trans, unsigned int & n_err, bool reverse = false); + +template +MAPNIK_DECL T reproject_copy(T const& geom, projection const& source, projection const& dest, unsigned int & n_err); + + +// No error count is required for a non const reprojection and this will reproject in place. +// because the reprojection is done on the same memory it is important to check if it succeeded, +// otherwise you could be dealing with a corrupt geometry. + +template +MAPNIK_DECL bool reproject(T & geom, proj_transform const& proj_trans, bool reverse = false); + +template +MAPNIK_DECL bool reproject(T & geom, projection const& source, projection const& dest); + +} // end geometry ns } // end mapnik ns diff --git a/include/mapnik/util/variant.hpp b/include/mapnik/util/variant.hpp index 90dd624ce..100088724 100644 --- a/include/mapnik/util/variant.hpp +++ b/include/mapnik/util/variant.hpp @@ -274,6 +274,12 @@ struct unwrapper> { return obj.get(); } + + auto operator() (recursive_wrapper & obj) const + -> typename recursive_wrapper::type & + { + return obj.get(); + } }; template diff --git a/src/geometry_reprojection.cpp b/src/geometry_reprojection.cpp index 3f4b20a7a..58883691f 100644 --- a/src/geometry_reprojection.cpp +++ b/src/geometry_reprojection.cpp @@ -22,223 +22,283 @@ // mapnik #include +#include namespace mapnik { +namespace geometry { + namespace detail { - -struct geom_reproj_visitor { - - geom_reproj_visitor(proj_transform const & proj_trans, unsigned int & n_err, bool reverse) - : proj_trans_(proj_trans), n_err_(n_err), reverse_(reverse) {} - - geometry::geometry operator() (geometry::geometry_empty const&) - { - return geometry::geometry(geometry::geometry_empty()); - } - bool trans_point(geometry::point & new_p) +geometry_empty reproject_internal(geometry_empty const&, proj_transform const&, unsigned int &, bool) +{ + return geometry_empty(); +} + +point reproject_internal(point const & p, proj_transform const& proj_trans, unsigned int & n_err, bool reverse) +{ + point new_p(p); + if (!reverse) { - if (!reverse_) + if (!proj_trans.forward(new_p)) { - if (!proj_trans_.forward(new_p)) - { - n_err_++; - return false; - } + n_err++; + } + } + else + { + if (!proj_trans.backward(new_p)) + { + n_err++; + } + } + return std::move(new_p); +} + +line_string reproject_internal(line_string const & ls, proj_transform const& proj_trans, unsigned int & n_err, bool reverse) +{ + line_string new_ls(ls); + unsigned int err = 0; + if (!reverse) + { + err = proj_trans.forward(new_ls); + } + else + { + err = proj_trans.backward(new_ls); + } + if (err > 0) + { + n_err += err; + } + return std::move(new_ls); +} + +polygon reproject_internal(polygon const & poly, proj_transform const& proj_trans, unsigned int & n_err, bool reverse) +{ + polygon new_poly; + linear_ring new_ext(poly.exterior_ring); + int err = 0; + if (!reverse) + { + err = proj_trans.forward(new_ext); + } + else + { + err = proj_trans.backward(new_ext); + } + // If the exterior ring doesn't transform don't bother with the holes. + if (err > 0) + { + n_err += err; + return std::move(new_poly); + } + new_poly.set_exterior_ring(std::move(new_ext)); + new_poly.interior_rings.reserve(poly.interior_rings.size()); + + for (auto const& lr : poly.interior_rings) + { + linear_ring new_lr(lr); + if (!reverse) + { + err = proj_trans.forward(new_lr); } else { - if (!proj_trans_.backward(new_p)) - { - n_err_++; - return false; - } - } - return true; - } - - geometry::geometry operator() (geometry::point const & p) - { - geometry::point new_p(p); - if (!trans_point(new_p)) - { - return geometry::geometry(geometry::geometry_empty()); - } - return geometry::geometry(std::move(new_p)); - } - - bool trans_ls(geometry::line_string & new_ls) - { - unsigned int err = 0; - if (!reverse_) - { - err = proj_trans_.forward(new_ls); - } - else - { - err = proj_trans_.backward(new_ls); + err = proj_trans.backward(new_lr); } if (err > 0) { - n_err_ += err; - return false; + n_err += err; + // If there is an error in interior ring drop + // it from polygon. + continue; } - return true; + new_poly.add_hole(std::move(new_lr)); } + return std::move(new_poly); +} - geometry::geometry operator() (geometry::line_string const & ls) +multi_point reproject_internal(multi_point const & mp, proj_transform const& proj_trans, unsigned int & n_err, bool reverse) +{ + if (proj_trans.is_known()) { - geometry::line_string new_ls(ls); - if (!trans_ls(new_ls)) + // If the projection is known we do them all at once because it is faster + // since we know that no point will fail reprojection + multi_point new_mp(mp); + if (!reverse) { - return geometry::geometry(geometry::geometry_empty()); - } - return geometry::geometry(std::move(new_ls)); - } - - bool trans_poly(geometry::polygon & new_poly, geometry::polygon const & poly) - { - geometry::linear_ring new_ext(poly.exterior_ring); - int err = 0; - if (!reverse_) - { - err = proj_trans_.forward(new_ext); + proj_trans.forward(new_mp); } else { - err = proj_trans_.backward(new_ext); + proj_trans.backward(new_mp); } - // If the exterior ring doesn't transform don't bother with the holes. - if (err > 0) + return std::move(new_mp); + } + multi_point new_mp; + new_mp.reserve(mp.size()); + for (auto const& p : mp) + { + point new_p(p); + if (!reverse) { - n_err_ += err; - return false; - } - new_poly.set_exterior_ring(std::move(new_ext)); - new_poly.interior_rings.reserve(poly.interior_rings.size()); - - for (auto const& lr : poly.interior_rings) - { - geometry::linear_ring new_lr(lr); - if (!reverse_) + if (!proj_trans.forward(new_p)) { - err = proj_trans_.forward(new_lr); + n_err++; } else { - err = proj_trans_.backward(new_lr); + new_mp.emplace_back(std::move(new_p)); } - if (err > 0) - { - n_err_ += err; - // If there is an error in interior ring drop - // it from polygon. - continue; - } - new_poly.add_hole(std::move(new_lr)); } - return true; + else + { + if (!proj_trans.backward(new_p)) + { + n_err++; + } + else + { + new_mp.emplace_back(std::move(new_p)); + } + } + } + return std::move(new_mp); +} + +multi_line_string reproject_internal(multi_line_string const & mls, proj_transform const& proj_trans, unsigned int & n_err, bool reverse) +{ + multi_line_string new_mls; + new_mls.reserve(mls.size()); + for (auto const& ls : mls) + { + line_string new_ls = reproject_internal(ls, proj_trans, n_err, reverse); + if (!new_ls.empty()) + { + new_mls.emplace_back(std::move(new_ls)); + } + } + return std::move(new_mls); +} + +multi_polygon reproject_internal(multi_polygon const & mpoly, proj_transform const& proj_trans, unsigned int & n_err, bool reverse) +{ + multi_polygon new_mpoly; + new_mpoly.reserve(mpoly.size()); + for (auto const& poly : mpoly) + { + polygon new_poly = reproject_internal(poly, proj_trans, n_err, reverse); + if (!new_poly.exterior_ring.empty()) + { + new_mpoly.emplace_back(std::move(new_poly)); + } + } + return std::move(new_mpoly); +} + +geometry_collection reproject_internal(geometry_collection const & c, proj_transform const& proj_trans, unsigned int & n_err, bool reverse) +{ + geometry_collection new_c; + new_c.reserve(c.size()); + for (auto const& g : c) + { + geometry new_g(std::move(reproject_copy(g, proj_trans, n_err, reverse))); + if (!new_g.is()) + { + new_c.emplace_back(std::move(new_g)); + } + } + return std::move(new_c); +} + +struct geom_reproj_copy_visitor { + + geom_reproj_copy_visitor(proj_transform const & proj_trans, + unsigned int & n_err, + bool reverse) + : proj_trans_(proj_trans), + n_err_(n_err), + reverse_(reverse) {} + + geometry operator() (geometry_empty const&) + { + return std::move(geometry_empty()); } - geometry::geometry operator() (geometry::polygon const & poly) + geometry operator() (point const& p) { - geometry::polygon new_poly; - if (!trans_poly(new_poly, poly)) + int intial_err = n_err_; + point new_p = reproject_internal(p, proj_trans_, n_err_, reverse_); + if (n_err_ > intial_err) { - return std::move(geometry::geometry_empty()); + return std::move(geometry_empty()); + } + return std::move(new_p); + } + + geometry operator() (line_string const& ls) + { + int intial_err = n_err_; + line_string new_ls = reproject_internal(ls, proj_trans_, n_err_, reverse_); + if (n_err_ > intial_err) + { + return std::move(geometry_empty()); + } + return std::move(new_ls); + } + + geometry operator() (polygon const& poly) + { + polygon new_poly = reproject_internal(poly, proj_trans_, n_err_, reverse_); + if (new_poly.exterior_ring.empty()) + { + return std::move(geometry_empty()); } return std::move(new_poly); } - geometry::geometry operator() (geometry::multi_point const & mp) + geometry operator() (multi_point const& mp) { - if (proj_trans_.is_known()) - { - geometry::multi_point new_mp(mp); - if (!trans_ls(new_mp)) - { - // should be impossible to reach this currently - /* LCOV_EXCL_START */ - return std::move(geometry::geometry_empty()); - /* LCOV_EXCL_END */ - } - return std::move(new_mp); - } - geometry::multi_point new_mp; - new_mp.reserve(mp.size()); - for (auto const& p : mp) - { - geometry::point new_p(p); - if (trans_point(new_p)) - { - new_mp.emplace_back(new_p); - } - } + multi_point new_mp = reproject_internal(mp, proj_trans_, n_err_, reverse_); if (new_mp.empty()) { - return std::move(geometry::geometry_empty()); + return std::move(geometry_empty()); } return std::move(new_mp); } - geometry::geometry operator() (geometry::multi_line_string const & mls) + geometry operator() (multi_line_string const& mls) { - geometry::multi_line_string new_mls; - new_mls.reserve(mls.size()); - for (auto const& ls : mls) - { - geometry::line_string new_ls(ls); - if (trans_ls(new_ls)) - { - new_mls.emplace_back(new_ls); - } - } + multi_line_string new_mls = reproject_internal(mls, proj_trans_, n_err_, reverse_); if (new_mls.empty()) { - return std::move(geometry::geometry_empty()); + return std::move(geometry_empty()); } return std::move(new_mls); } - geometry::geometry operator() (geometry::multi_polygon const & mpoly) + geometry operator() (multi_polygon const& mpoly) { - geometry::multi_polygon new_mpoly; - new_mpoly.reserve(mpoly.size()); - for (auto const& poly : mpoly) - { - geometry::polygon new_poly; - if (trans_poly(new_poly, poly)) - { - new_mpoly.emplace_back(new_poly); - } - } + multi_polygon new_mpoly = reproject_internal(mpoly, proj_trans_, n_err_, reverse_); if (new_mpoly.empty()) { - return std::move(geometry::geometry_empty()); + return std::move(geometry_empty()); } return std::move(new_mpoly); } - geometry::geometry operator() (geometry::geometry_collection const & c) + geometry operator() (geometry_collection const& c) { - geometry::geometry_collection new_c; - new_c.reserve(c.size()); - for (auto const& g : c) - { - geometry::geometry new_g(std::move(reproject(g, proj_trans_, n_err_, reverse_))); - if (!new_g.is()) - { - new_c.emplace_back(new_g); - } - } + geometry_collection new_c = reproject_internal(c, proj_trans_, n_err_, reverse_); if (new_c.empty()) { - return std::move(geometry::geometry_empty()); + return std::move(geometry_empty()); } return std::move(new_c); } + private: proj_transform const& proj_trans_; unsigned int & n_err_; @@ -248,10 +308,217 @@ struct geom_reproj_visitor { } // end detail ns -geometry::geometry reproject(geometry::geometry const& geom, proj_transform const& proj_trans, unsigned int & n_err, bool reverse) +geometry reproject_copy(geometry const& geom, proj_transform const& proj_trans, unsigned int & n_err, bool reverse) { - detail::geom_reproj_visitor visit(proj_trans, n_err, reverse); - return util::apply_visitor(visit, geom); + detail::geom_reproj_copy_visitor visit(proj_trans, n_err, reverse); + return mapnik::util::apply_visitor(visit, geom); } +template +T reproject_copy(T const& geom, proj_transform const& proj_trans, unsigned int & n_err, bool reverse) +{ + return detail::reproject_internal(geom, proj_trans, n_err, reverse); +} + +template MAPNIK_DECL geometry_empty reproject_copy(geometry_empty const& geom, proj_transform const& proj_trans, unsigned int & n_err, bool reverse); +template MAPNIK_DECL point reproject_copy(point const& geom, proj_transform const& proj_trans, unsigned int & n_err, bool reverse); +template MAPNIK_DECL line_string reproject_copy(line_string const& geom, proj_transform const& proj_trans, unsigned int & n_err, bool reverse); +template MAPNIK_DECL polygon reproject_copy(polygon const& geom, proj_transform const& proj_trans, unsigned int & n_err, bool reverse); +template MAPNIK_DECL multi_point reproject_copy(multi_point const& geom, proj_transform const& proj_trans, unsigned int & n_err, bool reverse); +template MAPNIK_DECL multi_line_string reproject_copy(multi_line_string const& geom, proj_transform const& proj_trans, unsigned int & n_err, bool reverse); +template MAPNIK_DECL multi_polygon reproject_copy(multi_polygon const& geom, proj_transform const& proj_trans, unsigned int & n_err, bool reverse); +template MAPNIK_DECL geometry_collection reproject_copy(geometry_collection const& geom, proj_transform const& proj_trans, unsigned int & n_err, bool reverse); + +template +T reproject_copy(T const& geom, projection const& source, projection const& dest, unsigned int & n_err) +{ + proj_transform proj_trans(source, dest); + return reproject_copy(geom, proj_trans, n_err, false); +} + +template MAPNIK_DECL geometry reproject_copy(geometry const& geom, projection const& source, projection const& dest, unsigned int & n_err); +template MAPNIK_DECL geometry_empty reproject_copy(geometry_empty const& geom, projection const& source, projection const& dest, unsigned int & n_err); +template MAPNIK_DECL point reproject_copy(point const& geom, projection const& source, projection const& dest, unsigned int & n_err); +template MAPNIK_DECL line_string reproject_copy(line_string const& geom, projection const& source, projection const& dest, unsigned int & n_err); +template MAPNIK_DECL polygon reproject_copy(polygon const& geom, projection const& source, projection const& dest, unsigned int & n_err); +template MAPNIK_DECL multi_point reproject_copy(multi_point const& geom, projection const& source, projection const& dest, unsigned int & n_err); +template MAPNIK_DECL multi_line_string reproject_copy(multi_line_string const& geom, projection const& source, projection const& dest, unsigned int & n_err); +template MAPNIK_DECL multi_polygon reproject_copy(multi_polygon const& geom, projection const& source, projection const& dest, unsigned int & n_err); +template MAPNIK_DECL geometry_collection reproject_copy(geometry_collection const& geom, projection const& source, projection const& dest, unsigned int & n_err); + +namespace detail { + +struct geom_reproj_visitor { + + geom_reproj_visitor(proj_transform const & proj_trans, bool reverse) + : proj_trans_(proj_trans), reverse_(reverse) {} + + bool operator() (geometry & geom) + { + return mapnik::util::apply_visitor((*this), geom); + } + + bool operator() (geometry_empty &) { return true; } + + bool operator() (point & p) + { + if (!reverse_) + { + if (!proj_trans_.forward(p)) + { + return false; + } + } + else + { + if (!proj_trans_.backward(p)) + { + return false; + } + } + return true; + } + + bool operator() (line_string & ls) + { + if (!reverse_) + { + if (proj_trans_.forward(ls) > 0) + { + return false; + } + } + else + { + if (proj_trans_.backward(ls) > 0) + { + return false; + } + } + return true; + } + + bool operator() (polygon & poly) + { + if (!reverse_) + { + if (proj_trans_.forward(poly.exterior_ring) > 0) + { + return false; + } + } + else + { + if (proj_trans_.backward(poly.exterior_ring) > 0) + { + return false; + } + } + + for (auto & lr : poly.interior_rings) + { + if (!reverse_) + { + if (proj_trans_.forward(lr) > 0) + { + return false; + } + } + else + { + if (proj_trans_.backward(lr) > 0) + { + return false; + } + } + } + return true; + } + + bool operator() (multi_point & mp) + { + return (*this) (static_cast(mp)); + } + + bool operator() (multi_line_string & mls) + { + for (auto & ls : mls) + { + if (! (*this) (ls)) + { + return false; + } + } + return true; + } + + bool operator() (multi_polygon & mpoly) + { + for (auto & poly : mpoly) + { + if(! (*this)(poly)) + { + return false; + } + } + return true; + } + + bool operator() (geometry_collection & c) + { + for (auto & g : c) + { + if (! (*this)(g) ) + { + return false; + } + } + return true; + } + + private: + proj_transform const& proj_trans_; + bool reverse_; + +}; + +} // end detail ns + +template +bool reproject(T & geom, proj_transform const& proj_trans, bool reverse) +{ + detail::geom_reproj_visitor visit(proj_trans, reverse); + return visit(geom); +} + +template MAPNIK_DECL bool reproject(geometry & geom, proj_transform const& proj_trans, bool reverse); +template MAPNIK_DECL bool reproject(geometry_empty & geom, proj_transform const& proj_trans, bool reverse); +template MAPNIK_DECL bool reproject(point & geom, proj_transform const& proj_trans, bool reverse); +template MAPNIK_DECL bool reproject(line_string & geom, proj_transform const& proj_trans, bool reverse); +template MAPNIK_DECL bool reproject(polygon & geom, proj_transform const& proj_trans, bool reverse); +template MAPNIK_DECL bool reproject(multi_point & geom, proj_transform const& proj_trans, bool reverse); +template MAPNIK_DECL bool reproject(multi_line_string & geom, proj_transform const& proj_trans, bool reverse); +template MAPNIK_DECL bool reproject(multi_polygon & geom, proj_transform const& proj_trans, bool reverse); +template MAPNIK_DECL bool reproject(geometry_collection & geom, proj_transform const& proj_trans, bool reverse); + +template +bool reproject(T & geom, projection const& source, projection const& dest) +{ + proj_transform proj_trans(source, dest); + detail::geom_reproj_visitor visit(proj_trans, false); + return visit(geom); +} + +template MAPNIK_DECL bool reproject(geometry & geom, projection const& source, projection const& dest); +template MAPNIK_DECL bool reproject(geometry_empty & geom, projection const& source, projection const& dest); +template MAPNIK_DECL bool reproject(point & geom, projection const& source, projection const& dest); +template MAPNIK_DECL bool reproject(line_string & geom, projection const& source, projection const& dest); +template MAPNIK_DECL bool reproject(polygon & geom, projection const& source, projection const& dest); +template MAPNIK_DECL bool reproject(multi_point & geom, projection const& source, projection const& dest); +template MAPNIK_DECL bool reproject(multi_line_string & geom, projection const& source, projection const& dest); +template MAPNIK_DECL bool reproject(multi_polygon & geom, projection const& source, projection const& dest); +template MAPNIK_DECL bool reproject(geometry_collection & geom, projection const& source, projection const& dest); + +} // end geometry ns + } // end mapnik ns diff --git a/tests/cxx/geometry_reprojection.cpp b/tests/cxx/geometry_reprojection.cpp new file mode 100644 index 000000000..ce40975f0 --- /dev/null +++ b/tests/cxx/geometry_reprojection.cpp @@ -0,0 +1,277 @@ +#include "catch.hpp" +//#include "geometry_equal.hpp" + +// mapnik +#include +#include +#include +#include +#include + +// std +#include + +TEST_CASE("geometry reprojection") { + +SECTION("test_projection_4326_3857 - Empty Geometry Object") { + using namespace mapnik::geometry; + mapnik::projection source("+init=epsg:4326"); + mapnik::projection dest("+init=epsg:3857"); + mapnik::proj_transform proj_trans(source, dest); + { + geometry_empty geom; + unsigned int err = 0; + // Test Standard Transform + geometry_empty new_geom = reproject_copy(geom, proj_trans, err); + REQUIRE(err == 0); + // Transform in reverse + new_geom = reproject_copy(geom, proj_trans, err, true); + REQUIRE(err == 0); + // Transform providing projections not transfrom + new_geom = reproject_copy(geom, source, dest, err); + REQUIRE(err == 0); + // Transform providing projections in reverse + new_geom = reproject_copy(geom, dest, source, err); + REQUIRE(err == 0); + // Transform in place + REQUIRE(reproject(new_geom, proj_trans)); + // Transform in place reverse + REQUIRE(reproject(new_geom, proj_trans, true)); + // Transform in place providing projections + REQUIRE(reproject(new_geom, source, dest)); + // Transform in place provoding projections reversed + REQUIRE(reproject(new_geom, dest, source)); + } +} // End Section + +SECTION("test_projection_4326_3857 - Empty Geometry in Geometry Variant") { + using namespace mapnik::geometry; + mapnik::projection source("+init=epsg:4326"); + mapnik::projection dest("+init=epsg:3857"); + mapnik::proj_transform proj_trans(source, dest); + { + geometry geom(std::move(geometry_empty())); + unsigned int err = 0; + // Test Standard Transform + geometry new_geom = reproject_copy(geom, proj_trans, err); + REQUIRE(err == 0); + REQUIRE(new_geom.is()); + // Transform in reverse + new_geom = reproject_copy(geom, proj_trans, err, true); + REQUIRE(err == 0); + REQUIRE(new_geom.is()); + // Transform providing projections not transfrom + new_geom = reproject_copy(geom, source, dest, err); + REQUIRE(err == 0); + REQUIRE(new_geom.is()); + // Transform providing projections in reverse + new_geom = reproject_copy(geom, dest, source, err); + REQUIRE(err == 0); + REQUIRE(new_geom.is()); + // Transform in place + REQUIRE(reproject(new_geom, proj_trans)); + // Transform in place reverse + REQUIRE(reproject(new_geom, proj_trans, true)); + // Transform in place providing projections + REQUIRE(reproject(new_geom, source, dest)); + // Transform in place provoding projections reversed + REQUIRE(reproject(new_geom, dest, source)); + } +} // End Section + +SECTION("test_projection_4326_3857 - Point Geometry Object") { + using namespace mapnik::geometry; + mapnik::projection source("+init=epsg:4326"); + mapnik::projection dest("+init=epsg:3857"); + mapnik::proj_transform proj_trans(source, dest); + { + point geom1(-97.552175, 35.522895); + point geom2(-10859458.446776, 4235169.496066); + unsigned int err = 0; + // Test Standard Transform + point new_geom = reproject_copy(geom1, proj_trans, err); + std::cout << std::fixed << new_geom.x << "," << std::fixed << new_geom.y << std::endl; + REQUIRE(err == 0); + REQUIRE(new_geom.x == Approx(geom2.x)); + REQUIRE(new_geom.y == Approx(geom2.y)); + // Transform in reverse + new_geom = reproject_copy(geom2, proj_trans, err, true); + REQUIRE(err == 0); + REQUIRE(new_geom.x == Approx(geom1.x)); + REQUIRE(new_geom.y == Approx(geom1.y)); + // Transform providing projections not transfrom + new_geom = reproject_copy(geom1, source, dest, err); + REQUIRE(err == 0); + REQUIRE(new_geom.x == Approx(geom2.x)); + REQUIRE(new_geom.y == Approx(geom2.y)); + // Transform providing projections in reverse + new_geom = reproject_copy(geom2, dest, source, err); + REQUIRE(err == 0); + REQUIRE(new_geom.x == Approx(geom1.x)); + REQUIRE(new_geom.y == Approx(geom1.y)); + // Transform in place + point geom3(-97.552175, 35.522895); + REQUIRE(reproject(geom3, proj_trans)); + REQUIRE(geom3.x == Approx(geom2.x)); + REQUIRE(geom3.y == Approx(geom2.y)); + // Transform in place reverse + REQUIRE(reproject(geom3, proj_trans, true)); + REQUIRE(geom3.x == Approx(geom1.x)); + REQUIRE(geom3.y == Approx(geom1.y)); + // Transform in place providing projections + REQUIRE(reproject(geom3, source, dest)); + REQUIRE(geom3.x == Approx(geom2.x)); + REQUIRE(geom3.y == Approx(geom2.y)); + // Transform in place provoding projections reversed + REQUIRE(reproject(geom3, dest, source)); + REQUIRE(geom3.x == Approx(geom1.x)); + REQUIRE(geom3.y == Approx(geom1.y)); + } +} // End Section +/* +SECTION("test_projection_4326_3857 - Point Geometry Object") { + using namespace mapnik::geometry; + mapnik::projection source("+init=epsg:4326"); + mapnik::projection dest("+init=epsg:3857"); + mapnik::proj_transform proj_trans(source, dest); + { + geometry geom1(point(-97.552175, 35.522895)); + geometry geom2(point(-10859458.446776, 4235169.496066)); + unsigned int err = 0; + // Test Standard Transform + point new_geom = reproject_copy(geom1, proj_trans, err); + std::cout << std::fixed << new_geom.x << "," << std::fixed << new_geom.y << std::endl; + REQUIRE(err == 0); + REQUIRE(new_geom.x == Approx(geom2_.x)); + REQUIRE(new_geom.y == Approx(geom2_.y)); + // Transform in reverse + new_geom = reproject_copy(geom2, proj_trans, err, true); + REQUIRE(err == 0); + REQUIRE(new_geom.x == Approx(geom1.x)); + REQUIRE(new_geom.y == Approx(geom1.y)); + // Transform providing projections not transfrom + new_geom = reproject_copy(geom1, source, dest, err); + REQUIRE(err == 0); + REQUIRE(new_geom.x == Approx(geom2.x)); + REQUIRE(new_geom.y == Approx(geom2.y)); + // Transform providing projections in reverse + new_geom = reproject_copy(geom2, dest, source, err); + REQUIRE(err == 0); + REQUIRE(new_geom.x == Approx(geom1.x)); + REQUIRE(new_geom.y == Approx(geom1.y)); + // Transform in place + point geom3(-97.552175, 35.522895); + REQUIRE(reproject(geom3, proj_trans)); + REQUIRE(geom3.x == Approx(geom2.x)); + REQUIRE(geom3.y == Approx(geom2.y)); + // Transform in place reverse + REQUIRE(reproject(geom3, proj_trans, true)); + REQUIRE(geom3.x == Approx(geom1.x)); + REQUIRE(geom3.y == Approx(geom1.y)); + // Transform in place providing projections + REQUIRE(reproject(geom3, source, dest)); + REQUIRE(geom3.x == Approx(geom2.x)); + REQUIRE(geom3.y == Approx(geom2.y)); + // Transform in place provoding projections reversed + REQUIRE(reproject(geom3, dest, source)); + REQUIRE(geom3.x == Approx(geom1.x)); + REQUIRE(geom3.y == Approx(geom1.y)); + } +} // End Section */ + /*{ + geometry geom(point(0,0)); + REQUIRE( mapnik::hit_test(geom,0,0,0) ); + } + { + geometry geom(point(0,0)); + REQUIRE( mapnik::hit_test(geom,1,0,1) ); + } + { + geometry geom(point(0,0)); + REQUIRE( mapnik::hit_test(geom,0,1,1) ); + } + { + geometry geom(point(0,0)); + REQUIRE( mapnik::hit_test(geom,1,1,1.5) ); + } + { + line_string line; + line.add_coord(0,0); + line.add_coord(1,1); + line.add_coord(2,2); + geometry geom(line); + REQUIRE( mapnik::hit_test(geom,0,0,1.5) ); + } + { + line_string line; + line.add_coord(0,0); + line.add_coord(1,1); + line.add_coord(2,2); + multi_line_string multi_line; + multi_line.emplace_back(std::move(line)); + geometry geom(multi_line); + REQUIRE( mapnik::hit_test(geom,0,0,1.5) ); + } + { + polygon poly; + linear_ring ring; + ring.add_coord(0,0); + ring.add_coord(-10,0); + ring.add_coord(-10,10); + ring.add_coord(0,10); + ring.add_coord(0,0); + poly.set_exterior_ring(std::move(ring)); + geometry geom(poly); + REQUIRE( mapnik::hit_test(geom,-5,5,0) ); + + multi_polygon mp; + mp.push_back(poly); + geometry geom_mp(mp); + REQUIRE( mapnik::hit_test(geom_mp,-5,5,0) ); + + correct(geom); + REQUIRE( mapnik::hit_test(geom,-5,5,0) ); + correct(geom_mp); + REQUIRE( mapnik::hit_test(geom_mp,-5,5,0) ); + + geometry_collection gc; + REQUIRE( !mapnik::hit_test(geometry(gc),-5,5,0) ); + gc.push_back(geom_mp); + REQUIRE( mapnik::hit_test(geometry(gc),-5,5,0) ); + REQUIRE( !mapnik::hit_test(geometry(gc),-50,-50,0) ); + gc.emplace_back(point(-50,-50)); + REQUIRE( mapnik::hit_test(geometry(gc),-50,-50,0) ); + } + + { + // polygon with hole + polygon poly; + linear_ring ring; + ring.add_coord(0,0); + ring.add_coord(-10,0); + ring.add_coord(-10,10); + ring.add_coord(0,10); + ring.add_coord(0,0); + poly.set_exterior_ring(std::move(ring)); + linear_ring hole; + hole.add_coord(-7,7); + hole.add_coord(-7,3); + hole.add_coord(-3,3); + hole.add_coord(-3,7); + hole.add_coord(-7,7); + poly.add_hole(std::move(hole)); + geometry geom(poly); + REQUIRE( !mapnik::hit_test(geom,-5,5,0) ); + // add another hole inside the first hole + // which should be considered a hit + linear_ring fill; + fill.add_coord(-6,4); + fill.add_coord(-6,6); + fill.add_coord(-4,6); + fill.add_coord(-4,4); + fill.add_coord(-6,4); + poly.add_hole(std::move(fill)); + REQUIRE( mapnik::hit_test(geometry(poly),-5,5,0) ); + }*/ + +} // End Testcase