Updated reprojection code
This commit is contained in:
parent
54d8746aa8
commit
0fec41a87e
5 changed files with 733 additions and 160 deletions
3
Makefile
3
Makefile
|
@ -74,6 +74,9 @@ test-python:
|
|||
test-cpp:
|
||||
./tests/cpp_tests/run
|
||||
|
||||
test-cxx:
|
||||
./tests/cxx/run
|
||||
|
||||
check: test-local
|
||||
|
||||
bench:
|
||||
|
|
|
@ -27,16 +27,36 @@
|
|||
#include <mapnik/proj_transform.hpp>
|
||||
#include <mapnik/geometry.hpp>
|
||||
|
||||
// std
|
||||
#include <string>
|
||||
|
||||
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 <typename T>
|
||||
MAPNIK_DECL T reproject_copy(T const& geom, proj_transform const& proj_trans, unsigned int & n_err, bool reverse = false);
|
||||
|
||||
template <typename T>
|
||||
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 <typename T>
|
||||
MAPNIK_DECL bool reproject(T & geom, proj_transform const& proj_trans, bool reverse = false);
|
||||
|
||||
template <typename T>
|
||||
MAPNIK_DECL bool reproject(T & geom, projection const& source, projection const& dest);
|
||||
|
||||
} // end geometry ns
|
||||
|
||||
} // end mapnik ns
|
||||
|
||||
|
|
|
@ -274,6 +274,12 @@ struct unwrapper<recursive_wrapper<T>>
|
|||
{
|
||||
return obj.get();
|
||||
}
|
||||
|
||||
auto operator() (recursive_wrapper<T> & obj) const
|
||||
-> typename recursive_wrapper<T>::type &
|
||||
{
|
||||
return obj.get();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
|
|
|
@ -22,223 +22,283 @@
|
|||
|
||||
// mapnik
|
||||
#include <mapnik/geometry_reprojection.hpp>
|
||||
#include <mapnik/geometry.hpp>
|
||||
|
||||
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<geometry_empty>())
|
||||
{
|
||||
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<geometry::geometry_empty>())
|
||||
{
|
||||
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 <typename T>
|
||||
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 <typename T>
|
||||
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<line_string &>(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 <typename T>
|
||||
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 <typename T>
|
||||
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
|
||||
|
|
277
tests/cxx/geometry_reprojection.cpp
Normal file
277
tests/cxx/geometry_reprojection.cpp
Normal file
|
@ -0,0 +1,277 @@
|
|||
#include "catch.hpp"
|
||||
//#include "geometry_equal.hpp"
|
||||
|
||||
// mapnik
|
||||
#include <mapnik/geometry.hpp>
|
||||
#include <mapnik/projection.hpp>
|
||||
#include <mapnik/proj_transform.hpp>
|
||||
#include <mapnik/geometry_reprojection.hpp>
|
||||
#include <mapnik/geometry_correct.hpp>
|
||||
|
||||
// std
|
||||
#include <iostream>
|
||||
|
||||
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<geometry_empty>());
|
||||
// Transform in reverse
|
||||
new_geom = reproject_copy(geom, proj_trans, err, true);
|
||||
REQUIRE(err == 0);
|
||||
REQUIRE(new_geom.is<geometry_empty>());
|
||||
// Transform providing projections not transfrom
|
||||
new_geom = reproject_copy(geom, source, dest, err);
|
||||
REQUIRE(err == 0);
|
||||
REQUIRE(new_geom.is<geometry_empty>());
|
||||
// Transform providing projections in reverse
|
||||
new_geom = reproject_copy(geom, dest, source, err);
|
||||
REQUIRE(err == 0);
|
||||
REQUIRE(new_geom.is<geometry_empty>());
|
||||
// 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
|
Loading…
Reference in a new issue