From 7cabdc39209263e2a714126029fd35371fe6a58d Mon Sep 17 00:00:00 2001 From: Asheem Mamoowala Date: Mon, 26 Mar 2018 13:11:10 -0700 Subject: [PATCH] Use points directly, instead of building edges. --- src/mbgl/util/tile_cover_impl.cpp | 303 ++++++++++++++---------------- src/mbgl/util/tile_cover_impl.hpp | 77 +++----- test/util/tile_cover.test.cpp | 224 ++++++++++------------ 3 files changed, 273 insertions(+), 331 deletions(-) diff --git a/src/mbgl/util/tile_cover_impl.cpp b/src/mbgl/util/tile_cover_impl.cpp index 64b655a0011..e7f5e548a24 100644 --- a/src/mbgl/util/tile_cover_impl.cpp +++ b/src/mbgl/util/tile_cover_impl.cpp @@ -1,6 +1,5 @@ #include -#include #include #include #include @@ -10,102 +9,111 @@ namespace mbgl { namespace util { -//namespace { -void start_list_on_local_minimum(edge_list& edges) { - if (edges.size() <= 2) { - return; - } +void start_list_on_local_minimum(point_list& points) { + assert(points.size() > 2); // Find the first local minimum going forward in the list - auto prev_edge = edges.end(); - --prev_edge; - auto edge = edges.begin(); - - while (edge != edges.end()) { - if (edge->p0 == prev_edge->p0) { + auto prev_pt = std::prev(points.end(), 2); + auto pt = points.begin(); + auto next_pt = std::next(pt); + while (pt != points.end()) { + if ((pt->y <= prev_pt->y) && + (pt->y < next_pt->y)) { break; } - prev_edge = edge; - ++edge; + prev_pt = pt; + pt++; + next_pt++; + if (next_pt == points.end()) { next_pt = std::next(points.begin()); } } - std::rotate(edges.begin(), edge, edges.end()); + //Re-close the linear ring + points.erase(std::prev(points.end())); + std::rotate(points.begin(), pt, points.end()); + points.push_back(*points.begin()); } -bound create_bound_towards_maximum(edge_list& edges) { - if (edges.size() == 0) return {}; - if (edges.size() == 1) { +bound create_bound_towards_maximum(point_list& points, point_list::iterator& pt) { + if (std::distance(pt, points.end()) < 2) { return {}; }; + if (std::distance(pt, points.end()) == 2) { bound bnd; - std::swap(bnd.edges, edges); + if (points[0].y < points[1].y) std::copy(pt, points.end(), std::back_inserter(bnd.points)); + else std::reverse_copy(pt, points.end(), std::back_inserter(bnd.points)); + pt = points.end(); return bnd; } - auto next_edge = edges.begin(); - auto edge = next_edge; - ++next_edge; - - while (next_edge != edges.end()) { - if (edge->p1 == next_edge->p1) { + const auto begin = pt; + auto prev_pt = pt == points.begin() ? std::prev(points.end(), 2) : std::prev(pt); + auto next_pt = std::next(pt) == points.end() ? std::next(points.begin()) : std::next(pt); + while (pt != points.end()) { + if ((pt->y >= prev_pt->y) && + (pt->y > next_pt->y )) { break; } - edge = next_edge; - ++next_edge; + prev_pt = pt; + pt++; + next_pt++; + if (next_pt == points.end()) { next_pt = std::next(points.begin()); } } bound bnd; - bnd.edges.reserve(static_cast(std::distance(edges.begin(), next_edge))); - std::move(edges.begin(), next_edge, std::back_inserter(bnd.edges)); - edges.erase(edges.begin(), next_edge); + if (std::next(pt) == points.end()) { next_pt = points.end(); pt++; }; + bnd.points.reserve(static_cast(std::distance(begin, next_pt))); + std::copy(begin, next_pt, std::back_inserter(bnd.points)); return bnd; } -bound create_bound_towards_minimum(edge_list& edges) { - if (edges.size() == 0) return {}; - if (edges.size() == 1) { +bound create_bound_towards_minimum(point_list& points, point_list::iterator& pt) { + if (std::distance(pt, points.end()) < 2) { return {}; }; + if (std::distance(pt, points.end()) == 2) { bound bnd; - std::swap(bnd.edges, edges); + if (pt->y < std::next(pt)->y) std::copy(pt, points.end(), std::back_inserter(bnd.points)); + else std::reverse_copy(pt, points.end(), std::back_inserter(bnd.points)); + pt = points.end(); return bnd; } - auto next_edge = edges.begin(); - auto edge = next_edge; - ++next_edge; - - while (next_edge != edges.end()) { - if (edge->p0 == next_edge->p0) { + auto begin = pt; + auto prev_pt = pt == points.begin() ? std::prev(points.end(), 2) : std::prev(pt); + auto next_pt = std::next(pt) == points.end() ? std::next(points.begin()) : std::next(pt); + while (pt != points.end()) { + if ((pt->y <= prev_pt->y) && + (pt->y < next_pt->y)) { break; } - edge = next_edge; - ++next_edge; + prev_pt = pt; + pt++; + next_pt++; + if (next_pt == points.end()) { next_pt = std::next(points.begin()); } } bound bnd; - - bnd.edges.reserve(static_cast(std::distance(edges.begin(), next_edge))); - std::move(edges.begin(), next_edge, std::back_inserter(bnd.edges)); - edges.erase(edges.begin(), next_edge); - std::reverse(bnd.edges.begin(), bnd.edges.end()); - + if (std::next(pt) == points.end()) { next_pt = points.end(); pt++; }; + bnd.points.reserve(static_cast(std::distance(begin, next_pt))); + std::reverse_copy(begin, next_pt, std::back_inserter(bnd.points)); return bnd; } -void build_edge_table(edge_list& edges, uint32_t maxTile, edge_table& et, bool closed = false) { +void build_edge_table(point_list& points, uint32_t maxTile, edge_table& et, bool closed = false) { if (closed) { - start_list_on_local_minimum(edges); + start_list_on_local_minimum(points); } - bound to_max, to_min; - while (!edges.empty()) { - to_max = create_bound_towards_maximum(edges); - to_min = create_bound_towards_minimum(edges); + auto pointsIter = points.begin(); + while (pointsIter != points.end()) { + bound to_max = create_bound_towards_maximum(points, pointsIter); + bound to_min = create_bound_towards_minimum(points, pointsIter); - if (to_max.edges.size() > 0) { + if (to_max.points.size() > 0) { // Projections may result in values beyond the bounds due to double precision - const auto y = static_cast(std::floor(util::clamp(to_max.edges.front().p0.y, 0.0, (double)maxTile))); + const auto y = static_cast(std::floor(clamp(to_max.points.front().y, 0.0, (double)maxTile))); + to_max.interpolate(y); et[y].push_back(std::move(to_max)); } - if (to_min.edges.size() > 0) { - const auto y = static_cast(std::floor(util::clamp(to_min.edges.front().p0.y, 0.0, (double)maxTile))); + if (to_min.points.size() > 0) { + const auto y = static_cast(std::floor(clamp(to_min.points.front().y, 0.0, (double)maxTile))); + to_min.interpolate(y); et[y].push_back(std::move(to_min)); } } - assert(edges.size() == 0); + assert(pointsIter == points.end()); } std::vector scan_row(uint32_t y, bound_list& aet) { @@ -115,38 +123,38 @@ std::vector scan_row(uint32_t y, bound_list& aet) { for(bound& b: aet) { x_range xp = { INT_MAX, 0 }; double x; - const auto numEdges = b.edges.size(); - while (b.currentEdgeIndex < numEdges) { - auto& e = b.currentEdge(); - x = e.x; + const auto numEdges = b.points.size() - 1; + while (b.currentPointIndex < numEdges) { + x = b.currentX; xp.first = std::min(xp.first, static_cast(std::floor(x))); xp.second = std::max(xp.second, static_cast(std::ceil(x))); // If this edge ends beyond the current row, find the x-value at the exit, // and be done with this bound - if (e.p1.y > y+1) { - x = e.interpolate(y+1); + auto& p1 = b.points[b.currentPointIndex + 1]; + if (p1.y > y+1) { + x = b.interpolate(y+1); xp.first = std::min(xp.first, static_cast(std::floor(x))); xp.second = std::max(xp.second, static_cast(std::ceil(x))); break; - } else if(b.currentEdgeIndex == numEdges - 1) { + } else if(b.currentPointIndex == numEdges - 1) { // For last edge, consider x at edge end; - x = e.p1.x; + x = p1.x; xp.first = std::min(xp.first, static_cast(std::floor(x))); xp.second = std::max(xp.second, static_cast(std::ceil(x))); } - b.currentEdgeIndex++; + b.currentPointIndex++; } tile_range.push_back(xp); } // Erase bounds in the aet whose current edge ends inside this row, or there are no more edges - auto b = aet.begin(); - while (b != aet.end()) { - if ( b->currentEdgeIndex >= b->edges.size() || - b->currentEdge().p1.y <= y+1) { - b = aet.erase(b); + auto bound = aet.begin(); + while (bound != aet.end()) { + if ( bound->currentPointIndex == bound->points.size() - 1 && + bound->points[bound->currentPointIndex].y <= y+1) { + bound = aet.erase(bound); } else { - b++; + bound++; } } // Sort the X-extents of each lml by x_min, x_max @@ -157,137 +165,118 @@ std::vector scan_row(uint32_t y, bound_list& aet) { return tile_range; } -struct ToEdges { +struct ToEdgeTables { int32_t zoom; - bool project; - ToEdges(int32_t z, bool p): zoom(z), project(p) {} - - void makeEdges(const std::vector>& points, edge_list& edges) const { - edges.reserve(edges.size() + points.size()-1); - auto p1 = !project ? points[0] : Projection::project({points[0].y, points[0].x}, zoom); - for (uint32_t j=1; j < points.size(); j++) { - auto p2 = !project ? points[j] : Projection::project({points[j].y, points[j].x}, zoom); - edges.emplace_back(p1, p2); - p1 = p2; + bool project = false; + ToEdgeTables(int32_t z, bool p): zoom(z), project(p) {} + + void buildTable(const std::vector>& points, edge_table& et, bool closed = false) const { + point_list projectedPoints; + if (project) { + projectedPoints.reserve(points.size()); + for(const auto&p : points) { + projectedPoints.push_back( + Projection::project(LatLng{ p.y, p.x }, zoom)); + } + } else { + projectedPoints.insert(projectedPoints.end(), points.begin(), points.end()); } + build_edge_table(projectedPoints, 1 << zoom, et, closed); } - - edge_list polyEdges(const Polygon& g) const { - edge_list edges; - - for (uint32_t i = 0; i < g.size(); i++) { - const auto ring = g[i]; - makeEdges(ring, edges); + + edge_table buildPolygonTable(const Polygon& polygon) const { + edge_table et; + for(const auto&ring : polygon) { + buildTable(ring, et, true); } - return edges; + return et; } - - std::vector operator()(const Point& p) const { - const auto pt = Projection::project({p.y, p.x}, zoom); - return { { {pt, pt} } }; + std::vector operator()(const Point&) const { + return { }; } - std::vector operator()(const MultiPoint&points) const { - std::vector edgeLists; - edgeLists.reserve(points.size()); - for (const auto& p: points) { - const auto pt = Projection::project({p.y, p.x}, zoom); - edgeLists.push_back({ edge{pt, pt} }); - } - return edgeLists; + std::vector operator()(const MultiPoint&) const { + return { }; } - std::vector operator()(const LineString& lines) const { - edge_list edges; - makeEdges(lines, edges); - return { edges }; + std::vector operator()(const LineString& lines) const { + edge_table et; + buildTable(lines, et); + return { et }; } - std::vector operator()(const MultiLineString& g) const { - std::vector edgeLists; - for(const auto& lines: g) { - edge_list edges; - makeEdges(lines, edges); - edgeLists.push_back(edges); - edges.clear(); + std::vector operator()(const MultiLineString& lines) const { + edge_table et; + for(const auto&line : lines) { + buildTable(line, et); } - return edgeLists; + return { et }; } - std::vector operator()(const Polygon& g) const { - return { polyEdges(g) }; + std::vector operator()(const Polygon& polygon) const { + return { buildPolygonTable(polygon) }; } - std::vector operator()(const MultiPolygon& g) const { - std::vector edgeLists; - for(const auto& polygon: g) { - edgeLists.push_back(polyEdges(polygon)); + std::vector operator()(const MultiPolygon& polygons) const { + std::vector tables; + for(const auto& polygon: polygons) { + tables.push_back(buildPolygonTable(polygon)); } - return edgeLists; + return tables; } - std::vector operator()(const mapbox::geometry::geometry_collection&) const { + std::vector operator()(const mapbox::geometry::geometry_collection&) const { return {}; } }; -//} //end namespace - -TileCoverImpl::TileCoverImpl(int32_t z, const Geometry& geom, bool project): max_y(1 << z) { +TileCoverImpl::TileCoverImpl(int32_t z, const Geometry& geom, bool project): maxY(1 << z) { ToFeatureType toFeatureType; - closed_geom = apply_visitor(toFeatureType, geom) == FeatureType::Polygon; + isClosed = apply_visitor(toFeatureType, geom) == FeatureType::Polygon; - // Build edge table - ToEdges te(z, project); - std::vector edgeLists = apply_visitor(te, geom); - for(auto& edges: edgeLists) { - edge_table table; - build_edge_table(edges, max_y, table, closed_geom); - et.push_back(table); - } + ToEdgeTables toEdgeTables(z, project); + edgeTables = apply_visitor(toEdgeTables, geom); reset(); } void TileCoverImpl::reset() { - current_edge_table = et.begin(); - if (current_edge_table != et.end()) { - aet = current_edge_table->begin()->second; - current_y = current_edge_table->begin()->first; + currentEdgeTable = edgeTables.begin(); + if (currentEdgeTable != edgeTables.end()) { + activeEdgeTable = currentEdgeTable->begin()->second; + currentY = currentEdgeTable->begin()->first; } } bool TileCoverImpl::next() { - return current_edge_table != et.end() && aet.size() != 0 && current_y < max_y; + return currentEdgeTable != edgeTables.end() && activeEdgeTable.size() != 0 && currentY < maxY; } bool TileCoverImpl::scanRow(ScanLine& scanCover) { if (!next()) { return false; } - auto xps = util::scan_row(current_y, aet); + auto xps = util::scan_row(currentY, activeEdgeTable); auto p = xps.begin(); - + assert(isClosed ? xps.size() % 2 == 0 : true); while (p != xps.end()) { auto x_min = p->first; // For closed geometries, consider odd even pairs of tile ranges - if (closed_geom) { p++; } + if (isClosed) { p++; } auto x_max = p++->second; - scanCover(x_min, x_max, current_y); + scanCover(x_min, x_max, currentY); } - // Move to the next row - current_y++; + currentY++; // Update AET for next row - auto nextRow = current_edge_table->find(current_y); - if (nextRow != current_edge_table->end()) { - aet.insert(aet.end(), nextRow->second.begin(), nextRow->second.end()); - } else if (aet.size() == 0){ - current_edge_table++; - if (current_edge_table == et.end()) return false; - aet = current_edge_table->begin()->second; - current_y = current_edge_table->begin()->first; + auto nextRow = currentEdgeTable->find(currentY); + if (nextRow != currentEdgeTable->end()) { + activeEdgeTable.insert(activeEdgeTable.end(), nextRow->second.begin(), nextRow->second.end()); + } else if (activeEdgeTable.size() == 0){ + currentEdgeTable++; + if (currentEdgeTable == edgeTables.end()) return false; + activeEdgeTable = currentEdgeTable->begin()->second; + currentY = currentEdgeTable->begin()->first; } - return next(); } diff --git a/src/mbgl/util/tile_cover_impl.hpp b/src/mbgl/util/tile_cover_impl.hpp index 6593babba17..24b60936671 100644 --- a/src/mbgl/util/tile_cover_impl.hpp +++ b/src/mbgl/util/tile_cover_impl.hpp @@ -1,10 +1,9 @@ #pragma once -#include #include #include #include -#include + #include #include @@ -15,60 +14,34 @@ class LatLngBounds; namespace util { -struct edge; struct bound; -using edge_list = std::vector; +using point_list = std::vector>; using bound_list = std::vector; using edge_table = std::map; using x_range = std::pair; -static const double INF = 1e20; - -struct edge { - Point p0, p1; - double x, m; - edge(Point _a, Point _b) { - if (_a.y > _b.y) { - std::swap(_a, _b); - } - p0 = _a; - p1 = _b; - const auto dx = p1.x - p0.x; - const auto dy = p1.y - p0.y; - x = p0.x; - if (dy == 0) { - m = INF; - } - else { - m = dx / dy; - } - } +struct bound { + point_list points; + size_t currentPointIndex = 0; + double currentX; double interpolate(uint32_t y) { - if (m == 0) { return x; } + const auto& p0 = points[currentPointIndex]; + const auto& p1 = points[currentPointIndex + 1]; - if ( y < p0.y || y > p1.y) { - util::clamp(y, static_cast(p0.y), static_cast(p1.y)); + const auto dx = p1.x - p0.x; + const auto dy = p1.y - p0.y; + currentX = p0.x; + if (dx == 0) { + return currentX; + } else if (dy == 0){ + return y <= p0.y ? p0.x : p1.x; } - - x = m * (y - p0.y) + p0.x; - return x; - } -}; - -struct bound { - edge_list edges; - size_t currentEdgeIndex = 0; - - edge& currentEdge() { - if(currentEdgeIndex >= edges.size()) { return edges.back(); }; - return edges[currentEdgeIndex]; - } - - const edge& current() const { - if(currentEdgeIndex >= edges.size()) { return edges.back(); }; - return edges[currentEdgeIndex]; + if (y < p0.y) return currentX; + if (y > p1.y) return p1.x; + currentX = (dx / dy) * (y - p0.y) + p0.x; + return currentX; } }; @@ -82,12 +55,12 @@ class TileCoverImpl { bool next(); void reset(); private: - bool closed_geom; - uint32_t current_y; // current scanLine - bound_list aet; - std::vector et; - std::vector::iterator current_edge_table; - uint32_t max_y; + bool isClosed; + uint32_t currentY; + bound_list activeEdgeTable; + std::vector edgeTables; + std::vector::iterator currentEdgeTable; + uint32_t maxY; }; diff --git a/test/util/tile_cover.test.cpp b/test/util/tile_cover.test.cpp index 82d539930b0..b707b8be6f4 100644 --- a/test/util/tile_cover.test.cpp +++ b/test/util/tile_cover.test.cpp @@ -2,6 +2,8 @@ #include #include +#include + #include using namespace mbgl; @@ -22,8 +24,8 @@ TEST(TileCover, Antarctic) { TEST(TileCover, WorldZ0) { EXPECT_EQ((std::vector{ - { 0, 0, 0 }, - }), + { 0, 0, 0 }, + }), util::tileCover(LatLngBounds::world(), 0)); } @@ -37,15 +39,15 @@ TEST(TileCover, Pitch) { transform.setPitch(40.0 * M_PI / 180.0); EXPECT_EQ((std::vector{ - { 2, 1, 2 }, { 2, 1, 1 }, { 2, 2, 2 }, { 2, 2, 1 }, { 2, 3, 2 } - }), + { 2, 1, 2 }, { 2, 1, 1 }, { 2, 2, 2 }, { 2, 2, 1 }, { 2, 3, 2 } + }), util::tileCover(transform.getState(), 2)); } TEST(TileCover, WorldZ1) { EXPECT_EQ((std::vector{ - { 1, 0, 0 }, { 1, 0, 1 }, { 1, 1, 0 }, { 1, 1, 1 }, - }), + { 1, 0, 0 }, { 1, 0, 1 }, { 1, 1, 0 }, { 1, 1, 1 }, + }), util::tileCover(LatLngBounds::world(), 1)); } @@ -83,24 +85,24 @@ TEST(TileCoverStream, WorldZ1) { } })){}; EXPECT_EQ((std::vector{ - { 1, 0, 0 }, { 1, 1, 0 }, { 1, 0, 1 }, { 1, 1, 1 }, - }), t); + { 1, 0, 0 }, { 1, 1, 0 }, { 1, 0, 1 }, { 1, 1, 1 }, + }), t); } static const LatLngBounds sanFrancisco = - LatLngBounds::hull({ 37.6609, -122.5744 }, { 37.8271, -122.3204 }); +LatLngBounds::hull({ 37.6609, -122.5744 }, { 37.8271, -122.3204 }); TEST(TileCover, SanFranciscoZ0) { EXPECT_EQ((std::vector{ - { 0, 0, 0 }, - }), + { 0, 0, 0 }, + }), util::tileCover(sanFrancisco, 0)); } TEST(TileCover, SanFranciscoZ10) { EXPECT_EQ((std::vector{ - { 10, 163, 395 }, { 10, 163, 396 }, { 10, 164, 395 }, { 10, 164, 396 }, - }), + { 10, 163, 395 }, { 10, 163, 396 }, { 10, 164, 395 }, { 10, 164, 396 }, + }), util::tileCover(sanFrancisco, 10)); } @@ -112,81 +114,100 @@ TEST(TileCover, SanFranciscoZ0Wrapped) { util::tileCover(sanFranciscoWrapped, 0)); } -TEST(TileCover, GeomPointZ13) { - EXPECT_EQ((std::vector{ { 13, 2343, 3133 } }), - util::tileCover(Point {-77.03355114851098,38.89224995264726 }, 13)); -} - -TEST(TileCover, GeomPointZ10) { - EXPECT_EQ((std::vector{ { 10, 292, 391 } }), - util::tileCover(Point {-77.03355114851098,38.89224995264726 }, 10)); -} - TEST(TileCover, GeomLineZ10) { - auto lineCover = util::tileCover(LineString{ - {-121.49368286132812,38.57903714667459}, - {-122.4422836303711,37.773157169570695} - }, 10); - EXPECT_EQ((std::vector{ { 10, 166, 392}, {10, 165, 393}, {10, 166, 393}, {10, 164, 394}, {10, 165, 394}, {10,163,395}, {10, 164, 395} }), - lineCover); - -} - -TEST(TileCover, GeomLineZ13) { - auto lineCover = util::tileCover(LineString{ - {-77.03342914581299,38.892101707724315}, - {-77.02394485473633,38.89203490311832}, - {-77.02390193939209,38.8824811975508}, - {-77.0119285583496,38.8824811975508}, - {-77.01218605041504,38.887391829071106}, - {-77.01390266418456,38.88735842456116}, - {-77.01622009277342,38.896510672795266}, - {-77.01725006103516,38.914143795902376}, - {-77.01879501342773,38.914143795902376}, - {-77.0196533203125,38.91307524644972} - }, 13); - EXPECT_EQ((std::vector{ { 13, 2343, 3133 }, { 13, 2343, 3134 } }), - lineCover); + {-121.49368286132812,38.57903714667459}, + {-122.4422836303711,37.773157169570695} + }, 10); + EXPECT_EQ((std::vector{ + { 10, 166, 392}, {10, 165, 393}, {10, 166, 393}, + {10, 164, 394}, {10, 165, 394}, {10,163,395}, {10, 164, 395} + }),lineCover); + } TEST(TileCover, WrappedGeomLineZ10) { auto lineCover = util::tileCover(LineString{ - {-179.93342914581299,38.892101707724315}, - {-180.02394485473633,38.89203490311832} - }, 10); + {-179.93342914581299,38.892101707724315}, + {-180.02394485473633,38.89203490311832} + }, 10); EXPECT_EQ((std::vector{ { 10, -1, 391 }, { 10, 0, 391 } }), - lineCover); - + lineCover); + lineCover = util::tileCover(LineString{ - {179.93342914581299,38.892101707724315}, - {180.02394485473633,38.89203490311832} - }, 10); + {179.93342914581299,38.892101707724315}, + {180.02394485473633,38.89203490311832} + }, 10); EXPECT_EQ((std::vector{ { 10, 1023, 391 }, { 10, 1024, 391 } }), - lineCover); + lineCover); } -TEST(TileCover, GeomLineZ15) { - auto lineCover = util::tileCover(LineString{ - {-77.03342914581299,38.892101707724315}, - {-77.02394485473633,38.89203490311832}, - {-77.02390193939209,38.8824811975508}, - {-77.0119285583496,38.8824811975508}, - {-77.01218605041504,38.887391829071106}, - {-77.01390266418456,38.88735842456116}, - {-77.01622009277342,38.896510672795266}, - {-77.01725006103516,38.914143795902376}, - {-77.01879501342773,38.914143795902376}, - {-77.0196533203125,38.91307524644972} - }, 15); - EXPECT_EQ(lineCover, (std::vector{ - { 15,9373,12533 }, - { 15,9373,12534 }, - { 15,9372,12535 }, - { 15,9373,12535 }, - { 15,9373,12536 }, - { 15,9374,12536 } - })); +TEST(TileCover, GeomMultiLineString) { + auto geom = MultiLineString{ + { { -122.5, 37.76 }, { -122.4, 37.76} }, + { { -122.5, 37.72 }, { -122.4, 37.72} } }; + + EXPECT_EQ((std::vector{ + {14, 2616, 6333}, {14, 2617, 6333}, {14, 2618, 6333}, + {14, 2619, 6333}, {14, 2620, 6333}, {14, 2621, 6333} }), + util::tileCover(geom, 14)); +} + +TEST(TileCover, GeomPolygon) { + auto polygon = Polygon{ + { + {5.09765625,53.067626642387374}, + {2.373046875,43.389081939117496}, + {-4.74609375,48.45835188280866}, + {-1.494140625,37.09023980307208}, + {22.587890625,36.24427318493909}, + {31.640625,46.13417004624326}, + {17.841796875,54.7246201949245}, + {5.09765625,53.067626642387374}, + },{ + {19.6875,49.66762782262194}, + {8.701171874999998,50.233151832472245}, + {5.185546875,41.244772343082076}, + {16.34765625,39.095962936305476}, + {13.623046875,45.089035564831036}, + {22.8515625,43.51668853502906}, + {19.6875,49.66762782262194} + } + }; + + auto results = util::tileCover(polygon, 8); + + EXPECT_NE(std::find(results.begin(), results.end(), UnwrappedTileID{8, 134, 87}), results.end()); + EXPECT_NE(std::find(results.begin(), results.end(), UnwrappedTileID{8, 139, 87}), results.end()); + // Should have a hole + EXPECT_EQ(std::find(results.begin(), results.end(), UnwrappedTileID{8, 136, 87}), results.end()); +} + +TEST(TileCover, GeomMultiPolygon) { + auto multiPolygon = MultiPolygon{ + {{ + {5.09765625,53.067626642387374}, + {2.373046875,43.389081939117496}, + {-4.74609375,48.45835188280866}, + {-1.494140625,37.09023980307208}, + {22.587890625,36.24427318493909}, + {31.640625,46.13417004624326}, + {17.841796875,54.7246201949245}, + {5.09765625,53.067626642387374}, + }},{{ + {59.150390625,45.460130637921004}, + {65.126953125,41.11246878918088}, + {69.169921875,47.45780853075031}, + {63.896484375,50.064191736659104}, + {59.150390625,45.460130637921004} + }} + }; + auto results = util::tileCover(multiPolygon, 8); + + EXPECT_EQ(423u, results.size()); + EXPECT_NE(std::find(results.begin(), results.end(), UnwrappedTileID{8, 139, 87}), results.end()); + EXPECT_NE(std::find(results.begin(), results.end(), UnwrappedTileID{8, 136, 87}), results.end()); + EXPECT_NE(std::find(results.begin(), results.end(), UnwrappedTileID{8, 174, 94}), results.end()); } TEST(TileCover, GeomSanFranciscoPoly) { @@ -218,55 +239,14 @@ TEST(TileCover, GeomSanFranciscoPoly) { } }; - auto results = util::tileCover(sanFranciscoGeom, 10); - EXPECT_EQ((std::vector{ { 10, 163, 395 }, { 10, 163, 396 } }), - results); - - results = util::tileCover(sanFranciscoGeom, 12); EXPECT_EQ((std::vector{ - { 12, 654, 1582 }, { 12, 655, 1582 }, - { 12, 654, 1583 }, { 12, 655, 1583 }, - { 12, 654, 1584 }, { 12, 655, 1584 } - }), results); - -} - -TEST(TileCover, GeomPoint) { - auto geom = Point(-122.5744, 37.6609); - - EXPECT_EQ((std::vector{ {2 ,0 ,1 } }), - util::tileCover(geom, 2)); + { 12, 654, 1582 }, { 12, 655, 1582 }, + { 12, 654, 1583 }, { 12, 655, 1583 }, + { 12, 654, 1584 }, { 12, 655, 1584 } + }), util::tileCover(sanFranciscoGeom, 12)); } -TEST(TileCover, GeomMultiPoint) { - auto geom = MultiPoint{ { -122.5, 37.76 }, { -122.4, 37.76} }; - EXPECT_EQ((std::vector{ - {19, 83740, 202675}, {19, 83886, 202675} }), - util::tileCover(geom, 19)); -} - -TEST(TileCover, GeomLineString) { - auto geom = LineString{{ -122.5, 37.76 }, { -122.4, 37.76} }; - - EXPECT_EQ((std::vector{ - {14, 2616, 6333}, {14, 2617, 6333}, {14, 2618, 6333}, - {14, 2619, 6333}, {14, 2620, 6333}, {14, 2621, 6333} }), - util::tileCover(geom, 14)); -} - -TEST(TileCover, GeomMultiLineString) { - auto geom = MultiLineString{ - { { -122.5, 37.76 }, { -122.4, 37.76} }, - { { -122.5, 37.72 }, { -122.4, 37.72} } }; - - EXPECT_EQ((std::vector{ - {14, 2616, 6333}, {14, 2617, 6333}, {14, 2618, 6333}, - {14, 2619, 6333}, {14, 2620, 6333}, {14, 2621, 6333}, - {14, 2616, 6335}, {14, 2617, 6335}, {14, 2618, 6335}, - {14, 2619, 6335}, {14, 2620, 6335}, {14, 2621, 6335} }), - util::tileCover(geom, 14)); -} TEST(TileCount, World) { EXPECT_EQ(1u, util::tileCount(LatLngBounds::world(), 0));