From 10a44050f485a18f8dd6523aca6a7a9f82f7afc7 Mon Sep 17 00:00:00 2001 From: Asheem Mamoowala Date: Fri, 5 Jan 2018 06:35:31 -0800 Subject: Support TileJSON bounds property (#10701) * [core] Parse TileJSON bounds property * [core] Add TileRange and LatLngBounds::contains(CanonicalTileID) Move LatLngBounds::contains impl to cpp file * [core] Skip tile creation outside of tileset bounds * [core] Fix TileRange for wrapped bounds and use for CustomTileLoader instead of LatLngBounds comparisons for tiles. --- cmake/core-files.cmake | 1 + cmake/test-files.cmake | 4 +- include/mbgl/util/geo.hpp | 78 ++------------------ include/mbgl/util/projection.hpp | 4 ++ include/mbgl/util/tileset.hpp | 13 ++-- platform/node/test/ignores.json | 1 - src/mbgl/algorithm/update_renderables.hpp | 6 +- src/mbgl/annotation/render_annotation_source.cpp | 1 + .../sources/render_custom_geometry_source.cpp | 1 + .../renderer/sources/render_geojson_source.cpp | 1 + src/mbgl/renderer/sources/render_raster_source.cpp | 1 + src/mbgl/renderer/sources/render_vector_source.cpp | 1 + src/mbgl/renderer/tile_pyramid.cpp | 11 +++ src/mbgl/renderer/tile_pyramid.hpp | 1 + src/mbgl/style/conversion/tileset.cpp | 31 ++++++++ src/mbgl/style/custom_tile_loader.cpp | 11 ++- src/mbgl/util/geo.cpp | 82 ++++++++++++++++++++++ src/mbgl/util/tile_range.hpp | 47 +++++++++++++ test/style/conversion/tileset.test.cpp | 72 +++++++++++++++++++ test/util/tile_range.test.cpp | 56 +++++++++++++++ 20 files changed, 339 insertions(+), 84 deletions(-) create mode 100644 src/mbgl/util/tile_range.hpp create mode 100644 test/style/conversion/tileset.test.cpp create mode 100644 test/util/tile_range.test.cpp diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake index 15812da42d..5c060f4698 100644 --- a/cmake/core-files.cmake +++ b/cmake/core-files.cmake @@ -690,6 +690,7 @@ set(MBGL_CORE_FILES src/mbgl/util/tile_coordinate.hpp src/mbgl/util/tile_cover.cpp src/mbgl/util/tile_cover.hpp + src/mbgl/util/tile_range.hpp src/mbgl/util/tiny_sdf.cpp src/mbgl/util/tiny_sdf.hpp src/mbgl/util/token.hpp diff --git a/cmake/test-files.cmake b/cmake/test-files.cmake index 55420ff2a8..381c609719 100644 --- a/cmake/test-files.cmake +++ b/cmake/test-files.cmake @@ -88,6 +88,7 @@ set(MBGL_TEST_FILES test/style/conversion/layer.test.cpp test/style/conversion/light.test.cpp test/style/conversion/stringify.test.cpp + test/style/conversion/tileset.test.cpp # style/expression test/style/expression/expression.test.cpp @@ -113,9 +114,9 @@ set(MBGL_TEST_FILES # text test/text/cross_tile_symbol_index.test.cpp - test/text/local_glyph_rasterizer.test.cpp test/text/glyph_manager.test.cpp test/text/glyph_pbf.test.cpp + test/text/local_glyph_rasterizer.test.cpp test/text/quads.test.cpp # tile @@ -146,6 +147,7 @@ set(MBGL_TEST_FILES test/util/thread.test.cpp test/util/thread_local.test.cpp test/util/tile_cover.test.cpp + test/util/tile_range.test.cpp test/util/timer.test.cpp test/util/token.test.cpp test/util/unique_any.test.cpp diff --git a/include/mbgl/util/geo.hpp b/include/mbgl/util/geo.hpp index 60043ee156..dacdb968f3 100644 --- a/include/mbgl/util/geo.hpp +++ b/include/mbgl/util/geo.hpp @@ -158,81 +158,11 @@ public: return (sw.wrapped().longitude() > ne.wrapped().longitude()); } - bool contains(const LatLng& point, LatLng::WrapMode wrap = LatLng::Unwrapped) const { - bool containsLatitude = point.latitude() >= sw.latitude() && - point.latitude() <= ne.latitude(); - if (!containsLatitude) { - return false; - } - - bool containsUnwrappedLongitude = point.longitude() >= sw.longitude() && - point.longitude() <= ne.longitude(); - if (containsUnwrappedLongitude) { - return true; - } else if (wrap == LatLng::Wrapped) { - LatLngBounds wrapped(sw.wrapped(), ne.wrapped()); - auto ptLon = point.wrapped().longitude(); - if (crossesAntimeridian()) { - return (ptLon >= wrapped.sw.longitude() && - ptLon <= util::LONGITUDE_MAX) || - (ptLon <= wrapped.ne.longitude() && - ptLon >= -util::LONGITUDE_MAX); - } else { - return (ptLon >= wrapped.sw.longitude() && - ptLon <= wrapped.ne.longitude()); - } - } - return false; - } + bool contains(const CanonicalTileID& tileID) const; + bool contains(const LatLng& point, LatLng::WrapMode wrap = LatLng::Unwrapped) const; + bool contains(const LatLngBounds& area, LatLng::WrapMode wrap = LatLng::Unwrapped) const; - bool contains(const LatLngBounds& area, LatLng::WrapMode wrap = LatLng::Unwrapped) const { - bool containsLatitude = area.north() <= north() && area.south() >= south(); - if (!containsLatitude) { - return false; - } - - bool containsUnwrapped = area.east() <= east() && area.west() >= west(); - if(containsUnwrapped) { - return true; - } else if (wrap == LatLng::Wrapped) { - LatLngBounds wrapped(sw.wrapped(), ne.wrapped()); - LatLngBounds other(area.sw.wrapped(), area.ne.wrapped()); - if (crossesAntimeridian() & !area.crossesAntimeridian()) { - return (other.east() <= util::LONGITUDE_MAX && other.west() >= wrapped.west()) || - (other.east() <= wrapped.east() && other.west() >= -util::LONGITUDE_MAX); - } else { - return other.east() <= wrapped.east() && other.west() >= wrapped.west(); - } - } - return false; - } - - bool intersects(const LatLngBounds area, LatLng::WrapMode wrap = LatLng::Unwrapped) const { - bool latitudeIntersects = area.north() > south() && area.south() < north(); - if (!latitudeIntersects) { - return false; - } - - bool longitudeIntersects = area.east() > west() && area.west() < east(); - if (longitudeIntersects) { - return true; - } else if (wrap == LatLng::Wrapped) { - LatLngBounds wrapped(sw.wrapped(), ne.wrapped()); - LatLngBounds other(area.sw.wrapped(), area.ne.wrapped()); - if (crossesAntimeridian()) { - return area.crossesAntimeridian() || - other.east() > wrapped.west() || - other.west() < wrapped.east(); - } else if (other.crossesAntimeridian()){ - return other.east() > wrapped.west() || - other.west() < wrapped.east(); - } else { - return other.east() > wrapped.west() && - other.west() < wrapped.east(); - } - } - return false; - } + bool intersects(const LatLngBounds area, LatLng::WrapMode wrap = LatLng::Unwrapped) const; private: LatLng sw; diff --git a/include/mbgl/util/projection.hpp b/include/mbgl/util/projection.hpp index 1613af3b36..b4a34521a4 100644 --- a/include/mbgl/util/projection.hpp +++ b/include/mbgl/util/projection.hpp @@ -78,6 +78,10 @@ public: return project_(latLng, worldSize(scale)); } + static Point project(const LatLng& latLng, uint8_t zoom) { + return project_(latLng, std::pow(2.0, zoom)); + } + static LatLng unproject(const Point& p, double scale, LatLng::WrapMode wrapMode = LatLng::Unwrapped) { auto p2 = p * util::DEGREES_MAX / worldSize(scale); return LatLng { diff --git a/include/mbgl/util/tileset.hpp b/include/mbgl/util/tileset.hpp index 5a03e1a9da..7bef0e89ed 100644 --- a/include/mbgl/util/tileset.hpp +++ b/include/mbgl/util/tileset.hpp @@ -2,7 +2,8 @@ #include #include - +#include +#include #include #include #include @@ -18,6 +19,7 @@ public: Range zoomRange; std::string attribution; Scheme scheme; + optional bounds; Tileset(std::vector tiles_ = std::vector(), Range zoomRange_ = { 0, util::DEFAULT_MAX_ZOOM }, @@ -26,13 +28,14 @@ public: : tiles(std::move(tiles_)), zoomRange(std::move(zoomRange_)), attribution(std::move(attribution_)), - scheme(scheme_) {} + scheme(scheme_), + bounds() {} - // TileJSON also includes center, zoom, and bounds, but they are not used by mbgl. + // TileJSON also includes center and zoom but they are not used by mbgl. friend bool operator==(const Tileset& lhs, const Tileset& rhs) { - return std::tie(lhs.tiles, lhs.zoomRange, lhs.attribution, lhs.scheme) - == std::tie(rhs.tiles, rhs.zoomRange, rhs.attribution, rhs.scheme); + return std::tie(lhs.tiles, lhs.zoomRange, lhs.attribution, lhs.scheme, lhs.bounds) + == std::tie(rhs.tiles, rhs.zoomRange, rhs.attribution, rhs.scheme, rhs.bounds); } }; diff --git a/platform/node/test/ignores.json b/platform/node/test/ignores.json index 10e5e88e79..62e042cd4d 100644 --- a/platform/node/test/ignores.json +++ b/platform/node/test/ignores.json @@ -85,7 +85,6 @@ "render-tests/text-pitch-alignment/map-text-rotation-alignment-map": "https://github.com/mapbox/mapbox-gl-native/issues/9732", "render-tests/text-pitch-alignment/viewport-text-rotation-alignment-map": "https://github.com/mapbox/mapbox-gl-native/issues/9732", "render-tests/text-pitch-scaling/line-half": "https://github.com/mapbox/mapbox-gl-native/issues/9732", - "render-tests/tilejson-bounds/default": "https://github.com/mapbox/mapbox-gl-native/pull/10701", "render-tests/video/default": "skip - https://github.com/mapbox/mapbox-gl-native/issues/601", "render-tests/background-color/colorSpace-hcl": "needs issue", "render-tests/hillshade-accent-color/default": "skip - https://github.com/mapbox/mapbox-gl-native/pull/10642", diff --git a/src/mbgl/algorithm/update_renderables.hpp b/src/mbgl/algorithm/update_renderables.hpp index c583b6b2b6..5fbe0d943f 100644 --- a/src/mbgl/algorithm/update_renderables.hpp +++ b/src/mbgl/algorithm/update_renderables.hpp @@ -35,7 +35,11 @@ void updateRenderables(GetTileFn getTile, auto tile = getTile(idealDataTileID); if (!tile) { tile = createTile(idealDataTileID); - assert(tile); + // For source types where TileJSON.bounds is set, tiles outside the + // bounds are not created + if(tile == nullptr) { + continue; + } } // if (source has the tile and bucket is loaded) { diff --git a/src/mbgl/annotation/render_annotation_source.cpp b/src/mbgl/annotation/render_annotation_source.cpp index a0b69af8d5..38ca5ccd0b 100644 --- a/src/mbgl/annotation/render_annotation_source.cpp +++ b/src/mbgl/annotation/render_annotation_source.cpp @@ -41,6 +41,7 @@ void RenderAnnotationSource::update(Immutable baseImpl_, // Zoom level 16 is typically sufficient for annotations. // See https://github.com/mapbox/mapbox-gl-native/issues/10197 { 0, 16 }, + optional {}, [&] (const OverscaledTileID& tileID) { return std::make_unique(tileID, parameters); }); diff --git a/src/mbgl/renderer/sources/render_custom_geometry_source.cpp b/src/mbgl/renderer/sources/render_custom_geometry_source.cpp index 111f0234ed..df615a7e20 100644 --- a/src/mbgl/renderer/sources/render_custom_geometry_source.cpp +++ b/src/mbgl/renderer/sources/render_custom_geometry_source.cpp @@ -44,6 +44,7 @@ void RenderCustomGeometrySource::update(Immutable baseImpl_ SourceType::CustomVector, util::tileSize, impl().getZoomRange(), + {}, [&] (const OverscaledTileID& tileID) { return std::make_unique(tileID, impl().id, parameters, impl().getTileOptions(), *tileLoader); }); diff --git a/src/mbgl/renderer/sources/render_geojson_source.cpp b/src/mbgl/renderer/sources/render_geojson_source.cpp index d07cfcdc41..8ea80cd813 100644 --- a/src/mbgl/renderer/sources/render_geojson_source.cpp +++ b/src/mbgl/renderer/sources/render_geojson_source.cpp @@ -62,6 +62,7 @@ void RenderGeoJSONSource::update(Immutable baseImpl_, SourceType::GeoJSON, util::tileSize, impl().getZoomRange(), + optional{}, [&] (const OverscaledTileID& tileID) { return std::make_unique(tileID, impl().id, parameters, data->getTile(tileID.canonical)); }); diff --git a/src/mbgl/renderer/sources/render_raster_source.cpp b/src/mbgl/renderer/sources/render_raster_source.cpp index f11f9b7aed..e99cd040e9 100644 --- a/src/mbgl/renderer/sources/render_raster_source.cpp +++ b/src/mbgl/renderer/sources/render_raster_source.cpp @@ -52,6 +52,7 @@ void RenderRasterSource::update(Immutable baseImpl_, SourceType::Raster, impl().getTileSize(), tileset->zoomRange, + tileset->bounds, [&] (const OverscaledTileID& tileID) { return std::make_unique(tileID, parameters, *tileset); }); diff --git a/src/mbgl/renderer/sources/render_vector_source.cpp b/src/mbgl/renderer/sources/render_vector_source.cpp index 49f8fdff2c..d53023e4d0 100644 --- a/src/mbgl/renderer/sources/render_vector_source.cpp +++ b/src/mbgl/renderer/sources/render_vector_source.cpp @@ -55,6 +55,7 @@ void RenderVectorSource::update(Immutable baseImpl_, SourceType::Vector, util::tileSize, tileset->zoomRange, + tileset->bounds, [&] (const OverscaledTileID& tileID) { return std::make_unique(tileID, impl().id, parameters, *tileset); }); diff --git a/src/mbgl/renderer/tile_pyramid.cpp b/src/mbgl/renderer/tile_pyramid.cpp index 6a711fb6d5..e474737f8d 100644 --- a/src/mbgl/renderer/tile_pyramid.cpp +++ b/src/mbgl/renderer/tile_pyramid.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -14,6 +15,7 @@ #include +#include #include namespace mbgl { @@ -61,6 +63,7 @@ void TilePyramid::update(const std::vector>& layer const SourceType type, const uint16_t tileSize, const Range zoomRange, + optional bounds, std::function (const OverscaledTileID&)> createTile) { // If we need a relayout, abandon any cached tiles; they're now stale. if (needsRelayout) { @@ -135,7 +138,15 @@ void TilePyramid::update(const std::vector>& layer auto it = tiles.find(tileID); return it == tiles.end() ? nullptr : it->second.get(); }; + + optional tileRange = {}; + if (bounds) { + tileRange = util::TileRange::fromLatLngBounds(*bounds, std::min(tileZoom, (int32_t)zoomRange.max)); + } auto createTileFn = [&](const OverscaledTileID& tileID) -> Tile* { + if (tileRange && !tileRange->contains(tileID.canonical)) { + return nullptr; + } std::unique_ptr tile = cache.get(tileID); if (!tile) { tile = createTile(tileID); diff --git a/src/mbgl/renderer/tile_pyramid.hpp b/src/mbgl/renderer/tile_pyramid.hpp index feab8a838c..3755cee06d 100644 --- a/src/mbgl/renderer/tile_pyramid.hpp +++ b/src/mbgl/renderer/tile_pyramid.hpp @@ -40,6 +40,7 @@ public: style::SourceType type, uint16_t tileSize, Range zoomRange, + optional bounds, std::function (const OverscaledTileID&)> createTile); void startRender(PaintParameters&); diff --git a/src/mbgl/style/conversion/tileset.cpp b/src/mbgl/style/conversion/tileset.cpp index b9383c41b8..6e559c0cac 100644 --- a/src/mbgl/style/conversion/tileset.cpp +++ b/src/mbgl/style/conversion/tileset.cpp @@ -1,9 +1,14 @@ #include +#include namespace mbgl { namespace style { namespace conversion { +bool validateLatitude(const double lat) { + return lat < 90 && lat > -90; +} + optional Converter::operator()(const Convertible& value, Error& error) const { Tileset result; @@ -65,6 +70,32 @@ optional Converter::operator()(const Convertible& value, Error result.attribution = std::move(*attribution); } + auto boundsValue = objectMember(value, "bounds"); + if (boundsValue) { + if (!isArray(*boundsValue) || arrayLength(*boundsValue) != 4) { + error = { "bounds must be an array with left, bottom, top, and right values" }; + return {}; + } + optional left = toDouble(arrayMember(*boundsValue, 0)); + optional bottom = toDouble(arrayMember(*boundsValue, 1)); + optional right = toDouble(arrayMember(*boundsValue, 2)); + optional top = toDouble(arrayMember(*boundsValue, 3)); + + if (!left || !right || !bottom || !top) { + error = { "bounds array must contain numeric longitude and latitude values" }; + return {}; + } + if (!validateLatitude(*bottom) || !validateLatitude(*top) || top <= bottom){ + error = { "bounds latitude values must be between -90 and 90 with bottom less than top" }; + return {}; + } + if(*left >= *right) { + error = { "bounds left longitude should be less than right longitude" }; + return {}; + } + result.bounds = LatLngBounds::hull({ *bottom, *left }, { *top, *right }); + } + return result; } diff --git a/src/mbgl/style/custom_tile_loader.cpp b/src/mbgl/style/custom_tile_loader.cpp index 76248b84bd..1c587302b8 100644 --- a/src/mbgl/style/custom_tile_loader.cpp +++ b/src/mbgl/style/custom_tile_loader.cpp @@ -1,5 +1,6 @@ #include #include +#include namespace mbgl { namespace style { @@ -79,9 +80,15 @@ void CustomTileLoader::invalidateTile(const CanonicalTileID& tileID) { } void CustomTileLoader::invalidateRegion(const LatLngBounds& bounds, Range ) { + std::map tileRanges; + for (auto idtuple= tileCallbackMap.begin(); idtuple != tileCallbackMap.end(); idtuple++) { - const LatLngBounds tileBounds(idtuple->first); - if (tileBounds.intersects(bounds, LatLng::Wrapped) || bounds.contains(tileBounds, LatLng::Wrapped) || tileBounds.contains(bounds, LatLng::Wrapped)) { + auto zoom = idtuple->first.z; + auto tileRange = tileRanges.find(zoom); + if(tileRange == tileRanges.end()) { + tileRange = tileRanges.emplace(std::make_pair(zoom, util::TileRange::fromLatLngBounds(bounds, zoom))).first; + } + if (tileRange->second.contains(idtuple->first)) { for (auto iter = idtuple->second.begin(); iter != idtuple->second.end(); iter++) { auto actor = std::get<2>(*iter); actor.invoke(&CustomGeometryTile::invalidateTileData); diff --git a/src/mbgl/util/geo.cpp b/src/mbgl/util/geo.cpp index f38aba20c4..a04e2c5355 100644 --- a/src/mbgl/util/geo.cpp +++ b/src/mbgl/util/geo.cpp @@ -1,6 +1,8 @@ #include #include #include +#include +#include #include @@ -32,6 +34,86 @@ LatLngBounds::LatLngBounds(const CanonicalTileID& id) ne({ lat_(id.z, id.y), lon_(id.z, id.x + 1) }) { } +bool LatLngBounds::contains(const CanonicalTileID& tileID) const { + return util::TileRange::fromLatLngBounds(*this, tileID.z).contains(tileID); +} + +bool LatLngBounds::contains(const LatLng& point, LatLng::WrapMode wrap /*= LatLng::Unwrapped*/) const { + bool containsLatitude = point.latitude() >= sw.latitude() && + point.latitude() <= ne.latitude(); + if (!containsLatitude) { + return false; + } + + bool containsUnwrappedLongitude = point.longitude() >= sw.longitude() && + point.longitude() <= ne.longitude(); + if (containsUnwrappedLongitude) { + return true; + } else if (wrap == LatLng::Wrapped) { + LatLngBounds wrapped(sw.wrapped(), ne.wrapped()); + auto ptLon = point.wrapped().longitude(); + if (crossesAntimeridian()) { + return (ptLon >= wrapped.sw.longitude() && + ptLon <= util::LONGITUDE_MAX) || + (ptLon <= wrapped.ne.longitude() && + ptLon >= -util::LONGITUDE_MAX); + } else { + return (ptLon >= wrapped.sw.longitude() && + ptLon <= wrapped.ne.longitude()); + } + } + return false; +} + +bool LatLngBounds::contains(const LatLngBounds& area, LatLng::WrapMode wrap /*= LatLng::Unwrapped*/) const { + bool containsLatitude = area.north() <= north() && area.south() >= south(); + if (!containsLatitude) { + return false; + } + + bool containsUnwrapped = area.east() <= east() && area.west() >= west(); + if(containsUnwrapped) { + return true; + } else if (wrap == LatLng::Wrapped) { + LatLngBounds wrapped(sw.wrapped(), ne.wrapped()); + LatLngBounds other(area.sw.wrapped(), area.ne.wrapped()); + if (crossesAntimeridian() & !area.crossesAntimeridian()) { + return (other.east() <= util::LONGITUDE_MAX && other.west() >= wrapped.west()) || + (other.east() <= wrapped.east() && other.west() >= -util::LONGITUDE_MAX); + } else { + return other.east() <= wrapped.east() && other.west() >= wrapped.west(); + } + } + return false; +} + +bool LatLngBounds::intersects(const LatLngBounds area, LatLng::WrapMode wrap /*= LatLng::Unwrapped*/) const { + bool latitudeIntersects = area.north() > south() && area.south() < north(); + if (!latitudeIntersects) { + return false; + } + + bool longitudeIntersects = area.east() > west() && area.west() < east(); + if (longitudeIntersects) { + return true; + } else if (wrap == LatLng::Wrapped) { + LatLngBounds wrapped(sw.wrapped(), ne.wrapped()); + LatLngBounds other(area.sw.wrapped(), area.ne.wrapped()); + if (crossesAntimeridian()) { + return area.crossesAntimeridian() || + other.east() > wrapped.west() || + other.west() < wrapped.east(); + } else if (other.crossesAntimeridian()){ + return other.east() > wrapped.west() || + other.west() < wrapped.east(); + } else { + return other.east() > wrapped.west() && + other.west() < wrapped.east(); + } + } + return false; +} + ScreenCoordinate EdgeInsets::getCenter(uint16_t width, uint16_t height) const { return { (width - left() - right()) / 2.0 + left(), diff --git a/src/mbgl/util/tile_range.hpp b/src/mbgl/util/tile_range.hpp new file mode 100644 index 0000000000..f630a49078 --- /dev/null +++ b/src/mbgl/util/tile_range.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include +#include + +namespace mbgl { + +namespace util { + +class TileRange { +public: + Range> range; + uint8_t z; + + // Compute the range of tiles covered by the bounds. + static TileRange fromLatLngBounds(const LatLngBounds& bounds, uint8_t z) { + auto swProj = Projection::project(bounds.southwest().wrapped(), z); + auto ne = bounds.northeast(); + auto neProj = Projection::project(ne.longitude() > util::LONGITUDE_MAX ? ne.wrapped() : ne , z); + const auto minX = std::floor(swProj.x); + const auto maxX = std::ceil(neProj.x); + const auto minY = std::floor(neProj.y); + const auto maxY = std::ceil(swProj.y); + return TileRange({ {minX, minY}, {maxX, maxY} }, z); + } + + bool contains(const CanonicalTileID& tileID) { + return z == tileID.z && + (range.min.x >= range.max.x ? //For wrapped bounds + tileID.x >= range.min.x || tileID.x < range.max.x : + tileID.x < range.max.x && tileID.x >= range.min.x) && + tileID.y < range.max.y && + tileID.y >= range.min.y; + } + +private: + TileRange(Range> range_, uint8_t z_) + : range(range_), + z(z_) { + } + +}; + +} // namespace util +} // namespace mbgl diff --git a/test/style/conversion/tileset.test.cpp b/test/style/conversion/tileset.test.cpp new file mode 100644 index 0000000000..8002cd038f --- /dev/null +++ b/test/style/conversion/tileset.test.cpp @@ -0,0 +1,72 @@ +#include + +#include +#include + +#include + +using namespace mbgl; +using namespace mbgl::style::conversion; + +TEST(Tileset, Empty) { + Error error; + mbgl::optional converted = convertJSON("{}", error); + EXPECT_FALSE((bool) converted); +} + +TEST(Tileset, ErrorHandling) { + Error error; + mbgl::optional converted = convertJSON(R"JSON({ + "tiles": "should not be a string" + })JSON", error); + EXPECT_FALSE((bool) converted); +} + +TEST(Tileset, InvalidBounds) { + { + Error error; + mbgl::optional converted = convertJSON(R"JSON({ + "tiles": ["http://mytiles"], + "bounds": [73, -180, -73, -120] + })JSON", error); + + EXPECT_FALSE((bool) converted); + } + { + Error error; + mbgl::optional converted = convertJSON(R"JSON({ + "tiles": ["http://mytiles"], + "bounds": [-120] + })JSON", error); + + EXPECT_FALSE((bool) converted); + } + { + Error error; + mbgl::optional converted = convertJSON(R"JSON({ + "tiles": ["http://mytiles"], + "bounds": "should not be a string" + })JSON", error); + + EXPECT_FALSE((bool) converted); + } +} + +TEST(Tileset, FullConversion) { + Error error; + Tileset converted = *convertJSON(R"JSON({ + "tiles": ["http://mytiles"], + "scheme": "xyz", + "minzoom": 1, + "maxzoom": 2, + "attribution": "mapbox", + "bounds": [-180, -73, -120, 73] + })JSON", error); + + EXPECT_EQ(converted.tiles[0], "http://mytiles"); + EXPECT_EQ(converted.scheme, Tileset::Scheme::XYZ); + EXPECT_EQ(converted.zoomRange.min, 1); + EXPECT_EQ(converted.zoomRange.max, 2); + EXPECT_EQ(converted.attribution, "mapbox"); + EXPECT_EQ(converted.bounds, LatLngBounds::hull({73, -180}, {-73, -120})); +} diff --git a/test/util/tile_range.test.cpp b/test/util/tile_range.test.cpp new file mode 100644 index 0000000000..dc8ae28705 --- /dev/null +++ b/test/util/tile_range.test.cpp @@ -0,0 +1,56 @@ + +#include +#include +#include + +#include + +using namespace mbgl; + +TEST(TileRange, ContainsWorld) { + auto range = util::TileRange::fromLatLngBounds(LatLngBounds::world(), 0); + EXPECT_TRUE(range.contains(CanonicalTileID(0, 0, 0))); + EXPECT_FALSE(range.contains(CanonicalTileID(10, 0, 0))); +} + +TEST(TileRange, ContainsBoundsFromTile) { + { + const LatLngBounds bounds{ CanonicalTileID(3, 7, 0) }; + auto range = util::TileRange::fromLatLngBounds(bounds, 3); + EXPECT_TRUE(range.contains(CanonicalTileID(3, 7, 0))); + } + { + const LatLngBounds bounds{ CanonicalTileID(10, 162, 395) }; + auto range = util::TileRange::fromLatLngBounds(bounds, 10); + EXPECT_TRUE(range.contains(CanonicalTileID(10, 162, 395))); + } +} +TEST(TileRange, ContainsIntersectingTiles) { + auto bounds = LatLngBounds::hull({ 37.6609, -122.5744 }, { 37.8271, -122.3204 }); + auto range = util::TileRange::fromLatLngBounds(bounds, 13); + EXPECT_FALSE(range.contains(CanonicalTileID(13, 1316, 3100))); + EXPECT_TRUE(range.contains(CanonicalTileID(13, 1310, 3166))); +} + +TEST(TileRange, ContainsWrappedBounds) { + auto wrappedBounds = LatLngBounds::hull({ 37.6609, 237.6796 }, { 37.8271, 237.4256 }); + auto range = util::TileRange::fromLatLngBounds(wrappedBounds, 13); + EXPECT_FALSE(range.contains(CanonicalTileID(13, 1316, 3100))); + EXPECT_TRUE(range.contains(CanonicalTileID(13, 1310, 3166))); +} + +TEST(TileRange, ContainsBoundsCrossingAntimeridian) { + { + auto cossingBounds = LatLngBounds::hull({-20.9615, -214.309}, {19.477, -155.830}); + auto range = util::TileRange::fromLatLngBounds(cossingBounds, 1); + EXPECT_TRUE(range.contains(CanonicalTileID(1, 1, 1))); + EXPECT_TRUE(range.contains(CanonicalTileID(1, 0, 0))); + } + { + auto cossingBounds = LatLngBounds::hull({-20.9615, -214.309}, {19.477, -155.830}); + auto range = util::TileRange::fromLatLngBounds(cossingBounds, 6); + EXPECT_FALSE(range.contains(CanonicalTileID(6, 55, 34))); + EXPECT_FALSE(range.contains(CanonicalTileID(6, 5, 28))); + EXPECT_TRUE(range.contains(CanonicalTileID(6, 63, 28))); + } +} -- cgit v1.2.1