-
Notifications
You must be signed in to change notification settings - Fork 682
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
Isochrone PBF output and multipolygon support #4575
Isochrone PBF output and multipolygon support #4575
Conversation
repeated Geometry geometries = 2; // if polygon first one is outer rest are inners, though this is a problem when we allow multi location isochrones | ||
} | ||
|
||
message Interval { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than just having a repeated Contour
field that each holds a ring (or linestring), there are multiple intervals (one for each requested contour), each of which holds several contours (i.e. linestrings or polygons). If linestrings were requested, there'll be just one geometry; if polygons were requested the geometry field holds a number of rings, where the first is the exterior ring and the remaning are inner rings.
Especially useful for multi-location, but also for separated contours (e.g. when a ferry edge is part of the expansion)
src/tyr/isochrone_serializer.cc
Outdated
case Options_Format_pbf: | ||
return serializeIsochronePbf(request, intervals, contours); | ||
case Options_Format_json: | ||
if (polygons) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This still needs to change, probably should keep the current serialization as the default and only use the new method when explicitly requested by the user.
test/isochrone.cc
Outdated
ASSERT_EQ(actual_poly_size, expected_poly_size); | ||
for (uint32_t j = 0; j < actual_poly_size; ++j) { | ||
|
||
// we only compare exterior rings |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I noticed we never compare full geometry, as we leave out the holes. maybe check those as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's do that
src/tyr/isochrone_serializer.cc
Outdated
// iterate over outer rings and for each inner ring check if the inner ring is within the outer ring | ||
for (const auto inner : inner_ptrs) { | ||
// construct bbox | ||
AABB2<PointLL> inner_bbox(std::vector(inner->rbegin(), inner->rend())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AABB2
constructor expects vector, not list. there's probably a better way to solve this 😄
One bigger question to be discussed: would this warrant a request param to enable proper multipolygons and keep the "legacy"/current behavior by default? IMO our current GeoJSON polygon support is broken and this is actually fixing that bug. Apart from the fact that it's contours and not polygons and it's a crutch to begin with. But if we offer this we should do it as correctly as we can. Most libs seem to be forgiving enough to render them half ok-ish even now (with multiple outer rings per polygon feature..). Also I'd guess 99% of consumers use some library's GeoJSON implementation, feeding in just the full thing and getting lucky rendering it properly, so nothing would change for them. As @chrstnbwnkl found out, our own web app at https://github.com/gis-ops/valhalla-app needs a bit of work to handle this, but that's just because we weren't doing using leaflet properly for GeoJSON in the first place (using |
return results; | ||
} | ||
|
||
// iterate over outer rings and for each inner ring check if the inner ring is within the exterior |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the check can simply take a single point from the inner and do point in polygon on a given outer. i would consider using the simplified winding number algorithm as proposed in the graphics gems book under "An Incremental Angle Point in Polygon Test". here's a sample implementation of that: https://jeremytammik.github.io/tbc/a/0491_point_in_poly.htm
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the resource! I gave it a shot, and placed the implentation in midgard/util.cc
, maybe there's a better place to put it though
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks excellent pbf support and multipolygon with corrected inner outer relationships is awesome
…rone response polygons
…la into cb-isochrone-polygons
So there's probably some floating point issue in the ARM build again that impacts the contouring. Shape tolerance in the isochrone test is exceeded by a bit, but I'm hesitant to increase it again, since I just did that not too long ago in #4463. |
I don’t think we can do too much here and have to deal with it as it comes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good stuff:) just a few nits here and there from my side
* ~~Generating outer contours or contours with interior holes for regions that cannot be accessed within the specified time.~~ | ||
* ~~Options to control the minimum size of interior holes.~~ | ||
* Removing self intersections from polygonal contours. | ||
* ~~Removing self intersections from polygonal contours.~~ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🥳
// if the user requested linestrings, we'll give them linestrings | ||
if (!polygons || contours.size() < 2) { | ||
std::for_each(contours.begin(), contours.end(), | ||
[&results](const contour_t& c) { results.push_back({&c}); }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
probably we can reserve here with results.reserve(contours.size()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They're nested vectors, of which we don't know the size before runtime, so not sure this makes any difference
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You’re right, didn’t see the nesting
src/tyr/isochrone_serializer.cc
Outdated
|
||
// iterate over outer rings and for each inner ring check if the inner ring is within the exterior | ||
// ring | ||
for (const auto inner : inner_ptrs) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for (const auto inner : inner_ptrs) { | |
for (const auto* inner : inner_ptrs) { |
it's easy to get confused if we hide stuff in auto
src/tyr/isochrone_serializer.cc
Outdated
bool found_exterior = false; | ||
// go over exterior rings from smallest to largest | ||
for (size_t i = results.size(); i > 0; --i) { | ||
contour_t ext = *results[i - 1][0]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you don't want the pointer, rather take a ref to the underlying value or you do a copy
contour_t ext = *results[i - 1][0]; | |
const contour_t& ext = *results[i - 1][0]; |
src/midgard/util.cc
Outdated
|
||
template bool point_in_poly<valhalla::midgard::PointLL, std::list<valhalla::midgard::PointLL>>( | ||
const valhalla::midgard::PointLL&, | ||
std::list<valhalla::midgard::PointLL>&); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the polygon is not const
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
whoops, updated that
if (!found_exterior) { | ||
LOG_WARN("No exterior ring contour found for inner contour."); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
did you observe this? probably could happen in the contouring algo?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Haven't observed this yet, even after excessive playing around (with different graph extracts even)
src/tyr/isochrone_serializer.cc
Outdated
for (const auto inner : inner_ptrs) { | ||
|
||
// get the first point of the ring (could be any though) | ||
PointLL inner_pt = inner->front(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could also take a (const
) reference here
Issue
This PR builds on #4543 but takes it step further by grouping contour rings into multipolygons by means of pont in polygon checks.
Tasklist