diff options
m--------- | .mason | 0 | ||||
-rwxr-xr-x | configure | 4 | ||||
-rw-r--r-- | include/mbgl/style/conversion/source.hpp | 89 | ||||
-rw-r--r-- | include/mbgl/style/sources/geojson_source.hpp | 35 | ||||
-rw-r--r-- | include/mbgl/util/geojson.hpp | 18 | ||||
-rw-r--r-- | mbgl.gypi | 4 | ||||
-rw-r--r-- | package.json | 2 | ||||
-rw-r--r-- | platform/android/scripts/configure.sh | 2 | ||||
-rw-r--r-- | platform/default/mbgl/storage/offline_download.cpp | 86 | ||||
-rw-r--r-- | platform/ios/ios.xcodeproj/project.pbxproj | 6 | ||||
-rw-r--r-- | platform/ios/scripts/configure.sh | 2 | ||||
-rw-r--r-- | platform/linux/scripts/configure.sh | 2 | ||||
-rw-r--r-- | platform/macos/macos.xcodeproj/project.pbxproj | 4 | ||||
-rw-r--r-- | platform/macos/scripts/configure.sh | 2 | ||||
-rw-r--r-- | platform/qt/scripts/configure.sh | 2 | ||||
-rw-r--r-- | src/mbgl/style/sources/geojson_source.cpp | 15 | ||||
-rw-r--r-- | src/mbgl/style/sources/geojson_source_impl.cpp | 85 | ||||
-rw-r--r-- | src/mbgl/style/sources/geojson_source_impl.hpp | 16 | ||||
-rw-r--r-- | src/mbgl/tile/geojson_tile.cpp | 24 | ||||
-rw-r--r-- | src/mbgl/tile/geojson_tile.hpp | 11 | ||||
-rw-r--r-- | src/mbgl/util/geojson.cpp | 11 | ||||
-rw-r--r-- | test/test.gypi | 5 |
22 files changed, 293 insertions, 132 deletions
diff --git a/.mason b/.mason -Subproject d3594e134cd3f6d12aafb0b79d85f087751dea7 +Subproject fc590c762474eae5bb78c3c825247face0aed9a @@ -119,7 +119,9 @@ print_flags nunicode static_libs cflags ldflags print_flags libzip static_libs cflags ldflags print_flags geometry cflags print_flags geojson static_libs cflags ldflags -print_flags geojsonvt static_libs cflags ldflags +print_flags geojsonvt cflags +print_flags supercluster cflags +print_flags kdbush cflags print_flags variant cflags print_flags rapidjson static_libs cflags ldflags print_flags gtest static_libs cflags ldflags diff --git a/include/mbgl/style/conversion/source.hpp b/include/mbgl/style/conversion/source.hpp index 00c6afb9fe..c4b2fe303f 100644 --- a/include/mbgl/style/conversion/source.hpp +++ b/include/mbgl/style/conversion/source.hpp @@ -1,12 +1,12 @@ #pragma once +#include <mbgl/style/conversion.hpp> +#include <mbgl/style/conversion/geojson.hpp> +#include <mbgl/style/conversion/tileset.hpp> #include <mbgl/style/source.hpp> #include <mbgl/style/sources/geojson_source.hpp> #include <mbgl/style/sources/raster_source.hpp> #include <mbgl/style/sources/vector_source.hpp> -#include <mbgl/style/conversion.hpp> -#include <mbgl/style/conversion/tileset.hpp> -#include <mbgl/style/conversion/geojson.hpp> namespace mbgl { namespace style { @@ -18,17 +18,17 @@ public: template <class V> Result<std::unique_ptr<Source>> operator()(const V& value, const std::string& id) const { if (!isObject(value)) { - return Error { "source must be an object" }; + return Error{ "source must be an object" }; } auto typeValue = objectMember(value, "type"); if (!typeValue) { - return Error { "source must have a type" }; + return Error{ "source must have a type" }; } optional<std::string> type = toString(*typeValue); if (!type) { - return Error { "source type must be a string" }; + return Error{ "source type must be a string" }; } if (*type == "raster") { @@ -38,7 +38,7 @@ public: } else if (*type == "geojson") { return convertGeoJSONSource(id, value); } else { - return Error { "invalid source type" }; + return Error{ "invalid source type" }; } } @@ -57,14 +57,15 @@ private: optional<std::string> url = toString(*urlVal); if (!url) { - return Error { "source url must be a string" }; + return Error{ "source url must be a string" }; } return *url; } template <class V> - Result<std::unique_ptr<Source>> convertRasterSource(const std::string& id, const V& value) const { + Result<std::unique_ptr<Source>> convertRasterSource(const std::string& id, + const V& value) const { Result<variant<std::string, Tileset>> urlOrTileset = convertURLOrTileset(value); if (!urlOrTileset) { return urlOrTileset.error(); @@ -75,7 +76,7 @@ private: if (tileSizeValue) { optional<float> size = toNumber(*tileSizeValue); if (!size || *size < 0 || *size > std::numeric_limits<uint16_t>::max()) { - return Error { "invalid tileSize" }; + return Error{ "invalid tileSize" }; } tileSize = *size; } @@ -84,7 +85,8 @@ private: } template <class V> - Result<std::unique_ptr<Source>> convertVectorSource(const std::string& id, const V& value) const { + Result<std::unique_ptr<Source>> convertVectorSource(const std::string& id, + const V& value) const { Result<variant<std::string, Tileset>> urlOrTileset = convertURLOrTileset(value); if (!urlOrTileset) { return urlOrTileset.error(); @@ -94,13 +96,70 @@ private: } template <class V> - Result<std::unique_ptr<Source>> convertGeoJSONSource(const std::string& id, const V& value) const { + Result<std::unique_ptr<Source>> convertGeoJSONSource(const std::string& id, + const V& value) const { auto dataValue = objectMember(value, "data"); if (!dataValue) { - return Error { "GeoJSON source must have a data value" }; + return Error{ "GeoJSON source must have a data value" }; + } + + GeoJSONOptions options; + + const auto maxzoomValue = objectMember(value, "maxzoom"); + if (maxzoomValue) { + if (toNumber(*maxzoomValue)) { + options.maxzoom = static_cast<uint8_t>(*toNumber(*maxzoomValue)); + } else { + return Error{ "GeoJSON source maxzoom value must be a number" }; + } + } + + const auto bufferValue = objectMember(value, "buffer"); + if (bufferValue) { + if (toNumber(*bufferValue)) { + options.buffer = static_cast<uint16_t>(*toNumber(*bufferValue)); + } else { + return Error{ "GeoJSON source buffer value must be a number" }; + } + } + + const auto toleranceValue = objectMember(value, "tolerance"); + if (toleranceValue) { + if (toNumber(*toleranceValue)) { + options.tolerance = static_cast<double>(*toNumber(*toleranceValue)); + } else { + return Error{ "GeoJSON source tolerance value must be a number" }; + } + } + + const auto clusterValue = objectMember(value, "cluster"); + if (clusterValue) { + if (toBool(*clusterValue)) { + options.cluster = *toBool(*clusterValue); + } else { + return Error{ "GeoJSON source cluster value must be a boolean" }; + } + } + + const auto clusterMaxZoomValue = objectMember(value, "clusterMaxZoom"); + if (clusterMaxZoomValue) { + if (toNumber(*clusterMaxZoomValue)) { + options.clusterMaxZoom = static_cast<uint8_t>(*toNumber(*clusterMaxZoomValue)); + } else { + return Error{ "GeoJSON source clusterMaxZoom value must be a number" }; + } + } + + const auto clusterRadiusValue = objectMember(value, "clusterRadius"); + if (clusterRadiusValue) { + if (toNumber(*clusterRadiusValue)) { + options.clusterRadius = static_cast<double>(*toNumber(*clusterRadiusValue)); + } else { + return Error{ "GeoJSON source clusterRadius value must be a number" }; + } } - auto result = std::make_unique<GeoJSONSource>(id); + auto result = std::make_unique<GeoJSONSource>(id, options); if (isObject(*dataValue)) { Result<GeoJSON> geoJSON = convertGeoJSON(*dataValue); @@ -111,7 +170,7 @@ private: } else if (toString(*dataValue)) { result->setURL(*toString(*dataValue)); } else { - return Error { "GeoJSON data must be a URL or an object" }; + return Error{ "GeoJSON data must be a URL or an object" }; } return std::move(result); diff --git a/include/mbgl/style/sources/geojson_source.hpp b/include/mbgl/style/sources/geojson_source.hpp index 3736dd44bc..37ddce1bcc 100644 --- a/include/mbgl/style/sources/geojson_source.hpp +++ b/include/mbgl/style/sources/geojson_source.hpp @@ -3,15 +3,46 @@ #include <mbgl/style/source.hpp> #include <mbgl/util/geojson.hpp> +#include <mapbox/geojson.hpp> + +namespace mapbox { + +namespace geojsonvt { +class GeoJSONVT; +} // namespace geojsonvt + +namespace supercluster { +class Supercluster; +} // namespace supercluster + +} // namespace mapbox + namespace mbgl { namespace style { +using GeoJSONVTPointer = std::unique_ptr<mapbox::geojsonvt::GeoJSONVT>; +using SuperclusterPointer = std::unique_ptr<mapbox::supercluster::Supercluster>; + +struct GeoJSONOptions { + // GeoJSON-VT options + uint8_t maxzoom = 18; + uint16_t buffer = 128; + double tolerance = 0.375; + + // Supercluster options + bool cluster = false; + uint16_t clusterRadius = 50; + uint8_t clusterMaxZoom = 17; +}; + class GeoJSONSource : public Source { public: - GeoJSONSource(const std::string& id); + GeoJSONSource(const std::string& id, const GeoJSONOptions options_ = GeoJSONOptions()); void setURL(const std::string& url); - void setGeoJSON(GeoJSON&&); + void setGeoJSON(const GeoJSON&); + + std::string getURL(); // Private implementation diff --git a/include/mbgl/util/geojson.hpp b/include/mbgl/util/geojson.hpp index 3fd8c6ac4b..b4e789a3ac 100644 --- a/include/mbgl/util/geojson.hpp +++ b/include/mbgl/util/geojson.hpp @@ -1,22 +1,10 @@ #pragma once -#include <memory> - -namespace mapbox { -namespace geojsonvt { -class GeoJSONVT; -} // namespace geojsonvt -} // namespace mapbox +#include <mapbox/geojson.hpp> namespace mbgl { -class GeoJSON { -public: - GeoJSON(std::unique_ptr<mapbox::geojsonvt::GeoJSONVT>); - GeoJSON(GeoJSON&&); - ~GeoJSON(); - - std::unique_ptr<mapbox::geojsonvt::GeoJSONVT> impl; -}; +using GeoJSON = mapbox::geojson::geojson; +using FeatureCollection = mapbox::geojson::feature_collection; } // namespace mbgl @@ -196,6 +196,8 @@ '<@(geometry_cflags)', '<@(geojson_cflags)', '<@(geojsonvt_cflags)', + '<@(supercluster_cflags)', + '<@(kdbush_cflags)', '<@(rapidjson_cflags)', '<@(variant_cflags)', '<@(earcut_cflags)', @@ -313,12 +315,14 @@ 'cflags_cc': [ '<@(variant_cflags)', '<@(geometry_cflags)', + '<@(geojson_cflags)', '<@(unique_resource_cflags)', ], 'xcode_settings': { 'OTHER_CPLUSPLUSFLAGS': [ '<@(variant_cflags)', '<@(geometry_cflags)', + '<@(geojson_cflags)', '<@(unique_resource_cflags)', ], }, diff --git a/package.json b/package.json index ab3d95380a..fe15f5ed61 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "express": "^4.11.1", "mapbox-gl-shaders": "mapbox/mapbox-gl-shaders#4d1f89514bf03536c8e682439df165c33a37122a", "mapbox-gl-style-spec": "mapbox/mapbox-gl-style-spec#83b1a3e5837d785af582efd5ed1a212f2df6a4ae", - "mapbox-gl-test-suite": "mapbox/mapbox-gl-test-suite#fbd511a54a8c27d86c5b2d7cdab94d0e507ba8e8", + "mapbox-gl-test-suite": "mapbox/mapbox-gl-test-suite#346aebbb3cf7af572e066e37c703634ceda95be2", "node-gyp": "^3.3.1", "request": "^2.72.0", "tape": "^4.5.1" diff --git a/platform/android/scripts/configure.sh b/platform/android/scripts/configure.sh index 038d4ad0ab..1c26108e11 100644 --- a/platform/android/scripts/configure.sh +++ b/platform/android/scripts/configure.sh @@ -12,6 +12,8 @@ LIBZIP_VERSION=0.11.2 GEOMETRY_VERSION=0.8.0 GEOJSON_VERSION=0.1.4 GEOJSONVT_VERSION=6.1.2 +SUPERCLUSTER_VERSION=0.2.0 +KDBUSH_VERSION=0.1.1 VARIANT_VERSION=1.1.0 RAPIDJSON_VERSION=1.0.2 JNI_HPP_VERSION=2.0.0 diff --git a/platform/default/mbgl/storage/offline_download.cpp b/platform/default/mbgl/storage/offline_download.cpp index 6ff605167a..dd66abf982 100644 --- a/platform/default/mbgl/storage/offline_download.cpp +++ b/platform/default/mbgl/storage/offline_download.cpp @@ -1,15 +1,15 @@ -#include <mbgl/storage/offline_download.hpp> -#include <mbgl/storage/offline_database.hpp> #include <mbgl/storage/file_source.hpp> +#include <mbgl/storage/offline_database.hpp> +#include <mbgl/storage/offline_download.hpp> #include <mbgl/storage/resource.hpp> #include <mbgl/storage/response.hpp> #include <mbgl/style/parser.hpp> -#include <mbgl/style/tile_source_impl.hpp> #include <mbgl/style/sources/geojson_source_impl.hpp> +#include <mbgl/style/tile_source_impl.hpp> #include <mbgl/text/glyph.hpp> -#include <mbgl/util/tile_cover.hpp> #include <mbgl/util/mapbox.hpp> #include <mbgl/util/run_loop.hpp> +#include <mbgl/util/tile_cover.hpp> #include <mbgl/util/tileset.hpp> #include <set> @@ -66,7 +66,8 @@ std::vector<Resource> OfflineDownload::glyphResources(const style::Parser& parse if (!parser.glyphURL.empty()) { for (const auto& fontStack : parser.fontStacks()) { for (uint32_t i = 0; i < 256; i++) { - result.push_back(Resource::glyphs(parser.glyphURL, fontStack, getGlyphRange(i * 256))); + result.push_back( + Resource::glyphs(parser.glyphURL, fontStack, getGlyphRange(i * 256))); } } } @@ -74,11 +75,13 @@ std::vector<Resource> OfflineDownload::glyphResources(const style::Parser& parse return result; } -std::vector<Resource> OfflineDownload::tileResources(SourceType type, uint16_t tileSize, const Tileset& tileset) const { +std::vector<Resource> +OfflineDownload::tileResources(SourceType type, uint16_t tileSize, const Tileset& tileset) const { std::vector<Resource> result; for (const auto& tile : definition.tileCover(type, tileSize, tileset.zoomRange)) { - result.push_back(Resource::tile(tileset.tiles[0], definition.pixelRatio, tile.x, tile.y, tile.z)); + result.push_back( + Resource::tile(tileset.tiles[0], definition.pixelRatio, tile.x, tile.y, tile.z)); } return result; @@ -106,18 +109,26 @@ OfflineRegionStatus OfflineDownload::getStatus() const { switch (source->baseImpl->type) { case SourceType::Vector: case SourceType::Raster: { - style::TileSourceImpl* tileSource = static_cast<style::TileSourceImpl*>(source->baseImpl.get()); + style::TileSourceImpl* tileSource = + static_cast<style::TileSourceImpl*>(source->baseImpl.get()); const variant<std::string, Tileset>& urlOrTileset = tileSource->getURLOrTileset(); if (urlOrTileset.is<Tileset>()) { - result.requiredResourceCount += tileResources(source->baseImpl->type, tileSource->getTileSize(), urlOrTileset.get<Tileset>()).size(); + result.requiredResourceCount += + tileResources(source->baseImpl->type, tileSource->getTileSize(), + urlOrTileset.get<Tileset>()) + .size(); } else { result.requiredResourceCount += 1; const std::string& url = urlOrTileset.get<std::string>(); optional<Response> sourceResponse = offlineDatabase.get(Resource::source(url)); if (sourceResponse) { - result.requiredResourceCount += tileResources(source->baseImpl->type, tileSource->getTileSize(), - style::TileSourceImpl::parseTileJSON(*sourceResponse->data, url, source->baseImpl->type, tileSource->getTileSize())).size(); + result.requiredResourceCount += + tileResources(source->baseImpl->type, tileSource->getTileSize(), + style::TileSourceImpl::parseTileJSON( + *sourceResponse->data, url, source->baseImpl->type, + tileSource->getTileSize())) + .size(); } else { result.requiredResourceCountIsPrecise = false; } @@ -126,10 +137,10 @@ OfflineRegionStatus OfflineDownload::getStatus() const { } case SourceType::GeoJSON: { - style::GeoJSONSource::Impl* geojsonSource = static_cast<style::GeoJSONSource::Impl*>(source->baseImpl.get()); - const variant<std::string, GeoJSON>& urlOrGeoJSON = geojsonSource->getURLOrGeoJSON(); + style::GeoJSONSource::Impl* geojsonSource = + static_cast<style::GeoJSONSource::Impl*>(source->baseImpl.get()); - if (urlOrGeoJSON.is<std::string>()) { + if (!geojsonSource->loaded) { result.requiredResourceCount += 1; } break; @@ -153,7 +164,7 @@ void OfflineDownload::activateDownload() { requiredSourceURLs.clear(); - ensureResource(Resource::style(definition.styleURL), [&] (Response styleResponse) { + ensureResource(Resource::style(definition.styleURL), [&](Response styleResponse) { status.requiredResourceCountIsPrecise = true; style::Parser parser; @@ -165,7 +176,8 @@ void OfflineDownload::activateDownload() { switch (type) { case SourceType::Vector: case SourceType::Raster: { - const style::TileSourceImpl* tileSource = static_cast<style::TileSourceImpl*>(source->baseImpl.get()); + const style::TileSourceImpl* tileSource = + static_cast<style::TileSourceImpl*>(source->baseImpl.get()); const variant<std::string, Tileset>& urlOrTileset = tileSource->getURLOrTileset(); const uint16_t tileSize = tileSource->getTileSize(); @@ -176,8 +188,9 @@ void OfflineDownload::activateDownload() { status.requiredResourceCountIsPrecise = false; requiredSourceURLs.insert(url); - ensureResource(Resource::source(url), [=] (Response sourceResponse) { - ensureTiles(type, tileSize, style::TileSourceImpl::parseTileJSON(*sourceResponse.data, url, type, tileSize)); + ensureResource(Resource::source(url), [=](Response sourceResponse) { + ensureTiles(type, tileSize, style::TileSourceImpl::parseTileJSON( + *sourceResponse.data, url, type, tileSize)); requiredSourceURLs.erase(url); if (requiredSourceURLs.empty()) { @@ -189,11 +202,11 @@ void OfflineDownload::activateDownload() { } case SourceType::GeoJSON: { - style::GeoJSONSource::Impl* geojsonSource = static_cast<style::GeoJSONSource::Impl*>(source->baseImpl.get()); - const variant<std::string, GeoJSON>& urlOrGeoJSON = geojsonSource->getURLOrGeoJSON(); + style::GeoJSONSource::Impl* geojsonSource = + static_cast<style::GeoJSONSource::Impl*>(source->baseImpl.get()); - if (urlOrGeoJSON.is<std::string>()) { - ensureResource(Resource::source(urlOrGeoJSON.get<std::string>())); + if (!geojsonSource->loaded) { + ensureResource(Resource::source(geojsonSource->getURL())); } break; } @@ -203,7 +216,7 @@ void OfflineDownload::activateDownload() { break; } } - + for (const auto& resource : spriteResources(parser)) { ensureResource(resource); } @@ -224,14 +237,16 @@ void OfflineDownload::ensureTiles(SourceType type, uint16_t tileSize, const Tile } } -void OfflineDownload::ensureResource(const Resource& resource, std::function<void (Response)> callback) { +void OfflineDownload::ensureResource(const Resource& resource, + std::function<void(Response)> callback) { status.requiredResourceCount++; auto workRequestsIt = requests.insert(requests.begin(), nullptr); - *workRequestsIt = util::RunLoop::Get()->invokeCancellable([=] () { + *workRequestsIt = util::RunLoop::Get()->invokeCancellable([=]() { requests.erase(workRequestsIt); - optional<std::pair<Response, uint64_t>> offlineResponse = offlineDatabase.getRegionResource(id, resource); + optional<std::pair<Response, uint64_t>> offlineResponse = + offlineDatabase.getRegionResource(id, resource); if (offlineResponse) { if (callback) { callback(offlineResponse->first); @@ -243,22 +258,22 @@ void OfflineDownload::ensureResource(const Resource& resource, std::function<voi status.completedTileCount += 1; status.completedTileSize += offlineResponse->second; } - + observer->statusChanged(status); - + if (status.complete()) { setState(OfflineRegionDownloadState::Inactive); } return; } - + if (checkTileCountLimit(resource)) { return; } auto fileRequestsIt = requests.insert(requests.begin(), nullptr); - *fileRequestsIt = onlineFileSource.request(resource, [=] (Response onlineResponse) { + *fileRequestsIt = onlineFileSource.request(resource, [=](Response onlineResponse) { if (onlineResponse.error) { observer->responseError(*onlineResponse.error); return; @@ -279,11 +294,11 @@ void OfflineDownload::ensureResource(const Resource& resource, std::function<voi } observer->statusChanged(status); - + if (checkTileCountLimit(resource)) { return; } - + if (status.complete()) { setState(OfflineRegionDownloadState::Inactive); } @@ -292,14 +307,13 @@ void OfflineDownload::ensureResource(const Resource& resource, std::function<voi } bool OfflineDownload::checkTileCountLimit(const Resource& resource) { - if (resource.kind == Resource::Kind::Tile - && util::mapbox::isMapboxURL(resource.url) - && offlineDatabase.offlineMapboxTileCountLimitExceeded()) { + if (resource.kind == Resource::Kind::Tile && util::mapbox::isMapboxURL(resource.url) && + offlineDatabase.offlineMapboxTileCountLimitExceeded()) { observer->mapboxTileCountLimitExceeded(offlineDatabase.getOfflineMapboxTileCountLimit()); setState(OfflineRegionDownloadState::Inactive); return true; } - + return false; } diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index c9f7821e8f..bfd5a5e8a9 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -1713,6 +1713,7 @@ "$(OTHER_CFLAGS)", "$(variant_cflags)", "$(geometry_cflags)", + "$(geojson_cflags)", ); PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.test; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1731,6 +1732,7 @@ "$(OTHER_CFLAGS)", "$(variant_cflags)", "$(geometry_cflags)", + "$(geojson_cflags)", ); PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.test; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1763,6 +1765,7 @@ "$(rapidjson_cflags)", "$(variant_cflags)", "$(geometry_cflags)", + "$(geojson_cflags)", ); OTHER_LDFLAGS = ( "$(sqlite_ldflags)", @@ -1804,6 +1807,7 @@ "$(rapidjson_cflags)", "$(variant_cflags)", "$(geometry_cflags)", + "$(geojson_cflags)", ); OTHER_LDFLAGS = ( "$(sqlite_ldflags)", @@ -1862,6 +1866,7 @@ "$(rapidjson_cflags)", "$(variant_cflags)", "$(geometry_cflags)", + "$(geojson_cflags)", ); OTHER_LDFLAGS = ( "-ObjC", @@ -1897,6 +1902,7 @@ "$(rapidjson_cflags)", "$(variant_cflags)", "$(geometry_cflags)", + "$(geojson_cflags)", ); OTHER_LDFLAGS = ( "-ObjC", diff --git a/platform/ios/scripts/configure.sh b/platform/ios/scripts/configure.sh index 200cedafac..82599f0fe9 100644 --- a/platform/ios/scripts/configure.sh +++ b/platform/ios/scripts/configure.sh @@ -8,6 +8,8 @@ ZLIB_VERSION=system GEOMETRY_VERSION=0.8.0 GEOJSON_VERSION=0.1.4 GEOJSONVT_VERSION=6.1.2 +SUPERCLUSTER_VERSION=0.2.0 +KDBUSH_VERSION=0.1.1 VARIANT_VERSION=1.1.0 RAPIDJSON_VERSION=1.0.2 GTEST_VERSION=1.7.0 diff --git a/platform/linux/scripts/configure.sh b/platform/linux/scripts/configure.sh index c6cd419c5f..3d37994868 100644 --- a/platform/linux/scripts/configure.sh +++ b/platform/linux/scripts/configure.sh @@ -17,6 +17,8 @@ NUNICODE_VERSION=1.6 GEOMETRY_VERSION=0.8.0 GEOJSON_VERSION=0.1.4${CXX11ABI:-} GEOJSONVT_VERSION=6.1.2 +SUPERCLUSTER_VERSION=0.2.0 +KDBUSH_VERSION=0.1.1 VARIANT_VERSION=1.1.0 RAPIDJSON_VERSION=1.0.2 GTEST_VERSION=1.7.0${CXX11ABI:-} diff --git a/platform/macos/macos.xcodeproj/project.pbxproj b/platform/macos/macos.xcodeproj/project.pbxproj index 4c2b75659c..ed980294a9 100644 --- a/platform/macos/macos.xcodeproj/project.pbxproj +++ b/platform/macos/macos.xcodeproj/project.pbxproj @@ -1039,6 +1039,7 @@ "$(rapidjson_cflags)", "$(variant_cflags)", "$(geometry_cflags)", + "$(geojson_cflags)", ); OTHER_LDFLAGS = ( "$(zlib_ldflags)", @@ -1078,6 +1079,7 @@ "$(rapidjson_cflags)", "$(variant_cflags)", "$(geometry_cflags)", + "$(geojson_cflags)", ); OTHER_LDFLAGS = ( "$(zlib_ldflags)", @@ -1104,6 +1106,7 @@ "$(OTHER_CFLAGS)", "$(variant_cflags)", "$(geometry_cflags)", + "$(geojson_cflags)", ); PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.test; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1122,6 +1125,7 @@ "$(OTHER_CFLAGS)", "$(variant_cflags)", "$(geometry_cflags)", + "$(geojson_cflags)", ); PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.test; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/platform/macos/scripts/configure.sh b/platform/macos/scripts/configure.sh index 744597eb14..d408eef7e4 100644 --- a/platform/macos/scripts/configure.sh +++ b/platform/macos/scripts/configure.sh @@ -11,6 +11,8 @@ NUNICODE_VERSION=1.6 GEOMETRY_VERSION=0.8.0 GEOJSON_VERSION=0.1.4 GEOJSONVT_VERSION=6.1.2 +SUPERCLUSTER_VERSION=0.2.0 +KDBUSH_VERSION=0.1.1 VARIANT_VERSION=1.1.0 RAPIDJSON_VERSION=1.0.2 GTEST_VERSION=1.7.0 diff --git a/platform/qt/scripts/configure.sh b/platform/qt/scripts/configure.sh index 8db0599183..23f4b0bf52 100644 --- a/platform/qt/scripts/configure.sh +++ b/platform/qt/scripts/configure.sh @@ -8,6 +8,8 @@ BOOST_VERSION=1.60.0 GEOMETRY_VERSION=0.8.0 GEOJSON_VERSION=0.1.4${CXX11ABI:-} GEOJSONVT_VERSION=6.1.2 +SUPERCLUSTER_VERSION=0.2.0 +KDBUSH_VERSION=0.1.1 GTEST_VERSION=1.7.0${CXX11ABI:-} LIBJPEG_TURBO_VERSION=1.4.2 PIXELMATCH_VERSION=0.9.0 diff --git a/src/mbgl/style/sources/geojson_source.cpp b/src/mbgl/style/sources/geojson_source.cpp index a3eec4f4ef..2bf27880b4 100644 --- a/src/mbgl/style/sources/geojson_source.cpp +++ b/src/mbgl/style/sources/geojson_source.cpp @@ -4,17 +4,22 @@ namespace mbgl { namespace style { -GeoJSONSource::GeoJSONSource(const std::string& id) - : Source(SourceType::GeoJSON, std::make_unique<GeoJSONSource::Impl>(std::move(id), *this)) - , impl(static_cast<Impl*>(baseImpl.get())) { +GeoJSONSource::GeoJSONSource(const std::string& id, const GeoJSONOptions options) + : Source(SourceType::GeoJSON, + std::make_unique<GeoJSONSource::Impl>(std::move(id), *this, options)), + impl(static_cast<Impl*>(baseImpl.get())) { } void GeoJSONSource::setURL(const std::string& url) { impl->setURL(url); } -void GeoJSONSource::setGeoJSON(GeoJSON&& geoJSON) { - impl->setGeoJSON(std::move(geoJSON)); +void GeoJSONSource::setGeoJSON(const mapbox::geojson::geojson& geoJSON) { + impl->setGeoJSON(geoJSON); +} + +std::string GeoJSONSource::getURL() { + return impl->getURL(); } } // namespace style diff --git a/src/mbgl/style/sources/geojson_source_impl.cpp b/src/mbgl/style/sources/geojson_source_impl.cpp index 9e6b3d34f6..0821ac0232 100644 --- a/src/mbgl/style/sources/geojson_source_impl.cpp +++ b/src/mbgl/style/sources/geojson_source_impl.cpp @@ -1,43 +1,37 @@ -#include <mbgl/style/sources/geojson_source_impl.hpp> -#include <mbgl/style/source_observer.hpp> +#include <mbgl/platform/log.hpp> +#include <mbgl/storage/file_source.hpp> #include <mbgl/style/conversion/geojson.hpp> +#include <mbgl/style/source_observer.hpp> +#include <mbgl/style/sources/geojson_source_impl.hpp> #include <mbgl/tile/geojson_tile.hpp> -#include <mbgl/storage/file_source.hpp> -#include <mbgl/platform/log.hpp> #include <mbgl/util/rapidjson.hpp> #include <mapbox/geojson.hpp> #include <mapbox/geojson/rapidjson.hpp> #include <mapbox/geojsonvt.hpp> #include <mapbox/geojsonvt/convert.hpp> +#include <supercluster.hpp> #include <rapidjson/error/en.h> #include <sstream> -using namespace mapbox::geojsonvt; - namespace mbgl { namespace style { namespace conversion { template <> Result<GeoJSON> convertGeoJSON(const JSValue& value) { - Options options; - options.buffer = util::EXTENT / util::tileSize * 128; - options.extent = util::EXTENT; - try { - const auto geojson = mapbox::geojson::convert(value); - return GeoJSON { std::make_unique<GeoJSONVT>(geojson, options) }; + return mapbox::geojson::convert(value); } catch (const std::exception& ex) { - return Error { ex.what() }; + return Error{ ex.what() }; } } } // namespace conversion -GeoJSONSource::Impl::Impl(std::string id_, Source& base_) - : Source::Impl(SourceType::GeoJSON, std::move(id_), base_) { +GeoJSONSource::Impl::Impl(std::string id_, Source& base_, const GeoJSONOptions options_) + : Source::Impl(SourceType::GeoJSON, std::move(id_), base_), options(options_) { } GeoJSONSource::Impl::~Impl() = default; @@ -46,12 +40,36 @@ void GeoJSONSource::Impl::setURL(std::string url) { urlOrGeoJSON = std::move(url); } -void GeoJSONSource::Impl::setGeoJSON(GeoJSON&& geoJSON) { - urlOrGeoJSON = std::move(geoJSON); +std::string GeoJSONSource::Impl::getURL() { + assert(urlOrGeoJSON.is<std::string>()); + return urlOrGeoJSON.get<std::string>(); +} + +void GeoJSONSource::Impl::setGeoJSON(const GeoJSON& geoJSON) { + double scale = util::EXTENT / util::tileSize; + + if (!options.cluster) { + mapbox::geojsonvt::Options vtOptions; + vtOptions.maxZoom = options.maxzoom; + vtOptions.extent = util::EXTENT; + vtOptions.buffer = std::round(scale * options.buffer); + vtOptions.tolerance = scale * options.tolerance; + urlOrGeoJSON = std::make_unique<mapbox::geojsonvt::GeoJSONVT>(geoJSON, vtOptions); + + } else { + mapbox::supercluster::Options clusterOptions; + clusterOptions.maxZoom = options.clusterMaxZoom; + clusterOptions.extent = util::EXTENT; + clusterOptions.radius = std::round(scale * options.clusterRadius); + + const auto& features = geoJSON.get<mapbox::geometry::feature_collection<double>>(); + urlOrGeoJSON = + std::make_unique<mapbox::supercluster::Supercluster>(features, clusterOptions); + } } void GeoJSONSource::Impl::load(FileSource& fileSource) { - if (urlOrGeoJSON.is<GeoJSON>()) { + if (!urlOrGeoJSON.is<std::string>()) { loaded = true; return; } @@ -63,19 +81,23 @@ void GeoJSONSource::Impl::load(FileSource& fileSource) { const std::string& url = urlOrGeoJSON.get<std::string>(); req = fileSource.request(Resource::source(url), [this](Response res) { if (res.error) { - observer->onSourceError(base, std::make_exception_ptr(std::runtime_error(res.error->message))); + observer->onSourceError( + base, std::make_exception_ptr(std::runtime_error(res.error->message))); } else if (res.notModified) { return; } else if (res.noContent) { - observer->onSourceError(base, std::make_exception_ptr(std::runtime_error("unexpectedly empty GeoJSON"))); + observer->onSourceError( + base, std::make_exception_ptr(std::runtime_error("unexpectedly empty GeoJSON"))); } else { rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> d; d.Parse<0>(res.data->c_str()); if (d.HasParseError()) { std::stringstream message; - message << d.GetErrorOffset() << " - " << rapidjson::GetParseError_En(d.GetParseError()); - observer->onSourceError(base, std::make_exception_ptr(std::runtime_error(message.str()))); + message << d.GetErrorOffset() << " - " + << rapidjson::GetParseError_En(d.GetParseError()); + observer->onSourceError(base, + std::make_exception_ptr(std::runtime_error(message.str()))); return; } @@ -83,13 +105,13 @@ void GeoJSONSource::Impl::load(FileSource& fileSource) { conversion::Result<GeoJSON> geoJSON = conversion::convertGeoJSON<JSValue>(d); if (!geoJSON) { - Log::Error(Event::ParseStyle, "Failed to parse GeoJSON data: %s", geoJSON.error().message.c_str()); + Log::Error(Event::ParseStyle, "Failed to parse GeoJSON data: %s", + geoJSON.error().message.c_str()); // Create an empty GeoJSON VT object to make sure we're not infinitely waiting for // tiles to load. - mapbox::geojson::feature_collection features; - urlOrGeoJSON = GeoJSON { std::make_unique<GeoJSONVT>(features) }; + setGeoJSON(GeoJSON{ FeatureCollection{} }); } else { - urlOrGeoJSON = std::move(*geoJSON); + setGeoJSON(*geoJSON); } loaded = true; @@ -100,13 +122,20 @@ void GeoJSONSource::Impl::load(FileSource& fileSource) { Range<uint8_t> GeoJSONSource::Impl::getZoomRange() { assert(loaded); - return { 0, urlOrGeoJSON.get<GeoJSON>().impl->options.maxZoom }; + return { 0, options.maxzoom }; } std::unique_ptr<Tile> GeoJSONSource::Impl::createTile(const OverscaledTileID& tileID, const UpdateParameters& parameters) { assert(loaded); - return std::make_unique<GeoJSONTile>(tileID, base.getID(), parameters, *urlOrGeoJSON.get<GeoJSON>().impl); + if (urlOrGeoJSON.is<GeoJSONVTPointer>()) { + return std::make_unique<GeoJSONTile>(tileID, base.getID(), parameters, + *urlOrGeoJSON.get<GeoJSONVTPointer>()); + } else { + assert(urlOrGeoJSON.is<SuperclusterPointer>()); + return std::make_unique<GeoJSONTile>(tileID, base.getID(), parameters, + *urlOrGeoJSON.get<SuperclusterPointer>()); + } } } // namespace style diff --git a/src/mbgl/style/sources/geojson_source_impl.hpp b/src/mbgl/style/sources/geojson_source_impl.hpp index e6e01c06e9..eb3563e85a 100644 --- a/src/mbgl/style/sources/geojson_source_impl.hpp +++ b/src/mbgl/style/sources/geojson_source_impl.hpp @@ -1,7 +1,7 @@ #pragma once -#include <mbgl/style/sources/geojson_source.hpp> #include <mbgl/style/source_impl.hpp> +#include <mbgl/style/sources/geojson_source.hpp> #include <mbgl/util/variant.hpp> namespace mbgl { @@ -12,11 +12,13 @@ namespace style { class GeoJSONSource::Impl : public Source::Impl { public: - Impl(std::string id, Source&); + Impl(std::string id, Source&, const GeoJSONOptions); ~Impl() final; void setURL(std::string); - void setGeoJSON(GeoJSON&&); + void setGeoJSON(const GeoJSON&); + + std::string getURL(); void load(FileSource&) final; @@ -24,16 +26,14 @@ public: return util::tileSize; } - const variant<std::string, GeoJSON>& getURLOrGeoJSON() const { - return urlOrGeoJSON; - } - private: Range<uint8_t> getZoomRange() final; std::unique_ptr<Tile> createTile(const OverscaledTileID&, const UpdateParameters&) final; - variant<std::string, GeoJSON> urlOrGeoJSON; + variant<std::string, GeoJSONVTPointer, SuperclusterPointer> urlOrGeoJSON; std::unique_ptr<AsyncRequest> req; + + GeoJSONOptions options; }; } // namespace style diff --git a/src/mbgl/tile/geojson_tile.cpp b/src/mbgl/tile/geojson_tile.cpp index 0dc4ac9107..ab596bd9ba 100644 --- a/src/mbgl/tile/geojson_tile.cpp +++ b/src/mbgl/tile/geojson_tile.cpp @@ -3,6 +3,7 @@ #include <mbgl/style/update_parameters.hpp> #include <mapbox/geojsonvt.hpp> +#include <supercluster.hpp> namespace mbgl { @@ -46,17 +47,16 @@ private: }; // Converts the geojsonvt::Tile to a a GeoJSONTile. They have a differing internal structure. -std::unique_ptr<GeoJSONTileData> convertTile(const mapbox::geojsonvt::Tile& tile) { +std::unique_ptr<GeoJSONTileData> convertTile(const mapbox::geometry::feature_collection<int16_t>& features) { std::shared_ptr<GeoJSONTileLayer> layer; - if (!tile.features.empty()) { - std::vector<std::shared_ptr<const GeoJSONTileFeature>> features; - GeometryCoordinates line; + if (!features.empty()) { + std::vector<std::shared_ptr<const GeoJSONTileFeature>> convertedFeatures; ToFeatureType toFeatureType; ToGeometryCollection toGeometryCollection; - for (auto& feature : tile.features) { + for (auto& feature : features) { const FeatureType featureType = apply_visitor(toFeatureType, feature.geometry); if (featureType == FeatureType::Unknown) { @@ -72,11 +72,11 @@ std::unique_ptr<GeoJSONTileData> convertTile(const mapbox::geojsonvt::Tile& tile PropertyMap properties = feature.properties; - features.emplace_back(std::make_shared<GeoJSONTileFeature>( + convertedFeatures.emplace_back(std::make_shared<GeoJSONTileFeature>( featureType, std::move(geometry), std::move(properties))); } - layer = std::make_unique<GeoJSONTileLayer>(std::move(features)); + layer = std::make_unique<GeoJSONTileLayer>(std::move(convertedFeatures)); } return std::make_unique<GeoJSONTileData>(layer); @@ -87,7 +87,15 @@ GeoJSONTile::GeoJSONTile(const OverscaledTileID& overscaledTileID, const style::UpdateParameters& parameters, mapbox::geojsonvt::GeoJSONVT& geojsonvt) : GeometryTile(overscaledTileID, sourceID_, parameters.style, parameters.mode) { - setData(convertTile(geojsonvt.getTile(id.canonical.z, id.canonical.x, id.canonical.y))); + setData(convertTile(geojsonvt.getTile(id.canonical.z, id.canonical.x, id.canonical.y).features)); +} + +GeoJSONTile::GeoJSONTile(const OverscaledTileID& overscaledTileID, + std::string sourceID_, + const style::UpdateParameters& parameters, + mapbox::supercluster::Supercluster& supercluster) + : GeometryTile(overscaledTileID, sourceID_, parameters.style, parameters.mode) { + setData(convertTile(supercluster.getTile(id.canonical.z, id.canonical.x, id.canonical.y))); } void GeoJSONTile::setNecessity(Necessity) {} diff --git a/src/mbgl/tile/geojson_tile.hpp b/src/mbgl/tile/geojson_tile.hpp index 09fdd1ec9b..422101b767 100644 --- a/src/mbgl/tile/geojson_tile.hpp +++ b/src/mbgl/tile/geojson_tile.hpp @@ -3,9 +3,15 @@ #include <mbgl/tile/geometry_tile.hpp> namespace mapbox { + namespace geojsonvt { class GeoJSONVT; } // namespace geojsonvt + +namespace supercluster { +class Supercluster; +} // namespace supercluster + } // namespace mapbox namespace mbgl { @@ -21,6 +27,11 @@ public: const style::UpdateParameters&, mapbox::geojsonvt::GeoJSONVT&); + GeoJSONTile(const OverscaledTileID&, + std::string sourceID, + const style::UpdateParameters&, + mapbox::supercluster::Supercluster&); + void setNecessity(Necessity) final; }; diff --git a/src/mbgl/util/geojson.cpp b/src/mbgl/util/geojson.cpp deleted file mode 100644 index a17d01f933..0000000000 --- a/src/mbgl/util/geojson.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include <mbgl/util/geojson.hpp> - -#include <mapbox/geojsonvt.hpp> - -namespace mbgl { - -GeoJSON::GeoJSON(std::unique_ptr<mapbox::geojsonvt::GeoJSONVT> impl_) : impl(std::move(impl_)) {} -GeoJSON::GeoJSON(GeoJSON&&) = default; -GeoJSON::~GeoJSON() = default; - -} // namespace mbgl diff --git a/test/test.gypi b/test/test.gypi index 304e0e9730..82bd02bd83 100644 --- a/test/test.gypi +++ b/test/test.gypi @@ -103,6 +103,8 @@ '<@(boost_cflags)', '<@(sqlite_cflags)', '<@(geojsonvt_cflags)', + '<@(supercluster_cflags)', + '<@(kdbush_cflags)', '<@(rapidjson_cflags)', '<@(pixelmatch_cflags)', '<@(earcut_cflags)', @@ -113,8 +115,7 @@ ], 'libraries': [ '<@(gtest_static_libs)', - '<@(sqlite_static_libs)', - '<@(geojsonvt_static_libs)', + '<@(sqlite_static_libs)' ], }, |