-
Notifications
You must be signed in to change notification settings - Fork 36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
make polygon a vector of rings #17
Comments
Having clear distinction between exterior ring and interior rings makes it easier to work when OGC geometry model is expected - WKT, WKB(postgis), boost::geometry. Explicit is better in this case. We used to have |
An "empty" geometry type is part of neither GeoJSON or OGC Simple Features. Clients that need a "empty" type can use std::experimental::optional or similar.
The further I get with integrating geometry.hpp and exploring how we'd write algorithms, the more I'm convinced that polygon should be a simple vector of rings. Uniformity and harmony with the other geometry.hpp types and with GeoJSON is more important than conformance with OGC in this case. Unless there are vehement objections, I'm going to pull 6b52596ec0efd0c5b6530782a79175428bce01f2 into the next release. |
Good to know your experience here @jfirebaugh. I've found this design without limitations myself, but I'm open to considering the merits of this. However I do cringe as it would require large updates to mapnik-vector-tile if we adapted to the latest geometry (https://github.com/mapbox/mapnik-vector-tile/search?utf8=%E2%9C%93&q=interior_rings) and the distinction has felt very useful. |
@springmeyer: from what I can see it will make mapnik code much shorter and simpler too. Every place that the same algorithm is repeated for |
Ooooo. Nice @jfirebaugh! 🙌 |
@jfirebaugh - one thought: In Mapnik, for best performance with polygons with lots of holes, we only use the exterior ring to determine the envelope: https://github.com/mapnik/mapnik/blob/d97583b53e134ca916aebbf9abbc4e8a5851d41f/include/mapnik/geometry_envelope_impl.hpp#L80-L95. How about specializing on multipolygon to avoid those extra loops? |
After thinking about this for some time I am not convinced this is the best decision. @artemp is right in that it is very hard to adapt this to something like boost. For example look at the code where I am trying to adapt boost geometry to geometry.hpp:
The problems here is that I am not able to provide easily a reference of a |
You can use a boost https://gist.github.com/jfirebaugh/c5e07939b6c4a5b975e20b966e371e3b |
@jfirebaugh - boost::geometry::intersection(bbox, polygon, result); error: no member named 'resize' in 'boost::iterator_range<std::__1::__wrap_iter<mapbox::geometry::linear_ring<double, std::vector> *> >'
interior_rings(destination).resize(
FYI, https://gist.github.com/jfirebaugh/c5e07939b6c4a5b975e20b966e371e3b - calling |
It seems that this will be inconvenient either way, especially when trying to work with boost geometry, but I'm still in favour of vector of rings — doing otherwise will make a lot of geojson-vt-cpp/geojson-cpp code much more verbose and complicated. |
@artemp This example compiles for me with Boost 1.61.0. In any case, these are proofs of concept -- whether or not it works for all boost geometry algorithms without additional wrappers or trait definitions, I think it's clear that the current definition of polygon does not preclude the use of boost, despite the fact that one of the reasons we started geometry.hpp was explicitly to avoid using boost geometry! There is zero doubt in my mind that the current design of polygon is the correct one, and I suspect that once you start to write or rewrite algorithms using it, you will come to appreciate the advantages of a uniform container structure for line strings, polygons, rings, and multi-geometries. |
@jfirebaugh The example compiles but :
container<mapbox::geometry::polygon<double>> result; which is what you'd expect as result from
|
I opened a ticket regarding the Regarding |
@jfirebaugh - https://gist.github.com/jfirebaugh/98e961c9be11990ac6aadea21bc1683e#file-boost3-cpp-L33 - unfortunately doesn't work, calling |
The desired interface is easily achieved by implementing "rings iterator" e.g namespace mapbox { namespace geometry {
template <typename T>
struct point
{
using coordinate_type = T;
point()
: x(), y()
{}
point(T x_, T y_)
: x(x_), y(y_)
{}
T x;
T y;
};
template <typename T>
bool operator==(point<T> const& lhs, point<T> const& rhs)
{
return lhs.x == rhs.x && lhs.y == rhs.y;
}
template <typename T>
bool operator!=(point<T> const& lhs, point<T> const& rhs)
{
return lhs.x != rhs.x || lhs.y != rhs.y;
}
template <typename T, template <typename...> class Cont = std::vector>
struct linear_ring : Cont<point<T>>
{
using coordinate_type = T;
using point_type = point<T>;
using container_type = Cont<point_type>;
using container_type::container_type;
};
template <typename T, template <typename...> class Cont = std::vector>
struct polygon
{
using coordinate_type = T;
using linear_ring_type = linear_ring<T>;
using container_type = Cont<linear_ring_type>;
linear_ring_type exterior_ring;
container_type interior_rings;
class rings_iterator : public boost::iterator_facade<rings_iterator,
linear_ring_type const,
boost::forward_traversal_tag>
{
public:
using value_type = linear_ring_type;
rings_iterator(std::size_t end)
: poly_(nullptr), pos_(end) {}
rings_iterator(polygon const& poly)
: poly_(&poly), pos_(0) {}
private:
friend class boost::iterator_core_access;
void increment() { ++pos_;}
void decrement() {} // no-op
void advance(typename boost::iterator_difference<rings_iterator>::type) {} // no-op
bool equal( rings_iterator const& other) const
{
return (pos_ == other.pos_);
}
value_type const& dereference() const
{
if (pos_ == 0) return poly_->exterior_ring;
else return poly_->interior_rings.at(pos_ - 1);
}
polygon const* poly_;
std::size_t pos_;
};
rings_iterator begin() { return rings_iterator(*this); }
rings_iterator end() { return rings_iterator(interior_rings.size() + 1); }
}; Usage mapbox::geometry::polygon<double> poly;
// .......
for (auto const& ring : poly)
{
std::cerr << ring.size() << std::endl;
} |
True. I think this goes back to the boost issue I filed. If |
https://github.com/mapnik/mapnik/blob/geometry-refactor/include/mapnik/geometry/polygon.hpp I have to say, I'm still totally unconvinced that not having |
@flippmoke @springmeyer @jfirebaugh |
polygon
currently has individualexterior_ring
andinterior_rings
members.It would be easier to work with in certain cases (notably GeoJSON serialization/deserialization) if it instead inherited from
std::vector<linear_ring<T>>
, where the exterior ring was element 0 and interior rings followed.The text was updated successfully, but these errors were encountered: