-
Notifications
You must be signed in to change notification settings - Fork 1.3k
[core] Implement TileCover for polygonal regions #11267
Conversation
static Point<double> project(const LatLng& latLng, uint8_t zoom) { | ||
return project_(latLng, std::pow(2.0, zoom)); | ||
//Returns point on tile | ||
static Point<double> project(const LatLng& latLng, int32_t zoom) { |
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 method projects to a tile fraction and not to a tile-size based world. I'm not sure if it should be called project
or exposed from TileCoordinate
test/util/tile_cover.test.cpp
Outdated
}; | ||
|
||
//These are all different from the js tile-cover lib | ||
EXPECT_EQ(1742u, util::tileCover(spikyGeom, 10).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.
I suspect that this test has different results based on floating point precision difference between the JS and CPP implementations. It's hard to reproduce with a small test case.
9f6ae2c
to
aafac15
Compare
aafac15
to
852963f
Compare
852963f
to
fb53043
Compare
fb53043
to
4bda046
Compare
1265389
to
7cabdc3
Compare
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.
So it did work after all? Awesome!
src/mbgl/util/tile_cover_impl.cpp
Outdated
|
||
auto x_min = xps[0].x0; | ||
auto x_max = xps[0].x1; | ||
int8_t nzRule = xps[0].winding ? 1 : -1; |
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.
nzRule
can overflow if the scan line intersects a ton of line strings, right? Maybe worth bumping int8_t
to 16 or 32 just to be safe.
src/mbgl/util/tile_cover.cpp
Outdated
auto nw = Projection::project(bounds.northwest(), z); | ||
|
||
Polygon<double> p({ {sw, nw, ne, se, sw} }); | ||
impl = new TileCoverImpl(z, p, false); |
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.
Don't we want to use a smart pointer here instead?
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.
@asheemmamoowala Should we make the TileCover
header public? That way we can use it, for exampleas a return type for
OfflineRegionDefinition::tileCover()`.
src/mbgl/util/tile_cover.hpp
Outdated
@@ -3,6 +3,7 @@ | |||
#include <mbgl/tile/tile_id.hpp> | |||
#include <mbgl/style/types.hpp> | |||
#include <mbgl/util/tile_coordinate.hpp> |
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 header is not used here atm right? (only non-public header included here)
@ivovandongen What is the use case for If there is no public use case for the |
a05ef3c
to
8c6534b
Compare
@asheemmamoowala I'm getting 3 test failures:
See the test results here |
src/mbgl/util/tile_cover_impl.cpp
Outdated
} | ||
|
||
} // namespace util | ||
} // namespace mbgl |
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 set of files needs more documentation:
- explanation of what every function does, and how the algorithm works
- explanation of the cryptic variable names, or renaming those variable names to speak for themselves
Additionally, there are some code style changes before merging: - adhere to Mapbox GL naming standards:
CamelCase
for class names,lowerCamelCase
for members - apply principle of least access by making as many members private if possible
- make as many variables
const
as possible if they're not modified after initialization
src/mbgl/util/tile_cover.hpp
Outdated
bool next(); | ||
//Invokes the ScanLine callback to indicate tiles coverd for the row from [x0,x1) | ||
// ScanLine may be invoked with duplcaite or overlapping ranges. | ||
bool getTiles(ScanLine&); |
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 think this API should work a bit differently, more like a generator, or C++ iterator rather than a function that accepts a callback so that the caller can drive iteration, rather than needing to process an entire scanline at once.
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.
@kkaefer I considered implementing a ForwardIterator
, but found that std::iterator
s are required to be CopyConstructible
and CopyAssignable
. Copying the TileCover::Impl
state is non-trivial and would affect performance if the iterator was used incorrectly.
Per our chat, the streaming should be per-tile, and not per-row.
src/mbgl/util/tile_cover_impl.cpp
Outdated
if (next_pt == points.end()) { next_pt = std::next(points.begin()); } | ||
} | ||
//Re-close the linear ring | ||
points.erase(std::prev(points.end())); |
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 is what points.pop_back()
does?
src/mbgl/util/tile_cover_impl.cpp
Outdated
namespace util { | ||
|
||
void start_list_on_local_minimum(point_list& points) { | ||
assert(points.size() > 2); |
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.
Why can we assert that this is the case? What happens when iterating over a null geometry?
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 method is only called from build_edge_table
when the point list is for a closed geometry. null
geometry can be guarded against. It would be better to move the assert
to a more appropriate method where the closed rings are being handled.
…riables. Move elements out of the boundsMap into the activeBounds (instead of copy).
@ivovandongen Point and Multipoint should be fixed now. The |
@asheemmamoowala Great. I've rebased on your changes and adapted to the streaming tile cover in #11447 Since you've included the test cases in tile cover, I removed the redundant ones on OfflineRegionDefinition and instead added tests for encode/decode. |
The existing scanline-based tile cover implementation blocks when computing large rectangles. When extended to polygonal regions, this would further impact the performance and block interactivity in end-user applications.
This PR implements a streaming tile cover algorithm. An edge table-like data structure is created for all geometry, and then a modified scan-line is used for each tile row. Each row requires just one scan-line
while accumulating the x-extents for each edge within that row.
Usage:
util::tileCover
that accepts arbitrary geometry and returns tiles.cc @ivovandongen @mourner