Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Commit

Permalink
Support TileJSON bounds property (#10701)
Browse files Browse the repository at this point in the history
* [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.
  • Loading branch information
Asheem Mamoowala committed Feb 7, 2018
1 parent 057af7e commit fae7a14
Show file tree
Hide file tree
Showing 18 changed files with 436 additions and 17 deletions.
1 change: 1 addition & 0 deletions cmake/core-files.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,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
Expand Down
2 changes: 2 additions & 0 deletions cmake/test-files.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,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
test/style/filter.test.cpp
Expand Down Expand Up @@ -138,6 +139,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/url.test.cpp
Expand Down
31 changes: 31 additions & 0 deletions include/mbgl/style/conversion/tileset.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ namespace conversion {
template <>
struct Converter<Tileset> {
public:

bool validateLatitude(const double lat) const {
return lat < 90 && lat > -90;
}

template <class V>
optional<Tileset> operator()(const V& value, Error& error) const {
Tileset result;
Expand Down Expand Up @@ -72,6 +77,32 @@ struct Converter<Tileset> {
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<double> left = toDouble(arrayMember(*boundsValue, 0));
optional<double> bottom = toDouble(arrayMember(*boundsValue, 1));
optional<double> right = toDouble(arrayMember(*boundsValue, 2));
optional<double> 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;
}
};
Expand Down
18 changes: 7 additions & 11 deletions include/mbgl/util/geo.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,19 +154,15 @@ class LatLngBounds {
sw.longitude() > ne.longitude();
}

bool contains(const LatLng& point) const {
return (point.latitude() >= sw.latitude() &&
point.latitude() <= ne.latitude() &&
point.longitude() >= sw.longitude() &&
point.longitude() <= ne.longitude());
bool crossesAntimeridian() const {
return (sw.wrapped().longitude() > ne.wrapped().longitude());
}

bool intersects(const LatLngBounds area) const {
return (area.ne.latitude() > sw.latitude() &&
area.sw.latitude() < ne.latitude() &&
area.ne.longitude() > sw.longitude() &&
area.sw.longitude() < ne.longitude());
}
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 intersects(const LatLngBounds area, LatLng::WrapMode wrap = LatLng::Unwrapped) const;

private:
LatLng sw;
Expand Down
4 changes: 4 additions & 0 deletions include/mbgl/util/projection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ class Projection {
return project_(latLng, worldSize(scale));
}

static Point<double> project(const LatLng& latLng, uint8_t zoom) {
return project_(latLng, std::pow(2.0, zoom));
}

static LatLng unproject(const Point<double>& p, double scale, LatLng::WrapMode wrapMode = LatLng::Unwrapped) {
auto p2 = p * util::DEGREES_MAX / worldSize(scale);
return LatLng {
Expand Down
14 changes: 9 additions & 5 deletions include/mbgl/util/tileset.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

#include <mbgl/util/range.hpp>
#include <mbgl/util/constants.hpp>

#include <mbgl/util/optional.hpp>
#include <mbgl/util/geo.hpp>
#include <tuple>
#include <vector>
#include <string>
#include <cstdint>
Expand All @@ -17,6 +19,7 @@ class Tileset {
Range<uint8_t> zoomRange;
std::string attribution;
Scheme scheme;
optional<LatLngBounds> bounds;

Tileset(std::vector<std::string> tiles_ = std::vector<std::string>(),
Range<uint8_t> zoomRange_ = { 0, util::DEFAULT_MAX_ZOOM },
Expand All @@ -25,13 +28,14 @@ class Tileset {
: 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);
}
};

Expand Down
6 changes: 5 additions & 1 deletion src/mbgl/algorithm/update_renderables.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
1 change: 1 addition & 0 deletions src/mbgl/annotation/render_annotation_source.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ void RenderAnnotationSource::update(Immutable<style::Source::Impl> baseImpl_,
// Zoom level 16 is typically sufficient for annotations.
// See https://github.com/mapbox/mapbox-gl-native/issues/10197
{ 0, 16 },
optional<LatLngBounds> {},
[&] (const OverscaledTileID& tileID) {
return std::make_unique<AnnotationTile>(tileID, parameters);
});
Expand Down
1 change: 1 addition & 0 deletions src/mbgl/renderer/sources/render_geojson_source.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ void RenderGeoJSONSource::update(Immutable<style::Source::Impl> baseImpl_,
SourceType::GeoJSON,
util::tileSize,
impl().getZoomRange(),
optional<LatLngBounds>{},
[&] (const OverscaledTileID& tileID) {
return std::make_unique<GeoJSONTile>(tileID, impl().id, parameters, data->getTile(tileID.canonical));
});
Expand Down
1 change: 1 addition & 0 deletions src/mbgl/renderer/sources/render_raster_source.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ void RenderRasterSource::update(Immutable<style::Source::Impl> baseImpl_,
SourceType::Raster,
impl().getTileSize(),
tileset->zoomRange,
tileset->bounds,
[&] (const OverscaledTileID& tileID) {
return std::make_unique<RasterTile>(tileID, parameters, *tileset);
});
Expand Down
1 change: 1 addition & 0 deletions src/mbgl/renderer/sources/render_vector_source.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ void RenderVectorSource::update(Immutable<style::Source::Impl> baseImpl_,
SourceType::Vector,
util::tileSize,
tileset->zoomRange,
tileset->bounds,
[&] (const OverscaledTileID& tileID) {
return std::make_unique<VectorTile>(tileID, impl().id, parameters, *tileset);
});
Expand Down
11 changes: 11 additions & 0 deletions src/mbgl/renderer/tile_pyramid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@
#include <mbgl/text/placement_config.hpp>
#include <mbgl/math/clamp.hpp>
#include <mbgl/util/tile_cover.hpp>
#include <mbgl/util/tile_range.hpp>
#include <mbgl/util/enum.hpp>
#include <mbgl/util/logging.hpp>

#include <mbgl/algorithm/update_renderables.hpp>

#include <mapbox/geometry/envelope.hpp>

#include <cmath>
#include <algorithm>

namespace mbgl {
Expand Down Expand Up @@ -62,6 +64,7 @@ void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layer
const SourceType type,
const uint16_t tileSize,
const Range<uint8_t> zoomRange,
optional<LatLngBounds> bounds,
std::function<std::unique_ptr<Tile> (const OverscaledTileID&)> createTile) {
// If we need a relayout, abandon any cached tiles; they're now stale.
if (needsRelayout) {
Expand Down Expand Up @@ -134,7 +137,15 @@ void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layer
auto it = tiles.find(tileID);
return it == tiles.end() ? nullptr : it->second.get();
};

optional<util::TileRange> 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> tile = cache.get(tileID);
if (!tile) {
tile = createTile(tileID);
Expand Down
1 change: 1 addition & 0 deletions src/mbgl/renderer/tile_pyramid.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class TilePyramid {
SourceType type,
uint16_t tileSize,
Range<uint8_t> zoomRange,
optional<LatLngBounds> bounds,
std::function<std::unique_ptr<Tile> (const OverscaledTileID&)> createTile);

void startRender(PaintParameters&);
Expand Down
104 changes: 104 additions & 0 deletions src/mbgl/style/conversion/tileset.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#include <mbgl/style/conversion/tileset.hpp>
#include <mbgl/util/geo.hpp>

namespace mbgl {
namespace style {
namespace conversion {

bool validateLatitude(const double lat) {
return lat < 90 && lat > -90;
}

optional<Tileset> Converter<Tileset>::operator()(const Convertible& value, Error& error) const {
Tileset result;

auto tiles = objectMember(value, "tiles");
if (!tiles) {
error = { "source must have tiles" };
return {};
}

if (!isArray(*tiles)) {
error = { "source tiles must be an array" };
return {};
}

for (std::size_t i = 0; i < arrayLength(*tiles); i++) {
optional<std::string> urlTemplate = toString(arrayMember(*tiles, i));
if (!urlTemplate) {
error = { "source tiles member must be a string" };
return {};
}
result.tiles.push_back(std::move(*urlTemplate));
}

auto schemeValue = objectMember(value, "scheme");
if (schemeValue) {
optional<std::string> scheme = toString(*schemeValue);
if (scheme && *scheme == "tms") {
result.scheme = Tileset::Scheme::TMS;
}
}

auto minzoomValue = objectMember(value, "minzoom");
if (minzoomValue) {
optional<float> minzoom = toNumber(*minzoomValue);
if (!minzoom || *minzoom < 0 || *minzoom > std::numeric_limits<uint8_t>::max()) {
error = { "invalid minzoom" };
return {};
}
result.zoomRange.min = *minzoom;
}

auto maxzoomValue = objectMember(value, "maxzoom");
if (maxzoomValue) {
optional<float> maxzoom = toNumber(*maxzoomValue);
if (!maxzoom || *maxzoom < 0 || *maxzoom > std::numeric_limits<uint8_t>::max()) {
error = { "invalid maxzoom" };
return {};
}
result.zoomRange.max = *maxzoom;
}

auto attributionValue = objectMember(value, "attribution");
if (attributionValue) {
optional<std::string> attribution = toString(*attributionValue);
if (!attribution) {
error = { "source attribution must be a string" };
return {};
}
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<double> left = toDouble(arrayMember(*boundsValue, 0));
optional<double> bottom = toDouble(arrayMember(*boundsValue, 1));
optional<double> right = toDouble(arrayMember(*boundsValue, 2));
optional<double> 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;
}

} // namespace conversion
} // namespace style
} // namespace mbgl
Loading

0 comments on commit fae7a14

Please sign in to comment.