From c148a7a0616568b0ae82ab8dc89c1c096af10537 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Wed, 24 Jan 2018 08:34:05 -0800 Subject: [core] align implementations of local and asset file source --- platform/default/asset_file_source.cpp | 27 ++++++++++++++++++--------- platform/default/default_file_source.cpp | 12 +----------- platform/default/local_file_source.cpp | 17 +++++++++++------ src/mbgl/storage/asset_file_source.hpp | 2 ++ test/storage/asset_file_source.test.cpp | 26 ++++++++++++++++++++++++++ test/storage/local_file_source.test.cpp | 26 ++++++++++++++++++++++++++ 6 files changed, 84 insertions(+), 26 deletions(-) diff --git a/platform/default/asset_file_source.cpp b/platform/default/asset_file_source.cpp index 54dbb8d0f6..3063bf88a0 100644 --- a/platform/default/asset_file_source.cpp +++ b/platform/default/asset_file_source.cpp @@ -10,6 +10,12 @@ #include #include +namespace { + +const std::string assetProtocol = "asset://"; + +} // namespace + namespace mbgl { class AssetFileSource::Impl { @@ -19,18 +25,17 @@ public: } void request(const std::string& url, ActorRef req) { - std::string path; + Response response; - if (url.size() <= 8 || url[8] == '/') { - // This is an empty or absolute path. - path = mbgl::util::percentDecode(url.substr(8)); - } else { - // This is a relative path. Prefix with the application root. - path = root + "/" + mbgl::util::percentDecode(url.substr(8)); + if (!acceptsURL(url)) { + response.error = std::make_unique(Response::Error::Reason::Other, + "Invalid asset URL"); + req.invoke(&FileSourceRequest::setResponse, response); + return; } - Response response; - + // Cut off the protocol and prefix with path. + const auto path = root + "/" + mbgl::util::percentDecode(url.substr(assetProtocol.size())); struct stat buf; int result = stat(path.c_str(), &buf); @@ -69,4 +74,8 @@ std::unique_ptr AssetFileSource::request(const Resource& resource, return std::move(req); } +bool AssetFileSource::acceptsURL(const std::string& url) { + return std::equal(assetProtocol.begin(), assetProtocol.end(), url.begin()); +} + } // namespace mbgl diff --git a/platform/default/default_file_source.cpp b/platform/default/default_file_source.cpp index 608b782ab9..cb602995a4 100644 --- a/platform/default/default_file_source.cpp +++ b/platform/default/default_file_source.cpp @@ -14,16 +14,6 @@ #include -namespace { - -const std::string assetProtocol = "asset://"; - -bool isAssetURL(const std::string& url) { - return std::equal(assetProtocol.begin(), assetProtocol.end(), url.begin()); -} - -} // namespace - namespace mbgl { class DefaultFileSource::Impl { @@ -118,7 +108,7 @@ public: ref.invoke(&FileSourceRequest::setResponse, res); }; - if (isAssetURL(resource.url)) { + if (AssetFileSource::acceptsURL(resource.url)) { //Asset request tasks[req] = assetFileSource->request(resource, callback); } else if (LocalFileSource::acceptsURL(resource.url)) { diff --git a/platform/default/local_file_source.cpp b/platform/default/local_file_source.cpp index 21803d0935..0635e86d80 100644 --- a/platform/default/local_file_source.cpp +++ b/platform/default/local_file_source.cpp @@ -16,8 +16,7 @@ namespace { -const char* protocol = "file://"; -const std::size_t protocolLength = 7; +const std::string fileProtocol = "file://"; } // namespace @@ -28,11 +27,17 @@ public: Impl(ActorRef) {} void request(const std::string& url, ActorRef req) { - // Cut off the protocol - std::string path = mbgl::util::percentDecode(url.substr(protocolLength)); - Response response; + if (!acceptsURL(url)) { + response.error = std::make_unique(Response::Error::Reason::Other, + "Invalid file URL"); + req.invoke(&FileSourceRequest::setResponse, response); + return; + } + + // Cut off the protocol and prefix with path. + const auto path = mbgl::util::percentDecode(url.substr(fileProtocol.size())); struct stat buf; int result = stat(path.c_str(), &buf); @@ -70,7 +75,7 @@ std::unique_ptr LocalFileSource::request(const Resource& resource, } bool LocalFileSource::acceptsURL(const std::string& url) { - return url.compare(0, protocolLength, protocol) == 0; + return std::equal(fileProtocol.begin(), fileProtocol.end(), url.begin()); } } // namespace mbgl diff --git a/src/mbgl/storage/asset_file_source.hpp b/src/mbgl/storage/asset_file_source.hpp index 6ed7af8aaf..5d98b4e69e 100644 --- a/src/mbgl/storage/asset_file_source.hpp +++ b/src/mbgl/storage/asset_file_source.hpp @@ -15,6 +15,8 @@ public: std::unique_ptr request(const Resource&, Callback) override; + static bool acceptsURL(const std::string& url); + private: class Impl; diff --git a/test/storage/asset_file_source.test.cpp b/test/storage/asset_file_source.test.cpp index a39d2963d2..978a41a306 100644 --- a/test/storage/asset_file_source.test.cpp +++ b/test/storage/asset_file_source.test.cpp @@ -69,6 +69,15 @@ TEST(AssetFileSource, Load) { loop.run(); } +TEST(AssetFileSource, AcceptsURL) { + EXPECT_TRUE(AssetFileSource::acceptsURL("asset://empty")); + EXPECT_TRUE(AssetFileSource::acceptsURL("asset:///test")); + EXPECT_FALSE(AssetFileSource::acceptsURL("assds://foo")); + EXPECT_FALSE(AssetFileSource::acceptsURL("asset:")); + EXPECT_FALSE(AssetFileSource::acceptsURL("style.json")); + EXPECT_FALSE(AssetFileSource::acceptsURL("")); +} + TEST(AssetFileSource, EmptyFile) { util::RunLoop loop; @@ -118,6 +127,23 @@ TEST(AssetFileSource, NonExistentFile) { loop.run(); } +TEST(AssetFileSource, InvalidURL) { + util::RunLoop loop; + + AssetFileSource fs("test/fixtures/storage/assets"); + + std::unique_ptr req = fs.request({ Resource::Unknown, "test://wrong-scheme" }, [&](Response res) { + req.reset(); + ASSERT_NE(nullptr, res.error); + EXPECT_EQ(Response::Error::Reason::Other, res.error->reason); + EXPECT_EQ("Invalid asset URL", res.error->message); + ASSERT_FALSE(res.data.get()); + loop.stop(); + }); + + loop.run(); +} + TEST(AssetFileSource, ReadDirectory) { util::RunLoop loop; diff --git a/test/storage/local_file_source.test.cpp b/test/storage/local_file_source.test.cpp index 4d509e6c7d..e1756f8e7d 100644 --- a/test/storage/local_file_source.test.cpp +++ b/test/storage/local_file_source.test.cpp @@ -20,6 +20,15 @@ std::string toAbsoluteURL(const std::string& fileName) { using namespace mbgl; +TEST(LocalFileSource, AcceptsURL) { + EXPECT_TRUE(LocalFileSource::acceptsURL("file://empty")); + EXPECT_TRUE(LocalFileSource::acceptsURL("file:///test")); + EXPECT_FALSE(LocalFileSource::acceptsURL("flie://foo")); + EXPECT_FALSE(LocalFileSource::acceptsURL("file:")); + EXPECT_FALSE(LocalFileSource::acceptsURL("style.json")); + EXPECT_FALSE(LocalFileSource::acceptsURL("")); +} + TEST(LocalFileSource, EmptyFile) { util::RunLoop loop; @@ -69,6 +78,23 @@ TEST(LocalFileSource, NonExistentFile) { loop.run(); } +TEST(LocalFileSource, InvalidURL) { + util::RunLoop loop; + + LocalFileSource fs; + + std::unique_ptr req = fs.request({ Resource::Unknown, "test://wrong-scheme" }, [&](Response res) { + req.reset(); + ASSERT_NE(nullptr, res.error); + EXPECT_EQ(Response::Error::Reason::Other, res.error->reason); + EXPECT_EQ("Invalid file URL", res.error->message); + ASSERT_FALSE(res.data.get()); + loop.stop(); + }); + + loop.run(); +} + TEST(LocalFileSource, ReadDirectory) { util::RunLoop loop; -- cgit v1.2.1 From 191c0124c7f167a5da90acb958e088e1e3e46b08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Thu, 25 Jan 2018 15:24:29 -0800 Subject: [core] don't force downloading of Open Sans fonts When a SymbolLayer doesn't have a text-font defined, we automatically add Open Sans/Arial Unicode MS. However, when the SymbolLayer is only used for rendering icons, it doesn't have text-field defined either. In those cases, we still force downloading Open Sans/Arial Unicode MS during offline pack creation. If the user doesn't use this font, this change should save ~15MB and a few seconds in download time. --- src/mbgl/style/parser.cpp | 2 +- test/fixtures/style_parser/font_stacks.json | 3 +++ test/style/style_parser.test.cpp | 21 +++++++++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/mbgl/style/parser.cpp b/src/mbgl/style/parser.cpp index b177f2159c..8d14d7972c 100644 --- a/src/mbgl/style/parser.cpp +++ b/src/mbgl/style/parser.cpp @@ -277,7 +277,7 @@ std::vector Parser::fontStacks() const { std::set result; for (const auto& layer : layers) { - if (layer->is()) { + if (layer->is() && !layer->as()->getTextField().isUndefined()) { layer->as()->getTextFont().match( [&] (Undefined) { result.insert({"Open Sans Regular", "Arial Unicode MS Regular"}); diff --git a/test/fixtures/style_parser/font_stacks.json b/test/fixtures/style_parser/font_stacks.json index 07fe223d46..0ff3e936a8 100644 --- a/test/fixtures/style_parser/font_stacks.json +++ b/test/fixtures/style_parser/font_stacks.json @@ -12,6 +12,7 @@ "source": "source", "source-layer": "source-layer", "layout": { + "text-field": "a", "text-font": ["a"] } }, { @@ -20,6 +21,7 @@ "source": "source", "source-layer": "source-layer", "layout": { + "text-field": "a", "text-font": { "stops": [[0, ["a", "b"]]] } @@ -30,6 +32,7 @@ "source": "source", "source-layer": "source-layer", "layout": { + "text-field": "a", "text-font": { "stops": [[0, ["a", "b"]], [1, ["a", "b", "c"]]] } diff --git a/test/style/style_parser.test.cpp b/test/style/style_parser.test.cpp index 7f7c06f4c5..43b982c3b9 100644 --- a/test/style/style_parser.test.cpp +++ b/test/style/style_parser.test.cpp @@ -103,6 +103,23 @@ TEST(StyleParser, FontStacks) { ASSERT_EQ(FontStack({"a", "b", "c"}), result[2]); } +TEST(StyleParser, FontStacksNoTextField) { + style::Parser parser; + parser.parse(R"({ + "version": 8, + "layers": [{ + "id": "symbol", + "type": "symbol", + "source": "vector", + "layout": { + "text-font": ["a"] + } + }] + })"); + auto result = parser.fontStacks(); + ASSERT_EQ(0u, result.size()); +} + TEST(StyleParser, FontStacksCaseExpression) { style::Parser parser; parser.parse(R"({ @@ -112,6 +129,7 @@ TEST(StyleParser, FontStacksCaseExpression) { "type": "symbol", "source": "vector", "layout": { + "text-field": "a", "text-font": ["case", ["==", "a", ["string", ["get", "text-font"]]], ["literal", ["Arial"]], ["literal", ["Helvetica"]]] } }] @@ -131,6 +149,7 @@ TEST(StyleParser, FontStacksMatchExpression) { "type": "symbol", "source": "vector", "layout": { + "text-field": "a", "text-font": ["match", ["get", "text-font"], "a", ["literal", ["Arial"]], ["literal", ["Helvetica"]]] } }] @@ -150,6 +169,7 @@ TEST(StyleParser, FontStacksStepExpression) { "type": "symbol", "source": "vector", "layout": { + "text-field": "a", "text-font": ["array", "string", ["step", ["get", "text-font"], ["literal", ["Arial"]], 0, ["literal", ["Helvetica"]]]] } }] @@ -170,6 +190,7 @@ TEST(StyleParser, FontStacksGetExpression) { "type": "symbol", "source": "vector", "layout": { + "text-field": "a", "text-font": ["array", "string", ["get", "text-font"]] } }] -- cgit v1.2.1 From a005fb737545c77d3bd7cf65a7b69023e0db1b21 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Fri, 26 Jan 2018 11:43:03 +0100 Subject: [android] - update changelog for v6.0.0-beta.1 --- platform/android/CHANGELOG.md | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index 95cf3a21ff..fa212b86ad 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -2,9 +2,28 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started. -## master +## 6.0.0-beta.1 - January 26, 2018 + - Binding integration for expressions [#10654](https://github.com/mapbox/mapbox-gl-native/pull/10654) + - CustomGeometrySource [#9983](https://github.com/mapbox/mapbox-gl-native/pull/9983) + - HillshadeLayer and RasterDemSource [#11031](https://github.com/mapbox/mapbox-gl-native/pull/11031) + - Revisit marker placement for snapshot [#11029](https://github.com/mapbox/mapbox-gl-native/pull/11029) + - SafeVarargs annotation for expressions [#11027](https://github.com/mapbox/mapbox-gl-native/pull/11027) + - Expression#toString [#11024](https://github.com/mapbox/mapbox-gl-native/pull/11024) + - Rename initRenderSurface to onSurfaceCreated [#11023](https://github.com/mapbox/mapbox-gl-native/pull/11023) + - Expose attribution manager as public API [#10942](https://github.com/mapbox/mapbox-gl-native/pull/10942) + - Replace Mapzen vector source example with Mapillary [#10931](https://github.com/mapbox/mapbox-gl-native/pull/10931) - Add Hebrew localization [#10967](https://github.com/mapbox/mapbox-gl-native/pull/10967) - + - Cleanup gradle configuration files [#10903](https://github.com/mapbox/mapbox-gl-native/pull/10903) + - Send double tap event only once [#10855](https://github.com/mapbox/mapbox-gl-native/pull/10855) + - Parameter validation for LatLngBounds#from [#10831](https://github.com/mapbox/mapbox-gl-native/pull/10831) + - Replace JSON parsing [#10815](https://github.com/mapbox/mapbox-gl-native/pull/10815) + - Orientation change regression test [#10814](https://github.com/mapbox/mapbox-gl-native/pull/10814) + - Max & min LatLng constants [#10780](https://github.com/mapbox/mapbox-gl-native/pull/10780) + - LatLng#wrap return new instance of LatLng [#10769](https://github.com/mapbox/mapbox-gl-native/pull/10769) + - Custom library loader [#10733](https://github.com/mapbox/mapbox-gl-native/pull/10733) + - Inconsistent parameters for LatLngBounds.union [#10728](https://github.com/mapbox/mapbox-gl-native/pull/10728) + - Gradle 4.1 / AS 3.0 [#10549](https://github.com/mapbox/mapbox-gl-native/pull/10549) + ## 5.3.2 - January 22, 2018 - Validate surface creation before destroying [#10890](https://github.com/mapbox/mapbox-gl-native/pull/10890) - Add filesource activation ot OfflineRegion [#10904](https://github.com/mapbox/mapbox-gl-native/pull/10904) -- cgit v1.2.1 From 082df681c866466b8a69bf27cbb42b01eed7f5e1 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Fri, 26 Jan 2018 11:59:09 +0100 Subject: [android] - bump snapshot version on master to 7.0.0-SNAPSHOT --- platform/android/MapboxGLAndroidSDK/gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/android/MapboxGLAndroidSDK/gradle.properties b/platform/android/MapboxGLAndroidSDK/gradle.properties index f00eb1d77f..3796173fd9 100644 --- a/platform/android/MapboxGLAndroidSDK/gradle.properties +++ b/platform/android/MapboxGLAndroidSDK/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.mapbox.mapboxsdk -VERSION_NAME=6.0.0-SNAPSHOT +VERSION_NAME=7.0.0-SNAPSHOT POM_DESCRIPTION=Mapbox GL Android SDK POM_URL=https://github.com/mapbox/mapbox-gl-native -- cgit v1.2.1 From 77f93996d53b88765f1430c8a28fb834e99f388a Mon Sep 17 00:00:00 2001 From: Tobrun Date: Fri, 26 Jan 2018 18:15:27 +0100 Subject: [windows] - run AppVeyor for all branches --- appveyor.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 8013c669f6..d31160261f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -7,10 +7,6 @@ environment: shallow_clone: true -branches: - only: - - master - cache: - '%APPVEYOR_BUILD_FOLDER%\mason_packages' - '%APPVEYOR_BUILD_FOLDER%\LLVM-5.0.1-win64.exe' -- cgit v1.2.1 From 7694c9c879ae1f9817166576a7c3b484da875ba6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Paczos?= Date: Mon, 29 Jan 2018 15:36:38 -0500 Subject: [android] - log lack of permissions only if enabling location --- .../src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java index 6eacbbaeaf..81fd091c90 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java @@ -374,7 +374,7 @@ public final class TrackingSettings { } private void setMyLocationEnabled(boolean locationEnabled, boolean isCustomLocationSource) { - if (!PermissionsManager.areLocationPermissionsGranted(myLocationView.getContext())) { + if (locationEnabled && !PermissionsManager.areLocationPermissionsGranted(myLocationView.getContext())) { Timber.e("Could not activate user location tracking: " + "user did not accept the permission or permissions were not requested."); return; -- cgit v1.2.1 From e2a4dffcb627498561d66a304ec668f8242b8342 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Thu, 25 Jan 2018 14:04:13 +0100 Subject: [android] - blacklist adreno 2xx for VAO support --- src/mbgl/gl/context.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/mbgl/gl/context.cpp b/src/mbgl/gl/context.cpp index 7444ac112c..b6244d58c7 100644 --- a/src/mbgl/gl/context.cpp +++ b/src/mbgl/gl/context.cpp @@ -258,9 +258,10 @@ UniqueTexture Context::createTexture() { bool Context::supportsVertexArrays() const { static bool blacklisted = []() { - // Blacklist Adreno 3xx as it crashes on glBuffer(Sub)Data + // Blacklist Adreno 2xx, 3xx as it crashes on glBuffer(Sub)Data const std::string renderer = reinterpret_cast(glGetString(GL_RENDERER)); - return renderer.find("Adreno (TM) 3") != std::string::npos; + return renderer.find("Adreno (TM) 2") != std::string::npos + || renderer.find("Adreno (TM) 3") != std::string::npos; }(); return !blacklisted && -- cgit v1.2.1 From d0309a3137e5dd5b52d761d9ff675390f5417235 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Fri, 2 Feb 2018 10:30:05 +0100 Subject: [android] - don't invoke onLowMemory on map when the map isn't fully created yet --- .../src/main/java/com/mapbox/mapboxsdk/maps/MapView.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java index 2e7d4c4270..a54a84f83b 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java @@ -486,7 +486,9 @@ public class MapView extends FrameLayout { */ @UiThread public void onLowMemory() { - nativeMapView.onLowMemory(); + if (nativeMapView != null) { + nativeMapView.onLowMemory(); + } } /** -- cgit v1.2.1 From 528729dbec6b3219699994f627eb200c28c25be7 Mon Sep 17 00:00:00 2001 From: Martin Hellwig Date: Thu, 25 Jan 2018 17:04:53 +0100 Subject: Implement new bearingTrackingMode GPS_NORTH_FACING --- .../mapboxsdk/constants/MyBearingTracking.java | 7 ++++++- .../mapboxsdk/maps/widgets/MyLocationView.java | 23 +++++++++++++++++----- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java index ceac862f39..c042b00577 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java @@ -22,7 +22,7 @@ import java.lang.annotation.RetentionPolicy; */ public class MyBearingTracking { - @IntDef( {NONE, COMPASS, GPS}) + @IntDef( {NONE, COMPASS, GPS, GPS_NORTH_FACING}) @Retention(RetentionPolicy.SOURCE) public @interface Mode { } @@ -42,4 +42,9 @@ public class MyBearingTracking { */ public static final int GPS = 0x00000008; + /** + * Tracking the bearing of the user based on GPS data, but camera always faces north direction + */ + public static final int GPS_NORTH_FACING = 0x0000000B; + } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java index 4f27e0ada8..1cdd91028d 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java @@ -356,7 +356,9 @@ public class MyLocationView extends View { foregroundDrawable.draw(canvas); } } else if (foregroundBearingDrawable != null && foregroundBounds != null) { - if (myBearingTrackingMode == MyBearingTracking.GPS || compassListener.isSensorAvailable()) { + if (myBearingTrackingMode == MyBearingTracking.GPS + || myBearingTrackingMode == MyBearingTracking.GPS_NORTH_FACING + || compassListener.isSensorAvailable()) { foregroundBearingDrawable.draw(canvas); } else { // We are tracking MyBearingTracking.COMPASS, but sensor is not available. @@ -383,7 +385,8 @@ public class MyLocationView extends View { public void setBearing(double bearing) { this.bearing = bearing; if (myLocationTrackingMode == MyLocationTracking.TRACKING_NONE) { - if (myBearingTrackingMode == MyBearingTracking.GPS) { + if (myBearingTrackingMode == MyBearingTracking.GPS + || myBearingTrackingMode == MyBearingTracking.GPS_NORTH_FACING) { if (location != null) { setCompass(location.getBearing() - bearing); } @@ -519,7 +522,8 @@ public class MyLocationView extends View { } private void toggleGps(boolean enableGps) { - toggleGps(enableGps, mapboxMap != null && mapboxMap.getTrackingSettings().isCustomLocationSource()); + toggleGps(enableGps, mapboxMap != null + && mapboxMap.getTrackingSettings().isCustomLocationSource()); } /** @@ -580,7 +584,8 @@ public class MyLocationView extends View { this.location = location; myLocationBehavior.updateLatLng(location); - if (mapboxMap != null && myBearingTrackingMode == MyBearingTracking.GPS + if (mapboxMap != null && (myBearingTrackingMode == MyBearingTracking.GPS + || myBearingTrackingMode == MyBearingTracking.GPS_NORTH_FACING) && myLocationTrackingMode == MyLocationTracking.TRACKING_NONE) { setBearing(mapboxMap.getCameraPosition().bearing); } @@ -616,7 +621,8 @@ public class MyLocationView extends View { compassListener.onResume(); } else { compassListener.onPause(); - if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) { + if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW + && myBearingTrackingMode == MyBearingTracking.GPS) { // always face north setCompass(0); } else { @@ -1017,6 +1023,13 @@ public class MyLocationView extends View { setCompass(0, COMPASS_UPDATE_RATE_MS); } + if (myBearingTrackingMode == MyBearingTracking.GPS_NORTH_FACING) { + builder.bearing(0); + if (location.hasBearing()) { + setCompass(location.getBearing(), COMPASS_UPDATE_RATE_MS); + } + } + // accuracy updateAccuracy(location); -- cgit v1.2.1 From 9fbf5f4e748b5d0b70cbc50e5194df282a5f9801 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Wed, 31 Jan 2018 14:30:25 +0100 Subject: [android] - blacklist flaky instrumentation tests --- circle.yml | 15 ++------------- platform/android/scripts/exclude-activity-gen.json | 7 ++++++- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/circle.yml b/circle.yml index c7e1e519c7..122d8f3b9a 100644 --- a/circle.yml +++ b/circle.yml @@ -339,21 +339,10 @@ jobs: shell: /bin/bash -euo pipefail command: | gcloud firebase test android models list - (gcloud firebase test android run --type instrumentation \ + gcloud firebase test android run --type instrumentation \ --app platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/debug/MapboxGLAndroidSDKTestApp-debug.apk \ --test platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/androidTest/debug/MapboxGLAndroidSDKTestApp-debug-androidTest.apk \ - --device-ids sailfish --os-version-ids 26 --locales en --orientations portrait --timeout 20m \ - 2>&1 | tee firebase.log) || EXIT_CODE=$? - - FIREBASE_TEST_BUCKET=$(sed -n 's|^.*\[https://console.developers.google.com/storage/browser/\([^]]*\).*|gs://\1|p' firebase.log) - echo "Downloading from: ${FIREBASE_TEST_BUCKET}" - gsutil -m cp -n -R -Z "$FIREBASE_TEST_BUCKET*" platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/debug - - echo "Try running ndk-stack on downloaded logcat to symbolicate the stacktraces:" - find platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/debug -type f -name "logcat" -print0 | \ - xargs -0 -I '{}' ${ANDROID_NDK_HOME}/ndk-stack -sym build/android-arm-v7/Debug -dump {} - - exit ${EXIT_CODE:-0} + --device-ids sailfish --os-version-ids 26 --locales en --orientations portrait --timeout 20m - store_artifacts: path: platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/debug destination: . diff --git a/platform/android/scripts/exclude-activity-gen.json b/platform/android/scripts/exclude-activity-gen.json index f05001c6ae..c1a6b5bb48 100644 --- a/platform/android/scripts/exclude-activity-gen.json +++ b/platform/android/scripts/exclude-activity-gen.json @@ -27,5 +27,10 @@ "QueryRenderedFeaturesBoxHighlightActivity", "MultiMapActivity", "MapInDialogActivity", - "SimpleMapActivity" + "SimpleMapActivity", + "ManualZoomActivity", + "MaxMinZoomActivity", + "ScrollByActivity", + "ZoomFunctionSymbolLayerActivity", + "SymbolGeneratorActivity" ] \ No newline at end of file -- cgit v1.2.1 From 23b413483dd29c640545a28b965d51018d50b403 Mon Sep 17 00:00:00 2001 From: Erol Baykal Date: Sat, 6 Jan 2018 00:40:45 +0100 Subject: [Android] fix typo in custom layer example --- platform/android/src/example_custom_layer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/android/src/example_custom_layer.cpp b/platform/android/src/example_custom_layer.cpp index 1ed68d0835..6d0bd4de4b 100644 --- a/platform/android/src/example_custom_layer.cpp +++ b/platform/android/src/example_custom_layer.cpp @@ -96,7 +96,7 @@ void nativeContextLost(void */*context*/) { mbgl::Log::Info(mbgl::Event::General, "nativeContextLost"); } -void nativeDenitialize(void *context) { +void nativeDeinitialize(void *context) { mbgl::Log::Info(mbgl::Event::General, "nativeDeinitialize"); delete reinterpret_cast(context); } @@ -132,7 +132,7 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) { env->SetStaticLongField(customLayerClass, env->GetStaticFieldID(customLayerClass, "DeinitializeFunction", "J"), - reinterpret_cast(nativeDenitialize)); + reinterpret_cast(nativeDeinitialize)); return JNI_VERSION_1_6; } -- cgit v1.2.1 From 6a7aa6de277ef226169f005c834b9f2b8ee55602 Mon Sep 17 00:00:00 2001 From: olheimer Date: Tue, 30 Jan 2018 16:11:09 +0100 Subject: Wrong Typo for BUILDTYPE --- platform/android/DISTRIBUTE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/android/DISTRIBUTE.md b/platform/android/DISTRIBUTE.md index 648f26ce25..1c61748322 100644 --- a/platform/android/DISTRIBUTE.md +++ b/platform/android/DISTRIBUTE.md @@ -13,7 +13,7 @@ BUILDTYPE=Debug or BUILDTYPE=Release ##### Creating an Android Archive file that supports all ABIs ```sh -BUILDTYPE=RELEASE make apackage +BUILDTYPE=Release make apackage ``` This will build native libraries to support following ABIs: -- cgit v1.2.1 From 90f29a91729b536da9f090d389549db1ec4b8e9f Mon Sep 17 00:00:00 2001 From: Tobrun Date: Wed, 31 Jan 2018 13:18:49 +0100 Subject: [android] - update changelog for 5.4.0 --- platform/android/CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index fa212b86ad..ed8667f42d 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -2,6 +2,12 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started. +## 5.4.0 - January 30, 2018 + - Blacklist Adreno 2xx GPU for VAO support [#11047](https://github.com/mapbox/mapbox-gl-native/pull/11047) + - Bearing tracking mode GPS_NORTH_FACING [#11095](https://github.com/mapbox/mapbox-gl-native/pull/11095) + - Disable logging for missing location permissions when location is disabled [#11084](https://github.com/mapbox/mapbox-gl-native/pull/11084) + - Create offline handler using the main thread looper [#11021](https://github.com/mapbox/mapbox-gl-native/pull/11021) + ## 6.0.0-beta.1 - January 26, 2018 - Binding integration for expressions [#10654](https://github.com/mapbox/mapbox-gl-native/pull/10654) - CustomGeometrySource [#9983](https://github.com/mapbox/mapbox-gl-native/pull/9983) -- cgit v1.2.1 From cecfa1834658e4ed65ecf8656f0d32bbe678ad9d Mon Sep 17 00:00:00 2001 From: Martin Hellwig Date: Thu, 25 Jan 2018 17:04:53 +0100 Subject: Implement new bearingTrackingMode GPS_NORTH_FACING --- .../mapboxsdk/constants/MyBearingTracking.java | 7 ++++++- .../mapboxsdk/maps/widgets/MyLocationView.java | 23 +++++++++++++++++----- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java index ceac862f39..c042b00577 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java @@ -22,7 +22,7 @@ import java.lang.annotation.RetentionPolicy; */ public class MyBearingTracking { - @IntDef( {NONE, COMPASS, GPS}) + @IntDef( {NONE, COMPASS, GPS, GPS_NORTH_FACING}) @Retention(RetentionPolicy.SOURCE) public @interface Mode { } @@ -42,4 +42,9 @@ public class MyBearingTracking { */ public static final int GPS = 0x00000008; + /** + * Tracking the bearing of the user based on GPS data, but camera always faces north direction + */ + public static final int GPS_NORTH_FACING = 0x0000000B; + } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java index 4f27e0ada8..1cdd91028d 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java @@ -356,7 +356,9 @@ public class MyLocationView extends View { foregroundDrawable.draw(canvas); } } else if (foregroundBearingDrawable != null && foregroundBounds != null) { - if (myBearingTrackingMode == MyBearingTracking.GPS || compassListener.isSensorAvailable()) { + if (myBearingTrackingMode == MyBearingTracking.GPS + || myBearingTrackingMode == MyBearingTracking.GPS_NORTH_FACING + || compassListener.isSensorAvailable()) { foregroundBearingDrawable.draw(canvas); } else { // We are tracking MyBearingTracking.COMPASS, but sensor is not available. @@ -383,7 +385,8 @@ public class MyLocationView extends View { public void setBearing(double bearing) { this.bearing = bearing; if (myLocationTrackingMode == MyLocationTracking.TRACKING_NONE) { - if (myBearingTrackingMode == MyBearingTracking.GPS) { + if (myBearingTrackingMode == MyBearingTracking.GPS + || myBearingTrackingMode == MyBearingTracking.GPS_NORTH_FACING) { if (location != null) { setCompass(location.getBearing() - bearing); } @@ -519,7 +522,8 @@ public class MyLocationView extends View { } private void toggleGps(boolean enableGps) { - toggleGps(enableGps, mapboxMap != null && mapboxMap.getTrackingSettings().isCustomLocationSource()); + toggleGps(enableGps, mapboxMap != null + && mapboxMap.getTrackingSettings().isCustomLocationSource()); } /** @@ -580,7 +584,8 @@ public class MyLocationView extends View { this.location = location; myLocationBehavior.updateLatLng(location); - if (mapboxMap != null && myBearingTrackingMode == MyBearingTracking.GPS + if (mapboxMap != null && (myBearingTrackingMode == MyBearingTracking.GPS + || myBearingTrackingMode == MyBearingTracking.GPS_NORTH_FACING) && myLocationTrackingMode == MyLocationTracking.TRACKING_NONE) { setBearing(mapboxMap.getCameraPosition().bearing); } @@ -616,7 +621,8 @@ public class MyLocationView extends View { compassListener.onResume(); } else { compassListener.onPause(); - if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) { + if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW + && myBearingTrackingMode == MyBearingTracking.GPS) { // always face north setCompass(0); } else { @@ -1017,6 +1023,13 @@ public class MyLocationView extends View { setCompass(0, COMPASS_UPDATE_RATE_MS); } + if (myBearingTrackingMode == MyBearingTracking.GPS_NORTH_FACING) { + builder.bearing(0); + if (location.hasBearing()) { + setCompass(location.getBearing(), COMPASS_UPDATE_RATE_MS); + } + } + // accuracy updateAccuracy(location); -- cgit v1.2.1 From c636b382e8bd61a05dd976d9aeb0ce5ae0894b0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Paczos?= Date: Mon, 29 Jan 2018 15:36:38 -0500 Subject: [android] - log lack of permissions only if enabling location --- .../src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java index 6eacbbaeaf..81fd091c90 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java @@ -374,7 +374,7 @@ public final class TrackingSettings { } private void setMyLocationEnabled(boolean locationEnabled, boolean isCustomLocationSource) { - if (!PermissionsManager.areLocationPermissionsGranted(myLocationView.getContext())) { + if (locationEnabled && !PermissionsManager.areLocationPermissionsGranted(myLocationView.getContext())) { Timber.e("Could not activate user location tracking: " + "user did not accept the permission or permissions were not requested."); return; -- cgit v1.2.1 From c8a96bb24ff23513b67c258e60f5820ee3134802 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Thu, 25 Jan 2018 14:04:13 +0100 Subject: [android] - blacklist adreno 2xx for VAO support --- src/mbgl/gl/context.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/mbgl/gl/context.cpp b/src/mbgl/gl/context.cpp index d8ade8b8b8..d1a37a861a 100644 --- a/src/mbgl/gl/context.cpp +++ b/src/mbgl/gl/context.cpp @@ -249,9 +249,10 @@ UniqueTexture Context::createTexture() { bool Context::supportsVertexArrays() const { static bool blacklisted = []() { - // Blacklist Adreno 3xx as it crashes on glBuffer(Sub)Data + // Blacklist Adreno 2xx, 3xx as it crashes on glBuffer(Sub)Data const std::string renderer = reinterpret_cast(glGetString(GL_RENDERER)); - return renderer.find("Adreno (TM) 3") != std::string::npos; + return renderer.find("Adreno (TM) 2") != std::string::npos + || renderer.find("Adreno (TM) 3") != std::string::npos; }(); return !blacklisted && -- cgit v1.2.1 From ff29a0cb99d5e8aedc66207080d7e83a7e2dc0dd Mon Sep 17 00:00:00 2001 From: Tobrun Date: Wed, 24 Jan 2018 11:28:47 +0100 Subject: [android] - create Handler using the main thread looper --- .../src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java index c033b1cbcb..987756876b 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java @@ -1,6 +1,7 @@ package com.mapbox.mapboxsdk.offline; import android.os.Handler; +import android.os.Looper; import android.support.annotation.IntDef; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -51,7 +52,7 @@ public class OfflineRegion { private byte[] metadata; // Makes sure callbacks come back to the main thread - private final Handler handler = new Handler(); + private final Handler handler = new Handler(Looper.getMainLooper()); /** * A region can have a single observer, which gets notified whenever a change -- cgit v1.2.1 From 0742f7fb66f832ffae4d96fa430a691e53a0c32d Mon Sep 17 00:00:00 2001 From: Tobrun Date: Wed, 31 Jan 2018 13:18:49 +0100 Subject: [android] - update changelog for 5.4.0 --- platform/android/CHANGELOG.md | 17 ++++++++++++++++- platform/android/MapboxGLAndroidSDK/gradle.properties | 2 +- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index e98ed491aa..1e51725ed0 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -2,7 +2,22 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started. -## master +## 5.4.0 - January 30, 2018 + - Blacklist Adreno 2xx GPU for VAO support [#11047](https://github.com/mapbox/mapbox-gl-native/pull/11047) + - Bearing tracking mode GPS_NORTH_FACING [#11095](https://github.com/mapbox/mapbox-gl-native/pull/11095) + - Disable logging missing location permissons [#11084](https://github.com/mapbox/mapbox-gl-native/pull/11084) + - Create offline handler using the main thread looper [#11021](https://github.com/mapbox/mapbox-gl-native/pull/11021) + +## 6.0.0-beta.1 - January 26, 2018 + - Binding integration for expressions [#10654](https://github.com/mapbox/mapbox-gl-native/pull/10654) + - CustomGeometrySource [#9983](https://github.com/mapbox/mapbox-gl-native/pull/9983) + - HillshadeLayer and RasterDemSource [#11031](https://github.com/mapbox/mapbox-gl-native/pull/11031) + - Revisit marker placement for snapshot [#11029](https://github.com/mapbox/mapbox-gl-native/pull/11029) + - SafeVarargs annotation for expressions [#11027](https://github.com/mapbox/mapbox-gl-native/pull/11027) + - Expression#toString [#11024](https://github.com/mapbox/mapbox-gl-native/pull/11024) + - Rename initRenderSurface to onSurfaceCreated [#11023](https://github.com/mapbox/mapbox-gl-native/pull/11023) + - Expose attribution manager as public API [#10942](https://github.com/mapbox/mapbox-gl-native/pull/10942) + - Replace Mapzen vector source example with Mapillary [#10931](https://github.com/mapbox/mapbox-gl-native/pull/10931) - Add Hebrew localization [#10967](https://github.com/mapbox/mapbox-gl-native/pull/10967) ## 5.3.2 - January 22, 2018 diff --git a/platform/android/MapboxGLAndroidSDK/gradle.properties b/platform/android/MapboxGLAndroidSDK/gradle.properties index 20d6f60baf..011052b521 100644 --- a/platform/android/MapboxGLAndroidSDK/gradle.properties +++ b/platform/android/MapboxGLAndroidSDK/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.mapbox.mapboxsdk -VERSION_NAME=5.3.3-SNAPSHOT +VERSION_NAME=5.4.1-SNAPSHOT POM_DESCRIPTION=Mapbox GL Android SDK POM_URL=https://github.com/mapbox/mapbox-gl-native -- cgit v1.2.1 From 332dad2a4e002b1856ea072d6af22c9e501a55fd Mon Sep 17 00:00:00 2001 From: Randall C Lee Date: Mon, 5 Feb 2018 16:17:57 -0500 Subject: [ios] Add Radius Configuration (#11070) * Add Radius Configuration * Remove default config - removes default configuration - moves config setup to events init method - renames config class * Delete MGLConfig header file --- platform/ios/ios.xcodeproj/project.pbxproj | 12 ++++++++++ platform/ios/src/MGLLocationManager.m | 6 ++--- platform/ios/src/MGLMapboxEvents.m | 3 +++ platform/ios/src/MGLTelemetryConfig.h | 18 +++++++++++++++ platform/ios/src/MGLTelemetryConfig.m | 35 ++++++++++++++++++++++++++++++ 5 files changed, 71 insertions(+), 3 deletions(-) create mode 100644 platform/ios/src/MGLTelemetryConfig.h create mode 100644 platform/ios/src/MGLTelemetryConfig.m diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index 52f33a83b6..2325f3d3ce 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -247,6 +247,10 @@ 968F36B51E4D128D003A5522 /* MGLDistanceFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = 3557F7AE1E1D27D300CCA5E6 /* MGLDistanceFormatter.h */; settings = {ATTRIBUTES = (Public, ); }; }; 96E027231E57C76E004B8E66 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 96E027251E57C76E004B8E66 /* Localizable.strings */; }; 96F3F73C1F57124B003E2D2C /* MGLUserLocationHeadingIndicator.h in Headers */ = {isa = PBXBuildFile; fileRef = 96F3F73B1F5711F1003E2D2C /* MGLUserLocationHeadingIndicator.h */; }; + AC518DFF201BB55A00EBC820 /* MGLTelemetryConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = AC518DFD201BB55A00EBC820 /* MGLTelemetryConfig.h */; }; + AC518E00201BB55A00EBC820 /* MGLTelemetryConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = AC518DFD201BB55A00EBC820 /* MGLTelemetryConfig.h */; }; + AC518E03201BB56000EBC820 /* MGLTelemetryConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = AC518DFE201BB55A00EBC820 /* MGLTelemetryConfig.m */; }; + AC518E04201BB56100EBC820 /* MGLTelemetryConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = AC518DFE201BB55A00EBC820 /* MGLTelemetryConfig.m */; }; DA00FC8E1D5EEB0D009AABC8 /* MGLAttributionInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = DA00FC8C1D5EEB0D009AABC8 /* MGLAttributionInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; DA00FC8F1D5EEB0D009AABC8 /* MGLAttributionInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = DA00FC8C1D5EEB0D009AABC8 /* MGLAttributionInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; DA00FC901D5EEB0D009AABC8 /* MGLAttributionInfo.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA00FC8D1D5EEB0D009AABC8 /* MGLAttributionInfo.mm */; }; @@ -750,6 +754,8 @@ 96E0272D1E57C7E6004B8E66 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Localizable.strings; sourceTree = ""; }; 96E0272E1E57C7E7004B8E66 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Localizable.strings"; sourceTree = ""; }; 96F3F73B1F5711F1003E2D2C /* MGLUserLocationHeadingIndicator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLUserLocationHeadingIndicator.h; sourceTree = ""; }; + AC518DFD201BB55A00EBC820 /* MGLTelemetryConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLTelemetryConfig.h; sourceTree = ""; }; + AC518DFE201BB55A00EBC820 /* MGLTelemetryConfig.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLTelemetryConfig.m; sourceTree = ""; }; DA00FC8C1D5EEB0D009AABC8 /* MGLAttributionInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAttributionInfo.h; sourceTree = ""; }; DA00FC8D1D5EEB0D009AABC8 /* MGLAttributionInfo.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLAttributionInfo.mm; sourceTree = ""; }; DA0CD58F1CF56F6A00A5F5A5 /* MGLFeatureTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLFeatureTests.mm; path = ../../darwin/test/MGLFeatureTests.mm; sourceTree = ""; }; @@ -1724,6 +1730,8 @@ DA8848491CBAFB9800AB86E3 /* MGLMapboxEvents.m */, 9620BB361E69FE1700705A1D /* MGLSDKUpdateChecker.h */, 9620BB371E69FE1700705A1D /* MGLSDKUpdateChecker.mm */, + AC518DFD201BB55A00EBC820 /* MGLTelemetryConfig.h */, + AC518DFE201BB55A00EBC820 /* MGLTelemetryConfig.m */, ); name = Telemetry; sourceTree = ""; @@ -1840,6 +1848,7 @@ DA8847F51CBAFA5100AB86E3 /* MGLOfflineRegion.h in Headers */, DA737EE11D056A4E005BDA16 /* MGLMapViewDelegate.h in Headers */, DA8848851CBB033F00AB86E3 /* FABKitProtocol.h in Headers */, + AC518DFF201BB55A00EBC820 /* MGLTelemetryConfig.h in Headers */, DA88481B1CBAFA6200AB86E3 /* MGLGeometry_Private.h in Headers */, 3510FFF91D6DCC4700F413B2 /* NSCompoundPredicate+MGLAdditions.h in Headers */, 3557F7B01E1D27D300CCA5E6 /* MGLDistanceFormatter.h in Headers */, @@ -1906,6 +1915,7 @@ 7E016D7F1D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.h in Headers */, DABFB8701CBE9A0F00D62B32 /* MGLMapView+IBAdditions.h in Headers */, 353AFA151D65AB17005A69F4 /* NSDate+MGLAdditions.h in Headers */, + AC518E00201BB55A00EBC820 /* MGLTelemetryConfig.h in Headers */, 3510FFFA1D6DCC4700F413B2 /* NSCompoundPredicate+MGLAdditions.h in Headers */, DA72620C1DEEE3480043BB89 /* MGLOpenGLStyleLayer.h in Headers */, 35CE61831D4165D9004F2359 /* UIColor+MGLAdditions.h in Headers */, @@ -2399,6 +2409,7 @@ DA72620D1DEEE3480043BB89 /* MGLOpenGLStyleLayer.mm in Sources */, DA88481A1CBAFA6200AB86E3 /* MGLAccountManager.m in Sources */, 3510FFFB1D6DCC4700F413B2 /* NSCompoundPredicate+MGLAdditions.mm in Sources */, + AC518E03201BB56000EBC820 /* MGLTelemetryConfig.m in Sources */, DA8848271CBAFA6200AB86E3 /* MGLPolyline.mm in Sources */, DA8848581CBAFB9800AB86E3 /* MGLMapboxEvents.m in Sources */, 35CE61841D4165D9004F2359 /* UIColor+MGLAdditions.mm in Sources */, @@ -2488,6 +2499,7 @@ DA72620E1DEEE3480043BB89 /* MGLOpenGLStyleLayer.mm in Sources */, DAA4E42F1CBB730400178DFB /* MGLCompactCalloutView.m in Sources */, 3510FFFC1D6DCC4700F413B2 /* NSCompoundPredicate+MGLAdditions.mm in Sources */, + AC518E04201BB56100EBC820 /* MGLTelemetryConfig.m in Sources */, DAA4E4271CBB730400178DFB /* MGLTilePyramidOfflineRegion.mm in Sources */, DAA4E41C1CBB730400178DFB /* MGLAccountManager.m in Sources */, 35CE61851D4165D9004F2359 /* UIColor+MGLAdditions.mm in Sources */, diff --git a/platform/ios/src/MGLLocationManager.m b/platform/ios/src/MGLLocationManager.m index b0d2e17d5d..85ef4ca489 100644 --- a/platform/ios/src/MGLLocationManager.m +++ b/platform/ios/src/MGLLocationManager.m @@ -1,9 +1,9 @@ #import "MGLLocationManager.h" +#import "MGLTelemetryConfig.h" #import static const NSTimeInterval MGLLocationManagerHibernationTimeout = 300.0; static const NSTimeInterval MGLLocationManagerHibernationPollInterval = 5.0; -static const CLLocationDistance MGLLocationManagerHibernationRadius = 300.0; static const CLLocationDistance MGLLocationManagerDistanceFilter = 5.0; static NSString * const MGLLocationManagerRegionIdentifier = @"MGLLocationManagerRegionIdentifier.fence.center"; @@ -122,7 +122,7 @@ static NSString * const MGLLocationManagerRegionIdentifier = @"MGLLocationManage } - (void)establishRegionMonitoringForLocation:(CLLocation *)location { - CLCircularRegion *region = [[CLCircularRegion alloc] initWithCenter:location.coordinate radius:MGLLocationManagerHibernationRadius identifier:MGLLocationManagerRegionIdentifier]; + CLCircularRegion *region = [[CLCircularRegion alloc] initWithCenter:location.coordinate radius:MGLTelemetryConfig.sharedConfig.MGLLocationManagerHibernationRadius identifier:MGLLocationManagerRegionIdentifier]; region.notifyOnEntry = NO; region.notifyOnExit = YES; [self.standardLocationManager startMonitoringForRegion:region]; @@ -151,7 +151,7 @@ static NSString * const MGLLocationManagerRegionIdentifier = @"MGLLocationManage if (location.speed > 0.0) { [self startBackgroundTimeoutTimer]; } - if (self.standardLocationManager.monitoredRegions.count == 0 || location.horizontalAccuracy < MGLLocationManagerHibernationRadius) { + if (self.standardLocationManager.monitoredRegions.count == 0 || location.horizontalAccuracy < MGLTelemetryConfig.sharedConfig.MGLLocationManagerHibernationRadius) { [self establishRegionMonitoringForLocation:location]; } if ([self.delegate respondsToSelector:@selector(locationManager:didUpdateLocations:)]) { diff --git a/platform/ios/src/MGLMapboxEvents.m b/platform/ios/src/MGLMapboxEvents.m index d59972f5bf..273af5b3bc 100644 --- a/platform/ios/src/MGLMapboxEvents.m +++ b/platform/ios/src/MGLMapboxEvents.m @@ -6,6 +6,7 @@ #import "NSException+MGLAdditions.h" #import "MGLAPIClient.h" #import "MGLLocationManager.h" +#import "MGLTelemetryConfig.h" #include #include @@ -172,6 +173,8 @@ const NSTimeInterval MGLFlushInterval = 180; - (instancetype) init { self = [super init]; if (self) { + [MGLTelemetryConfig.sharedConfig configurationFromKey:[[NSUserDefaults standardUserDefaults] objectForKey:MGLMapboxMetricsProfile]]; + _currentAccountTypeValue = @0; _currentMetricsEnabledValue = YES; diff --git a/platform/ios/src/MGLTelemetryConfig.h b/platform/ios/src/MGLTelemetryConfig.h new file mode 100644 index 0000000000..527d344291 --- /dev/null +++ b/platform/ios/src/MGLTelemetryConfig.h @@ -0,0 +1,18 @@ +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface MGLTelemetryConfig : NSObject + +@property (nonatomic) CLLocationDistance MGLLocationManagerHibernationRadius; + +extern NSString *const MGLMapboxMetricsProfile; + ++ (nullable instancetype)sharedConfig; + +- (void)configurationFromKey:(NSString *)key; + +@end + +NS_ASSUME_NONNULL_END diff --git a/platform/ios/src/MGLTelemetryConfig.m b/platform/ios/src/MGLTelemetryConfig.m new file mode 100644 index 0000000000..c45554414a --- /dev/null +++ b/platform/ios/src/MGLTelemetryConfig.m @@ -0,0 +1,35 @@ +#import "MGLTelemetryConfig.h" + +static const CLLocationDistance MGLConfigHibernationRadiusDefault = 300.0; +static const CLLocationDistance MGLConfigHibernationRadiusWide = 1200.0; + +NSString *const MGLMapboxMetricsProfile = @"MGLMapboxMetricsProfile"; + +static NSString *const MGLConfigHibernationRadiusWideKey = @"WideGeoFence"; + +@implementation MGLTelemetryConfig + +- (instancetype) init { + self = [super init]; + if (self) { + _MGLLocationManagerHibernationRadius = MGLConfigHibernationRadiusDefault; + } + return self; +} + ++ (nullable instancetype)sharedConfig { + static dispatch_once_t onceToken; + static MGLTelemetryConfig *_sharedConfig; + dispatch_once(&onceToken, ^{ + _sharedConfig = [[self alloc] init]; + }); + return _sharedConfig; +} + +- (void)configurationFromKey:(NSString *)key { + if ([key isEqualToString:MGLConfigHibernationRadiusWideKey]) { + _MGLLocationManagerHibernationRadius = MGLConfigHibernationRadiusWide; + } +} + +@end -- cgit v1.2.1 From 671dfefc1e3fff5038a6d9cedc78daabffa5597f Mon Sep 17 00:00:00 2001 From: Asheem Mamoowala Date: Mon, 5 Feb 2018 19:31:03 -0500 Subject: Reset tileset-based render sources when any tileset properties changed. (#11042) --- src/mbgl/renderer/sources/render_raster_dem_source.cpp | 15 ++++++++------- src/mbgl/renderer/sources/render_raster_dem_source.hpp | 2 +- src/mbgl/renderer/sources/render_raster_source.cpp | 15 ++++++++------- src/mbgl/renderer/sources/render_raster_source.hpp | 2 +- src/mbgl/renderer/sources/render_vector_source.cpp | 15 ++++++++------- src/mbgl/renderer/sources/render_vector_source.hpp | 2 +- 6 files changed, 27 insertions(+), 24 deletions(-) diff --git a/src/mbgl/renderer/sources/render_raster_dem_source.cpp b/src/mbgl/renderer/sources/render_raster_dem_source.cpp index 76716518d7..4de949c9f2 100644 --- a/src/mbgl/renderer/sources/render_raster_dem_source.cpp +++ b/src/mbgl/renderer/sources/render_raster_dem_source.cpp @@ -32,14 +32,10 @@ void RenderRasterDEMSource::update(Immutable baseImpl_, enabled = needsRendering; - optional tileset = impl().getTileset(); + optional _tileset = impl().getTileset(); - if (!tileset) { - return; - } - - if (tileURLTemplates != tileset->tiles) { - tileURLTemplates = tileset->tiles; + if (tileset != _tileset) { + tileset = _tileset; // TODO: this removes existing buckets, and will cause flickering. // Should instead refresh tile data in place. @@ -47,6 +43,11 @@ void RenderRasterDEMSource::update(Immutable baseImpl_, tilePyramid.renderTiles.clear(); tilePyramid.cache.clear(); } + // Allow clearing the tile pyramid first, before the early return in case + // the new tileset is not yet available or has an error in loading + if (!_tileset) { + return; + } tilePyramid.update(layers, needsRendering, diff --git a/src/mbgl/renderer/sources/render_raster_dem_source.hpp b/src/mbgl/renderer/sources/render_raster_dem_source.hpp index b6b8bf977c..87f9bcb563 100644 --- a/src/mbgl/renderer/sources/render_raster_dem_source.hpp +++ b/src/mbgl/renderer/sources/render_raster_dem_source.hpp @@ -40,7 +40,7 @@ private: const style::RasterSource::Impl& impl() const; TilePyramid tilePyramid; - optional> tileURLTemplates; + optional tileset; protected: void onTileChanged(Tile&) final; diff --git a/src/mbgl/renderer/sources/render_raster_source.cpp b/src/mbgl/renderer/sources/render_raster_source.cpp index e99cd040e9..5d32818663 100644 --- a/src/mbgl/renderer/sources/render_raster_source.cpp +++ b/src/mbgl/renderer/sources/render_raster_source.cpp @@ -29,14 +29,10 @@ void RenderRasterSource::update(Immutable baseImpl_, enabled = needsRendering; - optional tileset = impl().getTileset(); + optional _tileset = impl().getTileset(); - if (!tileset) { - return; - } - - if (tileURLTemplates != tileset->tiles) { - tileURLTemplates = tileset->tiles; + if (tileset != _tileset) { + tileset = _tileset; // TODO: this removes existing buckets, and will cause flickering. // Should instead refresh tile data in place. @@ -44,6 +40,11 @@ void RenderRasterSource::update(Immutable baseImpl_, tilePyramid.renderTiles.clear(); tilePyramid.cache.clear(); } + // Allow clearing the tile pyramid first, before the early return in case + // the new tileset is not yet available or has an error in loading + if (!_tileset) { + return; + } tilePyramid.update(layers, needsRendering, diff --git a/src/mbgl/renderer/sources/render_raster_source.hpp b/src/mbgl/renderer/sources/render_raster_source.hpp index 25041fde43..bc6ac1bd9f 100644 --- a/src/mbgl/renderer/sources/render_raster_source.hpp +++ b/src/mbgl/renderer/sources/render_raster_source.hpp @@ -40,7 +40,7 @@ private: const style::RasterSource::Impl& impl() const; TilePyramid tilePyramid; - optional> tileURLTemplates; + optional tileset; }; template <> diff --git a/src/mbgl/renderer/sources/render_vector_source.cpp b/src/mbgl/renderer/sources/render_vector_source.cpp index d53023e4d0..c01257aea9 100644 --- a/src/mbgl/renderer/sources/render_vector_source.cpp +++ b/src/mbgl/renderer/sources/render_vector_source.cpp @@ -32,14 +32,10 @@ void RenderVectorSource::update(Immutable baseImpl_, enabled = needsRendering; - optional tileset = impl().getTileset(); + optional _tileset = impl().getTileset(); - if (!tileset) { - return; - } - - if (tileURLTemplates != tileset->tiles) { - tileURLTemplates = tileset->tiles; + if (tileset != _tileset) { + tileset = _tileset; // TODO: this removes existing buckets, and will cause flickering. // Should instead refresh tile data in place. @@ -47,6 +43,11 @@ void RenderVectorSource::update(Immutable baseImpl_, tilePyramid.renderTiles.clear(); tilePyramid.cache.clear(); } + // Allow clearing the tile pyramid first, before the early return in case + // the new tileset is not yet available or has an error in loading + if (!_tileset) { + return; + } tilePyramid.update(layers, needsRendering, diff --git a/src/mbgl/renderer/sources/render_vector_source.hpp b/src/mbgl/renderer/sources/render_vector_source.hpp index 4a992e854f..57fe6dbb6c 100644 --- a/src/mbgl/renderer/sources/render_vector_source.hpp +++ b/src/mbgl/renderer/sources/render_vector_source.hpp @@ -40,7 +40,7 @@ private: const style::VectorSource::Impl& impl() const; TilePyramid tilePyramid; - optional> tileURLTemplates; + optional tileset; }; template <> -- cgit v1.2.1 From 14db958ae51d3f7cf8fa802a72dfe6fdba7f8864 Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Mon, 5 Feb 2018 15:09:58 -0800 Subject: [core] Account for overscaling in debug collision circles. Fixes issue #11116. Port of GL JS issue $6041. --- mapbox-gl-js | 2 +- src/mbgl/programs/collision_box_program.hpp | 1 + src/mbgl/programs/uniforms.hpp | 1 + src/mbgl/renderer/layers/render_symbol_layer.cpp | 3 ++- src/mbgl/shaders/collision_circle.cpp | 8 ++++++-- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/mapbox-gl-js b/mapbox-gl-js index de365184e1..063fdebeaf 160000 --- a/mapbox-gl-js +++ b/mapbox-gl-js @@ -1 +1 @@ -Subproject commit de365184e13c08fb42bbd93a08abfc8598294994 +Subproject commit 063fdebeaffbf6bc3ffff32233ed6248f42f3c59 diff --git a/src/mbgl/programs/collision_box_program.hpp b/src/mbgl/programs/collision_box_program.hpp index 8d712a3df3..6e75adf36e 100644 --- a/src/mbgl/programs/collision_box_program.hpp +++ b/src/mbgl/programs/collision_box_program.hpp @@ -110,6 +110,7 @@ class CollisionCircleProgram : public Program< gl::Uniforms< uniforms::u_matrix, uniforms::u_extrude_scale, + uniforms::u_overscale_factor, uniforms::u_camera_to_center_distance>, style::Properties<>> { diff --git a/src/mbgl/programs/uniforms.hpp b/src/mbgl/programs/uniforms.hpp index 184f42e504..107c084918 100644 --- a/src/mbgl/programs/uniforms.hpp +++ b/src/mbgl/programs/uniforms.hpp @@ -55,6 +55,7 @@ MBGL_DEFINE_UNIFORM_SCALAR(gl::TextureUnit, u_fadetexture); MBGL_DEFINE_UNIFORM_SCALAR(float, u_scale_a); MBGL_DEFINE_UNIFORM_SCALAR(float, u_scale_b); MBGL_DEFINE_UNIFORM_SCALAR(float, u_tile_units_to_pixels); +MBGL_DEFINE_UNIFORM_SCALAR(float, u_overscale_factor); } // namespace uniforms } // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_symbol_layer.cpp b/src/mbgl/renderer/layers/render_symbol_layer.cpp index 04fcb2c3ab..9e493003c0 100644 --- a/src/mbgl/renderer/layers/render_symbol_layer.cpp +++ b/src/mbgl/renderer/layers/render_symbol_layer.cpp @@ -268,9 +268,10 @@ void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) { gl::DepthMode::disabled(), gl::StencilMode::disabled(), parameters.colorModeForRenderPass(), - CollisionBoxProgram::UniformValues { + CollisionCircleProgram::UniformValues { uniforms::u_matrix::Value{ tile.matrix }, uniforms::u_extrude_scale::Value{ extrudeScale }, + uniforms::u_overscale_factor::Value{ float(tile.tile.id.overscaleFactor()) }, uniforms::u_camera_to_center_distance::Value{ parameters.state.getCameraToCenterDistance() } }, *bucket.collisionCircle.vertexBuffer, diff --git a/src/mbgl/shaders/collision_circle.cpp b/src/mbgl/shaders/collision_circle.cpp index f220586245..82ebbf05a0 100644 --- a/src/mbgl/shaders/collision_circle.cpp +++ b/src/mbgl/shaders/collision_circle.cpp @@ -26,7 +26,10 @@ varying vec2 v_extrude_scale; void main() { vec4 projectedPoint = u_matrix * vec4(a_anchor_pos, 0, 1); highp float camera_to_anchor_distance = projectedPoint.w; - highp float collision_perspective_ratio = 0.5 + 0.5 * (u_camera_to_center_distance / camera_to_anchor_distance); + highp float collision_perspective_ratio = clamp( + 0.5 + 0.5 * (u_camera_to_center_distance / camera_to_anchor_distance), + 0.0, // Prevents oversized near-field circles in pitched/overzoomed tiles + 4.0); gl_Position = u_matrix * vec4(a_pos, 0.0, 1.0); @@ -43,6 +46,7 @@ void main() { )MBGL_SHADER"; const char* collision_circle::fragmentSource = R"MBGL_SHADER( +uniform float u_overscale_factor; varying float v_placed; varying float v_notUsed; @@ -68,7 +72,7 @@ void main() { float extrude_scale_length = length(v_extrude_scale); float extrude_length = length(v_extrude) * extrude_scale_length; - float stroke_width = 15.0 * extrude_scale_length; + float stroke_width = 15.0 * extrude_scale_length / u_overscale_factor; float radius = v_radius * extrude_scale_length; float distance_to_edge = abs(extrude_length - radius); -- cgit v1.2.1 From 2a05a832b25dcafec361e0eeeea05d68a109b1f0 Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Mon, 5 Feb 2018 16:25:09 -0800 Subject: [test] Temporarily ignore hillshade tests pending PR #11119. --- platform/node/test/ignores.json | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/platform/node/test/ignores.json b/platform/node/test/ignores.json index ab187a86b3..91e80631a8 100644 --- a/platform/node/test/ignores.json +++ b/platform/node/test/ignores.json @@ -63,6 +63,15 @@ "render-tests/text-pitch-scaling/line-half": "https://github.com/mapbox/mapbox-gl-native/issues/9732", "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", + "render-tests/hillshade-accent-color/literal": "skip - https://github.com/mapbox/mapbox-gl-native/pull/10642", + "render-tests/hillshade-accent-color/zoom-function": "skip - https://github.com/mapbox/mapbox-gl-native/pull/10642", + "render-tests/hillshade-highlight-color/default": "skip - https://github.com/mapbox/mapbox-gl-native/pull/10642", + "render-tests/hillshade-highlight-color/literal": "skip - https://github.com/mapbox/mapbox-gl-native/pull/10642", + "render-tests/hillshade-highlight-color/zoom-function": "skip - https://github.com/mapbox/mapbox-gl-native/pull/10642", + "render-tests/hillshade-shadow-color/default": "skip - https://github.com/mapbox/mapbox-gl-native/pull/10642", + "render-tests/hillshade-shadow-color/literal": "skip - https://github.com/mapbox/mapbox-gl-native/pull/10642", + "render-tests/hillshade-shadow-color/zoom-function": "skip - https://github.com/mapbox/mapbox-gl-native/pull/10642", "render-tests/combinations/background-opaque--heatmap-translucent": "https://github.com/mapbox/mapbox-gl-native/issues/10146", "render-tests/combinations/background-translucent--heatmap-translucent": "https://github.com/mapbox/mapbox-gl-native/issues/10146", "render-tests/combinations/circle-translucent--heatmap-translucent": "https://github.com/mapbox/mapbox-gl-native/issues/10146", @@ -84,6 +93,26 @@ "render-tests/combinations/line-translucent--heatmap-translucent": "https://github.com/mapbox/mapbox-gl-native/issues/10146", "render-tests/combinations/raster-translucent--heatmap-translucent": "https://github.com/mapbox/mapbox-gl-native/issues/10146", "render-tests/combinations/symbol-translucent--heatmap-translucent": "https://github.com/mapbox/mapbox-gl-native/issues/10146", + "render-tests/combinations/background-opaque--hillshade-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642", + "render-tests/combinations/background-translucent--hillshade-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642", + "render-tests/combinations/circle-translucent--hillshade-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642", + "render-tests/combinations/fill-extrusion-translucent--hillshade-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642", + "render-tests/combinations/fill-opaque--hillshade-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642", + "render-tests/combinations/fill-translucent--hillshade-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642", + "render-tests/combinations/hillshade-translucent--background-opaque": "https://github.com/mapbox/mapbox-gl-native/pull/10642", + "render-tests/combinations/hillshade-translucent--background-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642", + "render-tests/combinations/hillshade-translucent--circle-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642", + "render-tests/combinations/hillshade-translucent--fill-extrusion-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642", + "render-tests/combinations/hillshade-translucent--fill-opaque": "https://github.com/mapbox/mapbox-gl-native/pull/10642", + "render-tests/combinations/hillshade-translucent--fill-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642", + "render-tests/combinations/hillshade-translucent--heatmap-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642", + "render-tests/combinations/hillshade-translucent--hillshade-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642", + "render-tests/combinations/hillshade-translucent--line-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642", + "render-tests/combinations/hillshade-translucent--raster-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642", + "render-tests/combinations/hillshade-translucent--symbol-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642", + "render-tests/combinations/line-translucent--hillshade-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642", + "render-tests/combinations/raster-translucent--hillshade-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642", + "render-tests/combinations/symbol-translucent--hillshade-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642", "render-tests/combinations/background-opaque--fill-extrusion-translucent": "needs investigation", "render-tests/combinations/background-translucent--fill-extrusion-translucent": "needs investigation", "render-tests/combinations/circle-translucent--fill-extrusion-translucent": "needs investigation", -- cgit v1.2.1 From f7cae4c3e49282417d87a98a64bc94b12993855d Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Mon, 5 Feb 2018 17:19:13 -0800 Subject: [test] Ignore slightly-different collision circles on native. --- platform/node/test/ignores.json | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/node/test/ignores.json b/platform/node/test/ignores.json index 91e80631a8..fbf3880fae 100644 --- a/platform/node/test/ignores.json +++ b/platform/node/test/ignores.json @@ -9,6 +9,7 @@ "render-tests/background-color/transition": "https://github.com/mapbox/mapbox-gl-native/issues/10619", "render-tests/debug/collision": "https://github.com/mapbox/mapbox-gl-native/issues/3841", "render-tests/debug/collision-lines": "https://github.com/mapbox/mapbox-gl-native/issues/10412", + "render-tests/debug/collision-lines-overscaled": "https://github.com/mapbox/mapbox-gl-native/issues/10412", "render-tests/debug/collision-lines-pitched": "https://github.com/mapbox/mapbox-gl-native/issues/10412", "render-tests/debug/collision-pitched-wrapped": "https://github.com/mapbox/mapbox-gl-native/issues/10412", "render-tests/debug/tile": "https://github.com/mapbox/mapbox-gl-native/issues/3841", -- cgit v1.2.1 From d3deb6d71bdf837c3d82078630dbd11710a03d32 Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Wed, 31 Jan 2018 13:09:00 +0000 Subject: [core] Don't crash on line labels with 0 glyphs. Fixes issue #10956. --- src/mbgl/layout/symbol_projection.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/mbgl/layout/symbol_projection.cpp b/src/mbgl/layout/symbol_projection.cpp index ee6385c93c..9e077e2532 100644 --- a/src/mbgl/layout/symbol_projection.cpp +++ b/src/mbgl/layout/symbol_projection.cpp @@ -318,7 +318,7 @@ namespace mbgl { placedGlyphs.push_back(*placedGlyph); } placedGlyphs.push_back(firstAndLastGlyph->second); - } else { + } else if (symbol.glyphOffsets.size() == 1) { // Only a single glyph to place // So, determine whether to flip based on projected angle of the line segment it's on if (keepUpright && !flip) { @@ -337,7 +337,6 @@ namespace mbgl { return *orientationChange; } } - assert(symbol.glyphOffsets.size() == 1); // We are relying on SymbolInstance.hasText filtering out symbols without any glyphs at all const float glyphOffsetX = symbol.glyphOffsets.front(); optional singleGlyph = placeGlyphAlongLine(fontScale * glyphOffsetX, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment, symbol.line, symbol.tileDistances, labelPlaneMatrix, false); @@ -347,6 +346,8 @@ namespace mbgl { placedGlyphs.push_back(*singleGlyph); } + // The number of placedGlyphs must equal the number of glyphOffsets, which must correspond to the number of glyph vertices + // There may be 0 glyphs here, if a label consists entirely of glyphs that have 0x0 dimensions for (auto& placedGlyph : placedGlyphs) { addDynamicAttributes(placedGlyph.point, placedGlyph.angle, dynamicVertexArray); } -- cgit v1.2.1 From 3ef7dab1a8c59560ef04fb4bca34637e352f9a47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Tue, 10 Oct 2017 15:35:00 +0200 Subject: [core] use the RunLoop's schedule call directly RunLoop already has a queue, and has the ability to schedule weak mailboxes, so we can remove the duplicate code. --- include/mbgl/util/thread.hpp | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/include/mbgl/util/thread.hpp b/include/mbgl/util/thread.hpp index 672eebf6db..e3bd18143d 100644 --- a/include/mbgl/util/thread.hpp +++ b/include/mbgl/util/thread.hpp @@ -128,26 +128,9 @@ private: MBGL_STORE_THREAD(tid); void schedule(std::weak_ptr mailbox) override { - { - std::lock_guard lock(mutex); - queue.push(mailbox); - } - - loop->invoke([this] { receive(); }); - } - - void receive() { - std::unique_lock lock(mutex); - - auto mailbox = queue.front(); - queue.pop(); - lock.unlock(); - - Mailbox::maybeReceive(mailbox); + loop->schedule(mailbox); } - std::mutex mutex; - std::queue> queue; std::thread thread; std::unique_ptr> object; -- cgit v1.2.1 From 78f8fd88b434099a6bc16b19d59e20b851e168ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Tue, 23 Jan 2018 14:30:01 -0800 Subject: [core] factor out RunLoop::wake() --- include/mbgl/util/run_loop.hpp | 12 ++++++++---- platform/android/src/run_loop.cpp | 7 ++----- platform/darwin/src/run_loop.cpp | 7 ++----- platform/default/run_loop.cpp | 7 ++----- platform/qt/src/run_loop.cpp | 7 ++----- 5 files changed, 16 insertions(+), 24 deletions(-) diff --git a/include/mbgl/util/run_loop.hpp b/include/mbgl/util/run_loop.hpp index acbea80273..ecb18c857b 100644 --- a/include/mbgl/util/run_loop.hpp +++ b/include/mbgl/util/run_loop.hpp @@ -76,16 +76,20 @@ private: using Queue = std::queue>; - void push(std::shared_ptr); + // Wakes up the RunLoop so that it starts processing items in the queue. + void wake(); - void withMutex(std::function&& fn) { + // Adds a WorkTask to the queue, and wakes it up. + void push(std::shared_ptr task) { std::lock_guard lock(mutex); - fn(); + queue.push(std::move(task)); + wake(); } void process() { + std::unique_lock lock(mutex); Queue queue_; - withMutex([&] { queue_.swap(queue); }); + lock.unlock(); while (!queue_.empty()) { (*(queue_.front()))(); diff --git a/platform/android/src/run_loop.cpp b/platform/android/src/run_loop.cpp index 1d284a9e72..34366d836a 100644 --- a/platform/android/src/run_loop.cpp +++ b/platform/android/src/run_loop.cpp @@ -216,11 +216,8 @@ LOOP_HANDLE RunLoop::getLoopHandle() { return Get()->impl.get(); } -void RunLoop::push(std::shared_ptr task) { - withMutex([&] { - queue.push(std::move(task)); - impl->wake(); - }); +void RunLoop::wake() { + impl->wake(); } void RunLoop::run() { diff --git a/platform/darwin/src/run_loop.cpp b/platform/darwin/src/run_loop.cpp index d60a88cf52..0778b004e5 100644 --- a/platform/darwin/src/run_loop.cpp +++ b/platform/darwin/src/run_loop.cpp @@ -29,11 +29,8 @@ RunLoop::~RunLoop() { Scheduler::SetCurrent(nullptr); } -void RunLoop::push(std::shared_ptr task) { - withMutex([&] { - queue.push(std::move(task)); - impl->async->send(); - }); +void RunLoop::wake() { + impl->async->send(); } void RunLoop::run() { diff --git a/platform/default/run_loop.cpp b/platform/default/run_loop.cpp index 5bccd21d56..868ee72114 100644 --- a/platform/default/run_loop.cpp +++ b/platform/default/run_loop.cpp @@ -129,11 +129,8 @@ LOOP_HANDLE RunLoop::getLoopHandle() { return Get()->impl->loop; } -void RunLoop::push(std::shared_ptr task) { - withMutex([&] { - queue.push(std::move(task)); - impl->async->send(); - }); +void RunLoop::wake() { + impl->async->send(); } void RunLoop::run() { diff --git a/platform/qt/src/run_loop.cpp b/platform/qt/src/run_loop.cpp index af0c50ebb9..c25243c8e7 100644 --- a/platform/qt/src/run_loop.cpp +++ b/platform/qt/src/run_loop.cpp @@ -52,11 +52,8 @@ LOOP_HANDLE RunLoop::getLoopHandle() { return nullptr; } -void RunLoop::push(std::shared_ptr task) { - withMutex([&] { - queue.push(std::move(task)); - impl->async->send(); - }); +void RunLoop::wake() { + impl->async->send(); } void RunLoop::run() { -- cgit v1.2.1 From 93c4a23636841ad81eaf49fcfab28f25d0ec868b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Tue, 23 Jan 2018 14:43:37 -0800 Subject: [core] change RunLoop processing to check queue on every iteration --- include/mbgl/util/run_loop.hpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/include/mbgl/util/run_loop.hpp b/include/mbgl/util/run_loop.hpp index ecb18c857b..7167652687 100644 --- a/include/mbgl/util/run_loop.hpp +++ b/include/mbgl/util/run_loop.hpp @@ -88,12 +88,13 @@ private: void process() { std::unique_lock lock(mutex); - Queue queue_; - lock.unlock(); - - while (!queue_.empty()) { - (*(queue_.front()))(); - queue_.pop(); + std::shared_ptr task; + while (!queue.empty()) { + task = std::move(queue.front()); + queue.pop(); + lock.unlock(); + (*task)(); + lock.lock(); } } -- cgit v1.2.1 From 26a00f6a3139a95ec098269a219d0765447b5853 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Tue, 23 Jan 2018 16:59:59 -0800 Subject: [core] prioritize Thread::pause() calls --- include/mbgl/util/run_loop.hpp | 43 ++++++++++++++++++++++++++++++++---------- include/mbgl/util/thread.hpp | 2 +- test/util/run_loop.test.cpp | 14 ++++++++++++++ 3 files changed, 48 insertions(+), 11 deletions(-) diff --git a/include/mbgl/util/run_loop.hpp b/include/mbgl/util/run_loop.hpp index 7167652687..381e3ae213 100644 --- a/include/mbgl/util/run_loop.hpp +++ b/include/mbgl/util/run_loop.hpp @@ -26,6 +26,11 @@ public: New, }; + enum class Priority : bool { + Default = false, + High = true, + }; + enum class Event : uint8_t { None = 0, Read = 1, @@ -47,11 +52,16 @@ public: void addWatch(int fd, Event, std::function&& callback); void removeWatch(int fd); + // Invoke fn(args...) on this RunLoop. + template + void invoke(Priority priority, Fn&& fn, Args&&... args) { + push(priority, WorkTask::make(std::forward(fn), std::forward(args)...)); + } + // Invoke fn(args...) on this RunLoop. template void invoke(Fn&& fn, Args&&... args) { - std::shared_ptr task = WorkTask::make(std::forward(fn), std::forward(args)...); - push(task); + invoke(Priority::Default, std::forward(fn), std::forward(args)...); } // Post the cancellable work fn(args...) to this RunLoop. @@ -59,7 +69,7 @@ public: std::unique_ptr invokeCancellable(Fn&& fn, Args&&... args) { std::shared_ptr task = WorkTask::make(std::forward(fn), std::forward(args)...); - push(task); + push(Priority::Default, task); return std::make_unique(task); } @@ -80,25 +90,38 @@ private: void wake(); // Adds a WorkTask to the queue, and wakes it up. - void push(std::shared_ptr task) { + void push(Priority priority, std::shared_ptr task) { std::lock_guard lock(mutex); - queue.push(std::move(task)); + if (priority == Priority::High) { + highPriorityQueue.emplace(std::move(task)); + } else { + defaultQueue.emplace(std::move(task)); + } wake(); } void process() { - std::unique_lock lock(mutex); std::shared_ptr task; - while (!queue.empty()) { - task = std::move(queue.front()); - queue.pop(); + std::unique_lock lock(mutex); + while (true) { + if (!highPriorityQueue.empty()) { + task = std::move(highPriorityQueue.front()); + highPriorityQueue.pop(); + } else if (!defaultQueue.empty()) { + task = std::move(defaultQueue.front()); + defaultQueue.pop(); + } else { + break; + } lock.unlock(); (*task)(); + task.reset(); lock.lock(); } } - Queue queue; + Queue defaultQueue; + Queue highPriorityQueue; std::mutex mutex; std::unique_ptr impl; diff --git a/include/mbgl/util/thread.hpp b/include/mbgl/util/thread.hpp index e3bd18143d..74e722b02d 100644 --- a/include/mbgl/util/thread.hpp +++ b/include/mbgl/util/thread.hpp @@ -103,7 +103,7 @@ public: auto pausing = paused->get_future(); - loop->invoke([this] { + loop->invoke(RunLoop::Priority::High, [this] { auto resuming = resumed->get_future(); paused->set_value(); resuming.get(); diff --git a/test/util/run_loop.test.cpp b/test/util/run_loop.test.cpp index 57bc613f9e..4d2c704421 100644 --- a/test/util/run_loop.test.cpp +++ b/test/util/run_loop.test.cpp @@ -50,3 +50,17 @@ TEST(RunLoop, MultipleRun) { EXPECT_TRUE(secondTimeout); } + +TEST(RunLoop, Priorities) { + std::vector order; + + RunLoop loop(RunLoop::Type::New); + loop.invoke([&] { order.push_back(1); }); + loop.invoke(RunLoop::Priority::High, [&] { order.push_back(2); }); + loop.invoke([&] { order.push_back(3); }); + loop.invoke(RunLoop::Priority::High, [&] { order.push_back(4); }); + loop.invoke(RunLoop::Priority::Default, [&] { loop.stop(); }); + loop.run(); + + EXPECT_EQ((std::vector{ 2, 4, 1, 3 }), order); +} -- cgit v1.2.1 From e03f7a4d1f067cd01eb3022811eddb0b1892801b Mon Sep 17 00:00:00 2001 From: Molly Lloyd Date: Tue, 6 Feb 2018 10:56:42 -0800 Subject: [core] align raster-dem tiles to pixel grid (#11119) * align raster-dem tiles to pixel grid * revert ignored hillshade tests --- platform/node/test/ignores.json | 25 ---------------------- .../renderer/layers/render_hillshade_layer.cpp | 4 ++-- 2 files changed, 2 insertions(+), 27 deletions(-) diff --git a/platform/node/test/ignores.json b/platform/node/test/ignores.json index fbf3880fae..4d6f26b056 100644 --- a/platform/node/test/ignores.json +++ b/platform/node/test/ignores.json @@ -64,15 +64,6 @@ "render-tests/text-pitch-scaling/line-half": "https://github.com/mapbox/mapbox-gl-native/issues/9732", "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", - "render-tests/hillshade-accent-color/literal": "skip - https://github.com/mapbox/mapbox-gl-native/pull/10642", - "render-tests/hillshade-accent-color/zoom-function": "skip - https://github.com/mapbox/mapbox-gl-native/pull/10642", - "render-tests/hillshade-highlight-color/default": "skip - https://github.com/mapbox/mapbox-gl-native/pull/10642", - "render-tests/hillshade-highlight-color/literal": "skip - https://github.com/mapbox/mapbox-gl-native/pull/10642", - "render-tests/hillshade-highlight-color/zoom-function": "skip - https://github.com/mapbox/mapbox-gl-native/pull/10642", - "render-tests/hillshade-shadow-color/default": "skip - https://github.com/mapbox/mapbox-gl-native/pull/10642", - "render-tests/hillshade-shadow-color/literal": "skip - https://github.com/mapbox/mapbox-gl-native/pull/10642", - "render-tests/hillshade-shadow-color/zoom-function": "skip - https://github.com/mapbox/mapbox-gl-native/pull/10642", "render-tests/combinations/background-opaque--heatmap-translucent": "https://github.com/mapbox/mapbox-gl-native/issues/10146", "render-tests/combinations/background-translucent--heatmap-translucent": "https://github.com/mapbox/mapbox-gl-native/issues/10146", "render-tests/combinations/circle-translucent--heatmap-translucent": "https://github.com/mapbox/mapbox-gl-native/issues/10146", @@ -98,22 +89,6 @@ "render-tests/combinations/background-translucent--hillshade-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642", "render-tests/combinations/circle-translucent--hillshade-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642", "render-tests/combinations/fill-extrusion-translucent--hillshade-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642", - "render-tests/combinations/fill-opaque--hillshade-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642", - "render-tests/combinations/fill-translucent--hillshade-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642", - "render-tests/combinations/hillshade-translucent--background-opaque": "https://github.com/mapbox/mapbox-gl-native/pull/10642", - "render-tests/combinations/hillshade-translucent--background-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642", - "render-tests/combinations/hillshade-translucent--circle-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642", - "render-tests/combinations/hillshade-translucent--fill-extrusion-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642", - "render-tests/combinations/hillshade-translucent--fill-opaque": "https://github.com/mapbox/mapbox-gl-native/pull/10642", - "render-tests/combinations/hillshade-translucent--fill-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642", - "render-tests/combinations/hillshade-translucent--heatmap-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642", - "render-tests/combinations/hillshade-translucent--hillshade-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642", - "render-tests/combinations/hillshade-translucent--line-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642", - "render-tests/combinations/hillshade-translucent--raster-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642", - "render-tests/combinations/hillshade-translucent--symbol-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642", - "render-tests/combinations/line-translucent--hillshade-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642", - "render-tests/combinations/raster-translucent--hillshade-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642", - "render-tests/combinations/symbol-translucent--hillshade-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642", "render-tests/combinations/background-opaque--fill-extrusion-translucent": "needs investigation", "render-tests/combinations/background-translucent--fill-extrusion-translucent": "needs investigation", "render-tests/combinations/circle-translucent--fill-extrusion-translucent": "needs investigation", diff --git a/src/mbgl/renderer/layers/render_hillshade_layer.cpp b/src/mbgl/renderer/layers/render_hillshade_layer.cpp index 7a767522c0..55702849df 100644 --- a/src/mbgl/renderer/layers/render_hillshade_layer.cpp +++ b/src/mbgl/renderer/layers/render_hillshade_layer.cpp @@ -136,14 +136,14 @@ void RenderHillshadeLayer::render(PaintParameters& parameters, RenderSource*) { if (bucket.vertexBuffer && bucket.indexBuffer && !bucket.segments.empty()) { // Draw only the parts of the tile that aren't drawn by another tile in the layer. - draw(tile.matrix, + draw(parameters.matrixForTile(tile.id, true), *bucket.vertexBuffer, *bucket.indexBuffer, bucket.segments, tile.id); } else { // Draw the full tile. - draw(tile.matrix, + draw(parameters.matrixForTile(tile.id, true), parameters.staticData.rasterVertexBuffer, parameters.staticData.quadTriangleIndexBuffer, parameters.staticData.rasterSegments, -- cgit v1.2.1 From 638ab799eda3d8eb474fb248469914f12e6a957a Mon Sep 17 00:00:00 2001 From: Randall C Lee Date: Tue, 6 Feb 2018 15:34:46 -0500 Subject: Adjust radius (#11132) --- platform/ios/src/MGLTelemetryConfig.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/ios/src/MGLTelemetryConfig.m b/platform/ios/src/MGLTelemetryConfig.m index c45554414a..828bafb14f 100644 --- a/platform/ios/src/MGLTelemetryConfig.m +++ b/platform/ios/src/MGLTelemetryConfig.m @@ -1,7 +1,7 @@ #import "MGLTelemetryConfig.h" static const CLLocationDistance MGLConfigHibernationRadiusDefault = 300.0; -static const CLLocationDistance MGLConfigHibernationRadiusWide = 1200.0; +static const CLLocationDistance MGLConfigHibernationRadiusWide = 600.0; NSString *const MGLMapboxMetricsProfile = @"MGLMapboxMetricsProfile"; -- cgit v1.2.1 From 6c5e4575907c9c7ff73d327114f0c105e6684a4d Mon Sep 17 00:00:00 2001 From: Asheem Mamoowala Date: Wed, 7 Feb 2018 12:07:31 -0800 Subject: Add options for Custom Geometry Source types to enable clipping and wrapping geometry (#11041) --- .../conversion/custom_geometry_source_options.hpp | 20 ++++++++++++++ .../mbgl/style/sources/custom_geometry_source.hpp | 2 ++ .../style/sources/CustomGeometrySource.java | 8 +++--- .../style/sources/CustomGeometrySourceOptions.java | 31 ++++++++++++++++++++++ .../src/style/sources/custom_geometry_source.cpp | 2 +- platform/darwin/src/MGLAbstractShapeSource.h | 18 +++++++++++++ platform/darwin/src/MGLAbstractShapeSource.mm | 19 +++++++++++++ platform/macos/app/MapDocument.m | 6 ++++- src/mbgl/tile/custom_geometry_tile.cpp | 2 +- 9 files changed, 101 insertions(+), 7 deletions(-) create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySourceOptions.java diff --git a/include/mbgl/style/conversion/custom_geometry_source_options.hpp b/include/mbgl/style/conversion/custom_geometry_source_options.hpp index 73b141e799..dedecd1aa4 100644 --- a/include/mbgl/style/conversion/custom_geometry_source_options.hpp +++ b/include/mbgl/style/conversion/custom_geometry_source_options.hpp @@ -54,6 +54,26 @@ struct Converter { } } + const auto wrapValue = objectMember(value, "wrap"); + if (wrapValue) { + if (toBool(*wrapValue)) { + options.tileOptions.wrap = static_cast(*toBool(*wrapValue)); + } else { + error = { "CustomGeometrySource TileOptions wrap value must be a boolean" }; + return {}; + } + } + + const auto clipValue = objectMember(value, "clip"); + if (clipValue) { + if (toBool(*clipValue)) { + options.tileOptions.clip = static_cast(*toBool(*clipValue)); + } else { + error = { "CustomGeometrySource TileOptiosn clip value must be a boolean" }; + return {}; + } + } + return { options }; } diff --git a/include/mbgl/style/sources/custom_geometry_source.hpp b/include/mbgl/style/sources/custom_geometry_source.hpp index a0b990b44b..9daeeb3819 100644 --- a/include/mbgl/style/sources/custom_geometry_source.hpp +++ b/include/mbgl/style/sources/custom_geometry_source.hpp @@ -25,6 +25,8 @@ public: double tolerance = 0.375; uint16_t tileSize = util::tileSize; uint16_t buffer = 128; + bool clip = false; + bool wrap = false; }; struct Options { diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySource.java index 62f1719ddf..50cb93e6c0 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySource.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySource.java @@ -37,18 +37,18 @@ public class CustomGeometrySource extends Source { * @param provider The tile provider that returns geometry data for this source. */ public CustomGeometrySource(String id, GeometryTileProvider provider) { - this(id, provider, new GeoJsonOptions()); + this(id, provider, new CustomGeometrySourceOptions()); } /** - * Create a CustomGeometrySource with non-default GeoJsonOptions. + * Create a CustomGeometrySource with non-default CustomGeometrySourceOptions. *

Supported options are minZoom, maxZoom, buffer, and tolerance.

* * @param id The source id. * @param provider The tile provider that returns geometry data for this source. - * @param options GeoJsonOptions. + * @param options CustomGeometrySourceOptions. */ - public CustomGeometrySource(String id, GeometryTileProvider provider, GeoJsonOptions options) { + public CustomGeometrySource(String id, GeometryTileProvider provider, CustomGeometrySourceOptions options) { this.provider = provider; executor = Executors.newFixedThreadPool(4); initialize(id, options); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySourceOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySourceOptions.java new file mode 100644 index 0000000000..4ada38c238 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySourceOptions.java @@ -0,0 +1,31 @@ +package com.mapbox.mapboxsdk.style.sources; + +/** + * Builder class for composing CustomGeometrySource objects. + */ +public class CustomGeometrySourceOptions extends GeoJsonOptions { + + /** + * If the data includes wrapped coordinates, setting this to true unwraps the coordinates. + * + * @param wrap defaults to false + * @return the current instance for chaining + */ + public CustomGeometrySourceOptions withWrap(boolean wrap) { + this.put("wrap", wrap); + return this; + } + + /** + * If the data includes geometry outside the tile boundaries, setting this to true clips the geometry + * to the tile boundaries. + * + * @param clip defaults to false + * @return the current instance for chaining + */ + public CustomGeometrySourceOptions withClip(boolean clip) { + this.put("clip", clip); + return this; + } + +} diff --git a/platform/android/src/style/sources/custom_geometry_source.cpp b/platform/android/src/style/sources/custom_geometry_source.cpp index a60b962e45..b38405a3b1 100644 --- a/platform/android/src/style/sources/custom_geometry_source.cpp +++ b/platform/android/src/style/sources/custom_geometry_source.cpp @@ -18,7 +18,7 @@ namespace mbgl { namespace android { // This conversion is expected not to fail because it's used only in contexts where - // the value was originally a GeoJsonOptions object on the Java side. If it fails + // the value was originally a CustomGeometrySourceOptions object on the Java side. If it fails // to convert, it's a bug in our serialization or Java-side static typing. static style::CustomGeometrySource::Options convertCustomGeometrySourceOptions(jni::JNIEnv& env, jni::Object<> options, diff --git a/platform/darwin/src/MGLAbstractShapeSource.h b/platform/darwin/src/MGLAbstractShapeSource.h index 3b35986b3f..a0c6af36c5 100644 --- a/platform/darwin/src/MGLAbstractShapeSource.h +++ b/platform/darwin/src/MGLAbstractShapeSource.h @@ -81,6 +81,24 @@ extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionBuffer; */ extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance; +/** + An `NSNumber` object containing a boolean; specifies whether the geometry for a + `MGLComputedShapeSource` needs to be wrapped to accomodate coordinates with longitudes outside the [-180,180] extents. The default is `false.` + + Setting this option to `true` affects performance. + */ +extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionWrapCoordinates; + +/** + An `NSNumber` object containing a boolean; specifies whether the geometry for a + `MGLComputedShapeSource` needs to be clipped at the tile boundaries. + The default is `false.` + + Setting this options to `true` affects performance. Use this option to clip + polygons and lines at tile boundaries without artefacts. + */ +extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionClipCoordinates; + /** `MGLAbstractShapeSource` is an abstract base class for map content sources that supply vector shapes to be shown on the map. A shape source is added to an diff --git a/platform/darwin/src/MGLAbstractShapeSource.mm b/platform/darwin/src/MGLAbstractShapeSource.mm index 755d040387..80442e0e6d 100644 --- a/platform/darwin/src/MGLAbstractShapeSource.mm +++ b/platform/darwin/src/MGLAbstractShapeSource.mm @@ -8,6 +8,8 @@ const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevel = @"MGLShapeSour const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevelForClustering = @"MGLShapeSourceOptionMaximumZoomLevelForClustering"; const MGLShapeSourceOption MGLShapeSourceOptionMinimumZoomLevel = @"MGLShapeSourceOptionMinimumZoomLevel"; const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance = @"MGLShapeSourceOptionSimplificationTolerance"; +const MGLShapeSourceOption MGLShapeSourceOptionWrapCoordinates = @"MGLShapeSourceOptionWrapCoordinates"; +const MGLShapeSourceOption MGLShapeSourceOptionClipCoordinates = @"MGLShapeSourceOptionClipCoordinates"; @interface MGLAbstractShapeSource () @@ -113,5 +115,22 @@ mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDi } sourceOptions.tileOptions.tolerance = value.doubleValue; } + + if (NSNumber *value = options[MGLShapeSourceOptionWrapCoordinates]) { + if (![value isKindOfClass:[NSNumber class]]) { + [NSException raise:NSInvalidArgumentException + format:@"MGLShapeSourceOptionWrapCoordinates must be an NSNumber."]; + } + sourceOptions.tileOptions.wrap = value.boolValue; + } + + if (NSNumber *value = options[MGLShapeSourceOptionClipCoordinates]) { + if (![value isKindOfClass:[NSNumber class]]) { + [NSException raise:NSInvalidArgumentException + format:@"MGLShapeSourceOptionClipCoordinates must be an NSNumber."]; + } + sourceOptions.tileOptions.clip = value.boolValue; + } + return sourceOptions; } diff --git a/platform/macos/app/MapDocument.m b/platform/macos/app/MapDocument.m index 602ed1aa41..e23927c5a7 100644 --- a/platform/macos/app/MapDocument.m +++ b/platform/macos/app/MapDocument.m @@ -727,7 +727,11 @@ NS_ARRAY_OF(id ) *MBXFlattenedShapes(NS_ARRAY_OF(id Date: Tue, 6 Feb 2018 10:16:14 -0800 Subject: [core] Don't crash on line labels with 0 glyphs. Fixes issue #10956. --- src/mbgl/layout/symbol_projection.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/mbgl/layout/symbol_projection.cpp b/src/mbgl/layout/symbol_projection.cpp index 279d251f8f..d97bfb1ac0 100644 --- a/src/mbgl/layout/symbol_projection.cpp +++ b/src/mbgl/layout/symbol_projection.cpp @@ -271,7 +271,7 @@ namespace mbgl { placedGlyphs.push_back(*placedGlyph); } placedGlyphs.push_back(*lastPlacedGlyph); - } else { + } else if (symbol.glyphOffsets.size() == 1) { // Only a single glyph to place // So, determine whether to flip based on projected angle of the line segment it's on if (keepUpright && !flip) { @@ -289,7 +289,6 @@ namespace mbgl { return PlacementResult::NeedsFlipping; } } - assert(symbol.glyphOffsets.size() == 1); // We are relying on SymbolInstance.hasText filtering out symbols without any glyphs at all const float glyphOffsetX = symbol.glyphOffsets.front(); optional singleGlyph = placeGlyphAlongLine(fontScale * glyphOffsetX, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix); @@ -299,6 +298,8 @@ namespace mbgl { placedGlyphs.push_back(*singleGlyph); } + // The number of placedGlyphs must equal the number of glyphOffsets, which must correspond to the number of glyph vertices + // There may be 0 glyphs here, if a label consists entirely of glyphs that have 0x0 dimensions for (auto& placedGlyph : placedGlyphs) { addDynamicAttributes(placedGlyph.point, placedGlyph.angle, symbol.placementZoom, dynamicVertexArray); } -- cgit v1.2.1 From 2ed069cb0a4e057d0a6135530e073d7094e6c4ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguye=CC=82=CC=83n?= Date: Wed, 7 Feb 2018 12:42:21 -0800 Subject: [ios, macos] Copyedited computed source wrapping options Renamed MGLShapeSourceOptionWrapCoordinates to MGLShapeSourceOptionWrapsCoordinates and MGLShapeSourceOptionClipCoordinates to MGLShapeSourceOptionClipsCoordinates. Copyedited their documentation comments. --- platform/darwin/src/MGLAbstractShapeSource.h | 31 ++++++++++++++++----------- platform/darwin/src/MGLAbstractShapeSource.mm | 12 +++++------ platform/macos/app/MapDocument.m | 10 +++++---- 3 files changed, 31 insertions(+), 22 deletions(-) diff --git a/platform/darwin/src/MGLAbstractShapeSource.h b/platform/darwin/src/MGLAbstractShapeSource.h index a0c6af36c5..d61f41fbf6 100644 --- a/platform/darwin/src/MGLAbstractShapeSource.h +++ b/platform/darwin/src/MGLAbstractShapeSource.h @@ -82,22 +82,29 @@ extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionBuffer; extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance; /** - An `NSNumber` object containing a boolean; specifies whether the geometry for a - `MGLComputedShapeSource` needs to be wrapped to accomodate coordinates with longitudes outside the [-180,180] extents. The default is `false.` + An `NSNumber` object containing a Boolean value; specifies whether the shape of + an `MGLComputedShapeSource` should be wrapped to accomodate coordinates with + longitudes beyond −180 and 180. The default value is `NO`. - Setting this option to `true` affects performance. - */ -extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionWrapCoordinates; + Setting this option to `YES` affects rendering performance. + + This option is ignored when creating an instance of a class besides + `MGLComputedShapeSource`. + */ +extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionWrapsCoordinates; /** - An `NSNumber` object containing a boolean; specifies whether the geometry for a - `MGLComputedShapeSource` needs to be clipped at the tile boundaries. - The default is `false.` - - Setting this options to `true` affects performance. Use this option to clip - polygons and lines at tile boundaries without artefacts. + An `NSNumber` object containing a Boolean value; specifies whether the shape of + an `MGLComputedShapeSource` should be clipped at the edge of each tile. The + default value is `NO`. + + Setting this option to `YES` affects rendering performance. Use this option to + clip `MGLPolyline`s and `MGLPolygon`s at tile boundaries without artifacts. + + This option is ignored when creating an instance of a class besides + `MGLComputedShapeSource`. */ -extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionClipCoordinates; +extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionClipsCoordinates; /** `MGLAbstractShapeSource` is an abstract base class for map content sources that diff --git a/platform/darwin/src/MGLAbstractShapeSource.mm b/platform/darwin/src/MGLAbstractShapeSource.mm index 80442e0e6d..eb0807cee0 100644 --- a/platform/darwin/src/MGLAbstractShapeSource.mm +++ b/platform/darwin/src/MGLAbstractShapeSource.mm @@ -8,8 +8,8 @@ const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevel = @"MGLShapeSour const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevelForClustering = @"MGLShapeSourceOptionMaximumZoomLevelForClustering"; const MGLShapeSourceOption MGLShapeSourceOptionMinimumZoomLevel = @"MGLShapeSourceOptionMinimumZoomLevel"; const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance = @"MGLShapeSourceOptionSimplificationTolerance"; -const MGLShapeSourceOption MGLShapeSourceOptionWrapCoordinates = @"MGLShapeSourceOptionWrapCoordinates"; -const MGLShapeSourceOption MGLShapeSourceOptionClipCoordinates = @"MGLShapeSourceOptionClipCoordinates"; +const MGLShapeSourceOption MGLShapeSourceOptionWrapsCoordinates = @"MGLShapeSourceOptionWrapsCoordinates"; +const MGLShapeSourceOption MGLShapeSourceOptionClipsCoordinates = @"MGLShapeSourceOptionClipsCoordinates"; @interface MGLAbstractShapeSource () @@ -116,18 +116,18 @@ mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDi sourceOptions.tileOptions.tolerance = value.doubleValue; } - if (NSNumber *value = options[MGLShapeSourceOptionWrapCoordinates]) { + if (NSNumber *value = options[MGLShapeSourceOptionWrapsCoordinates]) { if (![value isKindOfClass:[NSNumber class]]) { [NSException raise:NSInvalidArgumentException - format:@"MGLShapeSourceOptionWrapCoordinates must be an NSNumber."]; + format:@"MGLShapeSourceOptionWrapsCoordinates must be an NSNumber."]; } sourceOptions.tileOptions.wrap = value.boolValue; } - if (NSNumber *value = options[MGLShapeSourceOptionClipCoordinates]) { + if (NSNumber *value = options[MGLShapeSourceOptionClipsCoordinates]) { if (![value isKindOfClass:[NSNumber class]]) { [NSException raise:NSInvalidArgumentException - format:@"MGLShapeSourceOptionClipCoordinates must be an NSNumber."]; + format:@"MGLShapeSourceOptionClipsCoordinates must be an NSNumber."]; } sourceOptions.tileOptions.clip = value.boolValue; } diff --git a/platform/macos/app/MapDocument.m b/platform/macos/app/MapDocument.m index e23927c5a7..7d39f93347 100644 --- a/platform/macos/app/MapDocument.m +++ b/platform/macos/app/MapDocument.m @@ -726,11 +726,13 @@ NS_ARRAY_OF(id ) *MBXFlattenedShapes(NS_ARRAY_OF(id Date: Tue, 6 Feb 2018 14:57:45 +0100 Subject: [android] - introduce mapview weak reference in global layout listener --- .../java/com/mapbox/mapboxsdk/maps/MapView.java | 46 ++++++++++++++-------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java index c5b7159e8c..4deea90cb5 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java @@ -22,7 +22,6 @@ import android.view.ViewTreeObserver; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.ZoomButtonsController; - import com.mapbox.mapboxsdk.R; import com.mapbox.mapboxsdk.annotations.Annotation; import com.mapbox.mapboxsdk.annotations.MarkerViewManager; @@ -37,19 +36,18 @@ import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings; import com.mapbox.mapboxsdk.net.ConnectivityReceiver; import com.mapbox.mapboxsdk.storage.FileSource; import com.mapbox.services.android.telemetry.MapboxTelemetry; +import timber.log.Timber; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; -import javax.microedition.khronos.egl.EGLConfig; -import javax.microedition.khronos.opengles.GL10; - -import timber.log.Timber; - import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_MAP_NORTH_ANIMATION; import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_WAIT_IDLE; @@ -133,17 +131,7 @@ public class MapView extends FrameLayout { setContentDescription(context.getString(R.string.mapbox_mapActionDescription)); setWillNotDraw(false); - getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - getViewTreeObserver().removeOnGlobalLayoutListener(this); - } else { - getViewTreeObserver().removeGlobalOnLayoutListener(this); - } - initialiseDrawingSurface(options); - } - }); + getViewTreeObserver().addOnGlobalLayoutListener(new MapViewLayoutListener(this, options)); } private void initialiseMap() { @@ -882,6 +870,30 @@ public class MapView extends FrameLayout { void onMapChanged(@MapChange int change); } + private static class MapViewLayoutListener implements ViewTreeObserver.OnGlobalLayoutListener { + + private WeakReference mapViewWeakReference; + private MapboxMapOptions options; + + MapViewLayoutListener(MapView mapView, MapboxMapOptions options) { + this.mapViewWeakReference = new WeakReference<>(mapView); + this.options = options; + } + + @Override + public void onGlobalLayout() { + MapView mapView = mapViewWeakReference.get(); + if (mapView != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + mapView.getViewTreeObserver().removeOnGlobalLayoutListener(this); + } else { + mapView.getViewTreeObserver().removeGlobalOnLayoutListener(this); + } + mapView.initialiseDrawingSurface(options); + } + } + } + private class FocalPointInvalidator implements FocalPointChangeListener { private final List focalPointChangeListeners = new ArrayList<>(); -- cgit v1.2.1 From aeba621573d77e1ce4f72b1e9b39bde92bf174a2 Mon Sep 17 00:00:00 2001 From: tobrun Date: Tue, 6 Feb 2018 13:41:16 +0100 Subject: [android] - programmatically create GlSurfaceView --- .../src/main/java/com/mapbox/mapboxsdk/maps/MapView.java | 4 ++-- .../src/main/res/layout/mapbox_mapview_internal.xml | 7 ------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java index 4deea90cb5..6db11db49f 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java @@ -292,7 +292,7 @@ public class MapView extends FrameLayout { addView(textureView, 0); } else { - GLSurfaceView glSurfaceView = (GLSurfaceView) findViewById(R.id.surfaceView); + GLSurfaceView glSurfaceView = new GLSurfaceView(getContext()); glSurfaceView.setZOrderMediaOverlay(mapboxMapOptions.getRenderSurfaceOnTop()); mapRenderer = new GLSurfaceViewMapRenderer(getContext(), glSurfaceView, options.getLocalIdeographFontFamily()) { @Override @@ -302,7 +302,7 @@ public class MapView extends FrameLayout { } }; - glSurfaceView.setVisibility(View.VISIBLE); + addView(glSurfaceView, 0); } nativeMapView = new NativeMapView(this, mapRenderer); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_internal.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_internal.xml index df7ccaaca9..29ff49f47e 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_internal.xml +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_internal.xml @@ -1,13 +1,6 @@ - - Date: Thu, 8 Feb 2018 08:44:24 -0500 Subject: migrated to use mapbox-java3.0 migrated to use mapbox-java3.0 note that old 2.2.9 telementry is still used though --- .../java/com/mapbox/mapboxsdk/maps/MapboxMap.java | 4 +- .../com/mapbox/mapboxsdk/maps/NativeMapView.java | 4 +- .../style/sources/CustomGeometrySource.java | 4 +- .../mapboxsdk/style/sources/GeoJsonSource.java | 6 +- .../style/sources/GeometryTileProvider.java | 2 +- .../mapboxsdk/style/sources/VectorSource.java | 2 +- .../android/MapboxGLAndroidSDKTestApp/build.gradle | 2 + .../testapp/style/GeoJsonSourceTests.java | 8 +- .../annotation/AnimatedMarkerActivity.java | 8 +- .../QueryRenderedFeaturesBoxCountActivity.java | 12 +- .../QueryRenderedFeaturesBoxHighlightActivity.java | 5 +- ...ueryRenderedFeaturesBoxSymbolCountActivity.java | 2 +- .../QueryRenderedFeaturesPropertiesActivity.java | 15 +-- .../feature/QuerySourceFeaturesActivity.java | 9 +- .../activity/style/CustomSpriteActivity.java | 18 ++- .../activity/style/FillExtrusionActivity.java | 4 +- .../testapp/activity/style/GridSourceActivity.java | 21 ++-- .../activity/style/RuntimeStyleActivity.java | 9 +- .../activity/style/SymbolGeneratorActivity.java | 20 +--- .../activity/style/SymbolLayerActivity.java | 11 +- .../style/ZoomFunctionSymbolLayerActivity.java | 18 ++- .../mapboxsdk/testapp/utils/GeoParseUtil.java | 16 +-- platform/android/config.cmake | 2 - platform/android/gradle/dependencies.gradle | 12 +- .../android/src/geojson/conversion/geometry.hpp | 130 +++++++++++---------- platform/android/src/geojson/feature.cpp | 18 +-- platform/android/src/geojson/feature.hpp | 8 +- .../android/src/geojson/feature_collection.cpp | 6 +- .../android/src/geojson/feature_collection.hpp | 4 +- platform/android/src/geojson/geometry.cpp | 2 +- platform/android/src/geojson/geometry.hpp | 2 +- platform/android/src/geojson/line_string.cpp | 28 ++--- platform/android/src/geojson/line_string.hpp | 6 +- platform/android/src/geojson/multi_line_string.cpp | 22 ++-- platform/android/src/geojson/multi_line_string.hpp | 6 +- platform/android/src/geojson/multi_point.cpp | 10 +- platform/android/src/geojson/multi_point.hpp | 4 +- platform/android/src/geojson/multi_polygon.cpp | 16 +-- platform/android/src/geojson/multi_polygon.hpp | 4 +- platform/android/src/geojson/point.cpp | 34 +++++- platform/android/src/geojson/point.hpp | 10 +- platform/android/src/geojson/polygon.cpp | 12 +- platform/android/src/geojson/polygon.hpp | 6 +- platform/android/src/geojson/position.cpp | 27 ----- platform/android/src/geojson/position.hpp | 27 ----- platform/android/src/jni.cpp | 2 - 46 files changed, 284 insertions(+), 314 deletions(-) delete mode 100644 platform/android/src/geojson/position.cpp delete mode 100644 platform/android/src/geojson/position.hpp diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java index 509e784e58..834317dd25 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java @@ -16,6 +16,8 @@ import android.text.TextUtils; import android.view.View; import android.view.ViewGroup; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.Geometry; import com.mapbox.mapboxsdk.annotations.Annotation; import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions; import com.mapbox.mapboxsdk.annotations.BaseMarkerViewOptions; @@ -42,8 +44,6 @@ import com.mapbox.mapboxsdk.style.layers.Layer; import com.mapbox.mapboxsdk.style.light.Light; import com.mapbox.mapboxsdk.style.sources.Source; import com.mapbox.services.android.telemetry.location.LocationEngine; -import com.mapbox.services.commons.geojson.Feature; -import com.mapbox.services.commons.geojson.Geometry; import java.lang.reflect.ParameterizedType; import java.util.HashMap; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java index f1635c898f..785b045779 100755 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java @@ -11,6 +11,8 @@ import android.support.annotation.Nullable; import android.text.TextUtils; import android.util.DisplayMetrics; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.Geometry; import com.mapbox.mapboxsdk.LibraryLoader; import com.mapbox.mapboxsdk.annotations.Icon; import com.mapbox.mapboxsdk.annotations.Marker; @@ -29,8 +31,6 @@ import com.mapbox.mapboxsdk.style.light.Light; import com.mapbox.mapboxsdk.style.sources.CannotAddSourceException; import com.mapbox.mapboxsdk.style.sources.Source; import com.mapbox.mapboxsdk.utils.BitmapUtils; -import com.mapbox.services.commons.geojson.Feature; -import com.mapbox.services.commons.geojson.Geometry; import java.nio.ByteBuffer; import java.util.ArrayList; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySource.java index 50cb93e6c0..1b0999ae2e 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySource.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySource.java @@ -5,10 +5,10 @@ import android.support.annotation.Nullable; import android.support.annotation.UiThread; import android.support.annotation.WorkerThread; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; import com.mapbox.mapboxsdk.geometry.LatLngBounds; import com.mapbox.mapboxsdk.style.layers.Filter; -import com.mapbox.services.commons.geojson.Feature; -import com.mapbox.services.commons.geojson.FeatureCollection; import java.lang.ref.WeakReference; import java.util.ArrayList; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java index 10ecb945ad..5c740554cd 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java @@ -4,10 +4,10 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.UiThread; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; +import com.mapbox.geojson.Geometry; import com.mapbox.mapboxsdk.style.layers.Filter; -import com.mapbox.services.commons.geojson.Feature; -import com.mapbox.services.commons.geojson.FeatureCollection; -import com.mapbox.services.commons.geojson.Geometry; import java.net.URL; import java.util.ArrayList; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeometryTileProvider.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeometryTileProvider.java index 3f1eb315d3..17e7f0f5e4 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeometryTileProvider.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeometryTileProvider.java @@ -2,8 +2,8 @@ package com.mapbox.mapboxsdk.style.sources; import android.support.annotation.WorkerThread; +import com.mapbox.geojson.FeatureCollection; import com.mapbox.mapboxsdk.geometry.LatLngBounds; -import com.mapbox.services.commons.geojson.FeatureCollection; /** * Interface that defines methods for working with {@link CustomGeometrySource}. diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/VectorSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/VectorSource.java index 9b59cf8967..62b08a90ed 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/VectorSource.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/VectorSource.java @@ -5,8 +5,8 @@ import android.support.annotation.Nullable; import android.support.annotation.Size; import android.support.annotation.UiThread; +import com.mapbox.geojson.Feature; import com.mapbox.mapboxsdk.style.layers.Filter; -import com.mapbox.services.commons.geojson.Feature; import java.net.URL; import java.util.ArrayList; diff --git a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle index 3723ae2acf..1f889a6218 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle +++ b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle @@ -57,6 +57,8 @@ dependencies { transitive = true } + api dependenciesList.mapboxJavaTurf + implementation dependenciesList.supportAppcompatV7 implementation dependenciesList.supportRecyclerView implementation dependenciesList.supportDesign diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/GeoJsonSourceTests.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/GeoJsonSourceTests.java index 5d10cfa38a..2156c96973 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/GeoJsonSourceTests.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/GeoJsonSourceTests.java @@ -13,9 +13,9 @@ import com.mapbox.mapboxsdk.testapp.R; import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest; import com.mapbox.mapboxsdk.testapp.activity.style.RuntimeStyleTestActivity; import com.mapbox.mapboxsdk.testapp.utils.ResourceUtils; -import com.mapbox.services.commons.geojson.Feature; -import com.mapbox.services.commons.geojson.FeatureCollection; -import com.mapbox.services.commons.geojson.Point; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; +import com.mapbox.geojson.Point; import org.hamcrest.Matcher; import org.junit.Test; @@ -67,7 +67,7 @@ public class GeoJsonSourceTests extends BaseActivityTest { @Override public void perform(UiController uiController, View view) { - GeoJsonSource source = new GeoJsonSource("source", Point.fromCoordinates(new double[] {0d, 0d})); + GeoJsonSource source = new GeoJsonSource("source", Point.fromLngLat(0d, 0d)); mapboxMap.addSource(source); mapboxMap.addLayer(new CircleLayer("layer", source.getId())); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java index a557bb4ed4..e6db071141 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java @@ -12,6 +12,7 @@ import android.support.v7.app.AppCompatActivity; import android.view.View; import android.view.animation.AccelerateDecelerateInterpolator; +import com.mapbox.geojson.Point; import com.mapbox.mapboxsdk.annotations.Icon; import com.mapbox.mapboxsdk.annotations.IconFactory; import com.mapbox.mapboxsdk.annotations.Marker; @@ -25,8 +26,7 @@ import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.testapp.R; import com.mapbox.mapboxsdk.testapp.utils.IconUtils; -import com.mapbox.services.api.utils.turf.TurfMeasurement; -import com.mapbox.services.commons.models.Position; +import com.mapbox.turf.TurfMeasurement; import java.util.ArrayList; import java.util.List; @@ -272,8 +272,8 @@ public class AnimatedMarkerActivity extends AppCompatActivity { private double getBearing(LatLng from, LatLng to) { return TurfMeasurement.bearing( - Position.fromCoordinates(from.getLongitude(), from.getLatitude()), - Position.fromCoordinates(to.getLongitude(), to.getLatitude()) + Point.fromLngLat(from.getLongitude(), from.getLatitude()), + Point.fromLngLat(to.getLongitude(), to.getLatitude()) ); } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxCountActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxCountActivity.java index 70d5b53428..c4ea81263b 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxCountActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxCountActivity.java @@ -7,10 +7,10 @@ import android.view.View; import android.widget.Toast; import com.google.gson.JsonElement; +import com.mapbox.geojson.Feature; import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.testapp.R; -import com.mapbox.services.commons.geojson.Feature; import java.util.List; import java.util.Map; @@ -62,12 +62,12 @@ public class QueryRenderedFeaturesBoxCountActivity extends AppCompatActivity { for (Feature feature : features) { if (feature != null) { Timber.i("Got feature %s with %s properties and Geometry %s", - feature.getId(), - feature.getProperties() != null ? feature.getProperties().entrySet().size() : "", - feature.getGeometry() != null ? feature.getGeometry().getClass().getSimpleName() : "" + feature.id(), + feature.properties() != null ? feature.properties().entrySet().size() : "", + feature.geometry() != null ? feature.geometry().getClass().getSimpleName() : "" ); - if (feature.getProperties() != null) { - for (Map.Entry entry : feature.getProperties().entrySet()) { + if (feature.properties() != null) { + for (Map.Entry entry : feature.properties().entrySet()) { Timber.i("Prop %s - %s", entry.getKey(), entry.getValue()); } } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxHighlightActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxHighlightActivity.java index 8c9d056764..df608360ad 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxHighlightActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxHighlightActivity.java @@ -7,14 +7,15 @@ import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Toast; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.style.layers.FillLayer; import com.mapbox.mapboxsdk.style.layers.Filter; import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; import com.mapbox.mapboxsdk.testapp.R; -import com.mapbox.services.commons.geojson.Feature; -import com.mapbox.services.commons.geojson.FeatureCollection; + import java.util.List; diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxSymbolCountActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxSymbolCountActivity.java index 9bad5f3e62..46409d1893 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxSymbolCountActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxSymbolCountActivity.java @@ -7,13 +7,13 @@ import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Toast; +import com.mapbox.geojson.Feature; import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.style.layers.SymbolLayer; import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; import com.mapbox.mapboxsdk.testapp.R; import com.mapbox.mapboxsdk.testapp.utils.ResourceUtils; -import com.mapbox.services.commons.geojson.Feature; import java.io.IOException; import java.util.List; diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesPropertiesActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesPropertiesActivity.java index 150b081f7f..be32718dc3 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesPropertiesActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesPropertiesActivity.java @@ -12,12 +12,13 @@ import android.widget.LinearLayout; import android.widget.TextView; import com.google.gson.JsonElement; +import com.mapbox.geojson.Feature; import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions; import com.mapbox.mapboxsdk.annotations.Marker; import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.testapp.R; -import com.mapbox.services.commons.geojson.Feature; + import java.util.List; import java.util.Map; @@ -80,12 +81,12 @@ public class QueryRenderedFeaturesPropertiesActivity extends AppCompatActivity { for (Feature feature : features) { if (feature != null) { Timber.i("Got feature %s with %s properties and Geometry %s", - feature.getId(), - feature.getProperties() != null ? feature.getProperties().entrySet().size() : "", - feature.getGeometry() != null ? feature.getGeometry().getClass().getSimpleName() : "" + feature.id(), + feature.properties() != null ? feature.properties().entrySet().size() : "", + feature.geometry() != null ? feature.geometry().getClass().getSimpleName() : "" ); - if (feature.getProperties() != null) { - for (Map.Entry entry : feature.getProperties().entrySet()) { + if (feature.properties() != null) { + for (Map.Entry entry : feature.properties().entrySet()) { Timber.i("Prop %s - %s", entry.getKey(), entry.getValue()); } } @@ -114,7 +115,7 @@ public class QueryRenderedFeaturesPropertiesActivity extends AppCompatActivity { if (customMarker.features.size() > 0) { view.addView(row(String.format("Found %s features", customMarker.features.size()))); Feature feature = customMarker.features.get(0); - for (Map.Entry prop : feature.getProperties().entrySet()) { + for (Map.Entry prop : feature.properties().entrySet()) { view.addView(row(String.format("%s: %s", prop.getKey(), prop.getValue()))); } } else { diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QuerySourceFeaturesActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QuerySourceFeaturesActivity.java index c8bef26856..14de81ab30 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QuerySourceFeaturesActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QuerySourceFeaturesActivity.java @@ -5,15 +5,16 @@ import android.support.v7.app.AppCompatActivity; import android.widget.Toast; import com.google.gson.JsonObject; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; +import com.mapbox.geojson.Point; import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.style.layers.CircleLayer; import com.mapbox.mapboxsdk.style.layers.Filter; import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; import com.mapbox.mapboxsdk.testapp.R; -import com.mapbox.services.commons.geojson.Feature; -import com.mapbox.services.commons.geojson.FeatureCollection; -import com.mapbox.services.commons.geojson.Point; + import java.util.List; @@ -42,7 +43,7 @@ public class QuerySourceFeaturesActivity extends AppCompatActivity { properties.addProperty("key1", "value1"); final GeoJsonSource source = new GeoJsonSource("test-source", FeatureCollection.fromFeatures(new Feature[] { - Feature.fromGeometry(Point.fromCoordinates(new double[] {0, 0}), properties) + Feature.fromGeometry(Point.fromLngLat(0, 0), properties) })); mapboxMap.addSource(source); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CustomSpriteActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CustomSpriteActivity.java index 8d35e659d3..30cb0a8660 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CustomSpriteActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CustomSpriteActivity.java @@ -7,6 +7,9 @@ import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.view.View; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; +import com.mapbox.geojson.Point; import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.maps.MapView; @@ -15,10 +18,7 @@ import com.mapbox.mapboxsdk.style.layers.Layer; import com.mapbox.mapboxsdk.style.layers.SymbolLayer; import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; import com.mapbox.mapboxsdk.testapp.R; -import com.mapbox.services.commons.geojson.Feature; -import com.mapbox.services.commons.geojson.FeatureCollection; -import com.mapbox.services.commons.geojson.Point; -import com.mapbox.services.commons.models.Position; + import timber.log.Timber; @@ -58,7 +58,7 @@ public class CustomSpriteActivity extends AppCompatActivity { mapboxMap.addImage(CUSTOM_ICON, BitmapFactory.decodeResource(getResources(), R.drawable.ic_car_top)); // Add a source with a geojson point - point = Point.fromCoordinates(Position.fromCoordinates(13.400972d, 52.519003d)); + point = Point.fromLngLat(13.400972d, 52.519003d); source = new GeoJsonSource( "point", FeatureCollection.fromFeatures(new Feature[] {Feature.fromGeometry(point)}) @@ -78,15 +78,13 @@ public class CustomSpriteActivity extends AppCompatActivity { fab.setImageResource(R.drawable.ic_directions_car_black); } else { // Update point - point = Point.fromCoordinates( - Position.fromCoordinates(point.getCoordinates().getLongitude() + 0.001, - point.getCoordinates().getLatitude() + 0.001) - ); + point = Point.fromLngLat(point.longitude() + 0.001, + point.latitude() + 0.001); source.setGeoJson(FeatureCollection.fromFeatures(new Feature[] {Feature.fromGeometry(point)})); // Move the camera as well mapboxMap.moveCamera(CameraUpdateFactory.newLatLng(new LatLng( - point.getCoordinates().getLatitude(), point.getCoordinates().getLongitude()))); + point.latitude(), point.longitude()))); } } }); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionActivity.java index a88a489cb1..15d7024abf 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionActivity.java @@ -12,7 +12,7 @@ import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.style.layers.FillExtrusionLayer; import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; import com.mapbox.mapboxsdk.testapp.R; -import com.mapbox.services.commons.geojson.Polygon; +import com.mapbox.geojson.Polygon; import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionColor; import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionHeight; @@ -35,7 +35,7 @@ public class FillExtrusionActivity extends AppCompatActivity { mapView.onCreate(savedInstanceState); mapView.getMapAsync(map -> { mapboxMap = map; - Polygon domTower = Polygon.fromCoordinates(new double[][][] { + Polygon domTower = Polygon.fromLngLats(new double[][][] { new double[][] { new double[] { 5.12112557888031, diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GridSourceActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GridSourceActivity.java index 9dda0f8fa2..fdc3826fb1 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GridSourceActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GridSourceActivity.java @@ -5,6 +5,10 @@ import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v7.app.AppCompatActivity; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; +import com.mapbox.geojson.MultiLineString; +import com.mapbox.geojson.Point; import com.mapbox.mapboxsdk.geometry.LatLngBounds; import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; @@ -13,10 +17,7 @@ import com.mapbox.mapboxsdk.style.layers.LineLayer; import com.mapbox.mapboxsdk.style.sources.CustomGeometrySource; import com.mapbox.mapboxsdk.style.sources.GeometryTileProvider; import com.mapbox.mapboxsdk.testapp.R; -import com.mapbox.services.commons.geojson.Feature; -import com.mapbox.services.commons.geojson.FeatureCollection; -import com.mapbox.services.commons.geojson.MultiLineString; -import com.mapbox.services.commons.models.Position; + import java.util.ArrayList; import java.util.Arrays; @@ -68,18 +69,18 @@ public class GridSourceActivity extends AppCompatActivity implements OnMapReadyC List gridLines = new ArrayList(); for (double y = Math.ceil(bounds.getLatNorth() / gridSpacing) * gridSpacing; y >= Math.floor(bounds.getLatSouth() / gridSpacing) * gridSpacing; y -= gridSpacing) { - gridLines.add(Arrays.asList(Position.fromCoordinates(bounds.getLonWest(), y), - Position.fromCoordinates(bounds.getLonEast(), y))); + gridLines.add(Arrays.asList(Point.fromLngLat(bounds.getLonWest(), y), + Point.fromLngLat(bounds.getLonEast(), y))); } - features.add(Feature.fromGeometry(MultiLineString.fromCoordinates(gridLines))); + features.add(Feature.fromGeometry(MultiLineString.fromLngLats(gridLines))); gridLines = new ArrayList(); for (double x = Math.floor(bounds.getLonWest() / gridSpacing) * gridSpacing; x <= Math.ceil(bounds.getLonEast() / gridSpacing) * gridSpacing; x += gridSpacing) { - gridLines.add(Arrays.asList(Position.fromCoordinates(x, bounds.getLatSouth()), - Position.fromCoordinates(x, bounds.getLatNorth()))); + gridLines.add(Arrays.asList(Point.fromLngLat(x, bounds.getLatSouth()), + Point.fromLngLat(x, bounds.getLatNorth()))); } - features.add(Feature.fromGeometry(MultiLineString.fromCoordinates(gridLines))); + features.add(Feature.fromGeometry(MultiLineString.fromLngLats(gridLines))); return FeatureCollection.fromFeatures(features); } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java index 942ce9aa3d..6eb4772b15 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java @@ -8,6 +8,8 @@ import android.view.Menu; import android.view.MenuItem; import android.widget.Toast; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.maps.MapView; @@ -28,8 +30,7 @@ import com.mapbox.mapboxsdk.style.sources.TileSet; import com.mapbox.mapboxsdk.style.sources.VectorSource; import com.mapbox.mapboxsdk.testapp.R; import com.mapbox.mapboxsdk.testapp.utils.ResourceUtils; -import com.mapbox.services.commons.geojson.Feature; -import com.mapbox.services.commons.geojson.FeatureCollection; + import java.io.IOException; import java.util.ArrayList; @@ -364,7 +365,7 @@ public class RuntimeStyleActivity extends AppCompatActivity { Timber.d("Updating parks source"); // change the source - int park = counter < parks.getFeatures().size() - 1 ? counter : 0; + int park = counter < parks.features().size() - 1 ? counter : 0; GeoJsonSource source = mapboxMap.getSourceAs("dynamic-park-source"); @@ -375,7 +376,7 @@ public class RuntimeStyleActivity extends AppCompatActivity { } List features = new ArrayList<>(); - features.add(parks.getFeatures().get(park)); + features.add(parks.features().get(park)); source.setGeoJson(FeatureCollection.fromFeatures(features)); // Re-post diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java index 1ef59db9b1..ca4176be6e 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java @@ -15,7 +15,8 @@ import android.view.View; import android.widget.TextView; import android.widget.Toast; -import com.google.gson.GsonBuilder; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; @@ -25,12 +26,7 @@ import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; import com.mapbox.mapboxsdk.style.sources.Source; import com.mapbox.mapboxsdk.testapp.R; import com.mapbox.mapboxsdk.testapp.utils.ResourceUtils; -import com.mapbox.services.commons.geojson.Feature; -import com.mapbox.services.commons.geojson.FeatureCollection; -import com.mapbox.services.commons.geojson.Geometry; -import com.mapbox.services.commons.geojson.custom.GeometryDeserializer; -import com.mapbox.services.commons.geojson.custom.PositionDeserializer; -import com.mapbox.services.commons.models.Position; + import java.io.IOException; import java.util.HashMap; @@ -211,14 +207,8 @@ public class SymbolGeneratorActivity extends AppCompatActivity implements OnMapR try { // read local geojson from raw folder String tinyCountriesJson = ResourceUtils.readRawResource(activity, R.raw.tiny_countries); + return FeatureCollection.fromJson(tinyCountriesJson); - // convert geojson to a model - FeatureCollection featureCollection = new GsonBuilder() - .registerTypeAdapter(Geometry.class, new GeometryDeserializer()) - .registerTypeAdapter(Position.class, new PositionDeserializer()) - .create().fromJson(tinyCountriesJson, FeatureCollection.class); - - return featureCollection; } catch (IOException exception) { return null; } @@ -288,7 +278,7 @@ public class SymbolGeneratorActivity extends AppCompatActivity implements OnMapR FeatureCollection featureCollection = params[0]; HashMap imagesMap = new HashMap<>(); - for (Feature feature : featureCollection.getFeatures()) { + for (Feature feature : featureCollection.features()) { String countryName = feature.getStringProperty(FEATURE_ID); TextView textView = new TextView(context); textView.setBackgroundColor(context.getResources().getColor(R.color.blueAccent)); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolLayerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolLayerActivity.java index d89d71e604..e3a4f4be93 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolLayerActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolLayerActivity.java @@ -11,6 +11,9 @@ import android.view.MenuItem; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; +import com.mapbox.geojson.Point; import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.maps.MapView; @@ -18,9 +21,7 @@ import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.style.layers.SymbolLayer; import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; import com.mapbox.mapboxsdk.testapp.R; -import com.mapbox.services.commons.geojson.Feature; -import com.mapbox.services.commons.geojson.FeatureCollection; -import com.mapbox.services.commons.geojson.Point; + import java.util.Arrays; import java.util.List; @@ -62,8 +63,8 @@ public class SymbolLayerActivity extends AppCompatActivity implements MapboxMap. // Add a source FeatureCollection markers = FeatureCollection.fromFeatures(new Feature[] { - Feature.fromGeometry(Point.fromCoordinates(new double[] {4.91638, 52.35673}), featureProperties("Marker 1")), - Feature.fromGeometry(Point.fromCoordinates(new double[] {4.91638, 52.34673}), featureProperties("Marker 2")) + Feature.fromGeometry(Point.fromLngLat(4.91638, 52.35673), featureProperties("Marker 1")), + Feature.fromGeometry(Point.fromLngLat(4.91638, 52.34673), featureProperties("Marker 2")) }); mapboxMap.addSource(new GeoJsonSource(MARKER_SOURCE, markers)); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/ZoomFunctionSymbolLayerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/ZoomFunctionSymbolLayerActivity.java index 4a6e62ef7d..180e2e726a 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/ZoomFunctionSymbolLayerActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/ZoomFunctionSymbolLayerActivity.java @@ -7,16 +7,16 @@ import android.view.Menu; import android.view.MenuItem; import com.google.gson.JsonObject; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; +import com.mapbox.geojson.Point; import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.style.layers.Property; import com.mapbox.mapboxsdk.style.layers.SymbolLayer; import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; import com.mapbox.mapboxsdk.testapp.R; -import com.mapbox.services.commons.geojson.Feature; -import com.mapbox.services.commons.geojson.FeatureCollection; -import com.mapbox.services.commons.geojson.Point; -import com.mapbox.services.commons.models.Position; + import java.util.List; @@ -87,15 +87,13 @@ public class ZoomFunctionSymbolLayerActivity extends AppCompatActivity { } private FeatureCollection createFeatureCollection() { - Position position = isInitialPosition - ? Position.fromCoordinates(-74.01618140, 40.701745) - : Position.fromCoordinates(-73.988097, 40.749864); + Point point = isInitialPosition + ? Point.fromLngLat(-74.01618140, 40.701745) + : Point.fromLngLat(-73.988097, 40.749864); - Point point = Point.fromCoordinates(position); - Feature feature = Feature.fromGeometry(point); JsonObject properties = new JsonObject(); properties.addProperty(KEY_PROPERTY_SELECTED, isSelected); - feature.setProperties(properties); + Feature feature = Feature.fromGeometry(point, properties); return FeatureCollection.fromFeatures(new Feature[] {feature}); } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/GeoParseUtil.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/GeoParseUtil.java index 97f70e33bb..c21e479659 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/GeoParseUtil.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/GeoParseUtil.java @@ -2,11 +2,11 @@ package com.mapbox.mapboxsdk.testapp.utils; import android.content.Context; import android.text.TextUtils; + +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; +import com.mapbox.geojson.Point; import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.services.commons.geojson.Feature; -import com.mapbox.services.commons.geojson.FeatureCollection; -import com.mapbox.services.commons.geojson.Point; -import com.mapbox.services.commons.models.Position; import java.io.BufferedReader; import java.io.IOException; @@ -31,10 +31,10 @@ public class GeoParseUtil { public static List parseGeoJsonCoordinates(String geojsonStr) { List latLngs = new ArrayList<>(); FeatureCollection featureCollection = FeatureCollection.fromJson(geojsonStr); - for (Feature feature : featureCollection.getFeatures()) { - if (feature.getGeometry() instanceof Point) { - Position point = ((Point) feature.getGeometry()).getCoordinates(); - latLngs.add(new LatLng(point.getLatitude(), point.getLongitude())); + for (Feature feature : featureCollection.features()) { + if (feature.geometry() instanceof Point) { + Point point = (Point) feature.geometry(); + latLngs.add(new LatLng(point.latitude(), point.longitude())); } } return latLngs; diff --git a/platform/android/config.cmake b/platform/android/config.cmake index 4832f8b251..e1c36789f5 100644 --- a/platform/android/config.cmake +++ b/platform/android/config.cmake @@ -248,8 +248,6 @@ add_library(mbgl-android STATIC platform/android/src/geojson/point.hpp platform/android/src/geojson/polygon.cpp platform/android/src/geojson/polygon.hpp - platform/android/src/geojson/position.cpp - platform/android/src/geojson/position.hpp # Geometry platform/android/src/geometry/lat_lng.cpp diff --git a/platform/android/gradle/dependencies.gradle b/platform/android/gradle/dependencies.gradle index 0c72368d3d..7ce0cd6196 100644 --- a/platform/android/gradle/dependencies.gradle +++ b/platform/android/gradle/dependencies.gradle @@ -8,7 +8,8 @@ ext { ] versions = [ - mapboxServices: '2.2.9', + mapboxServices: '3.0.0-beta.2', + mapboxTelemetry: '2.2.9', supportLib : '25.4.0', espresso : '3.0.1', testRunner : '1.0.1', @@ -22,9 +23,12 @@ ext { ] dependenciesList = [ - mapboxJavaServices : "com.mapbox.mapboxsdk:mapbox-java-services:${versions.mapboxServices}@jar", - mapboxJavaGeoJSON : "com.mapbox.mapboxsdk:mapbox-java-geojson:${versions.mapboxServices}@jar", - mapboxAndroidTelemetry: "com.mapbox.mapboxsdk:mapbox-android-telemetry:${versions.mapboxServices}@aar", + mapboxJavaServices : "com.mapbox.mapboxsdk:mapbox-sdk-services:${versions.mapboxServices}@jar", + mapboxJavaGeoJSON : "com.mapbox.mapboxsdk:mapbox-sdk-geojson:${versions.mapboxServices}@jar", + mapboxAndroidTelemetry: "com.mapbox.mapboxsdk:mapbox-android-telemetry:${versions.mapboxTelemetry}@aar", + + // for testApp + mapboxJavaTurf : "com.mapbox.mapboxsdk:mapbox-sdk-turf:${versions.mapboxServices}@jar", junit : "junit:junit:${versions.junit}", mockito : "org.mockito:mockito-core:${versions.mockito}", diff --git a/platform/android/src/geojson/conversion/geometry.hpp b/platform/android/src/geojson/conversion/geometry.hpp index 2ca63e2c11..5d2aab4c2d 100644 --- a/platform/android/src/geojson/conversion/geometry.hpp +++ b/platform/android/src/geojson/conversion/geometry.hpp @@ -21,88 +21,91 @@ public: jni::JNIEnv& env; /** - * Point (double[]) + * static Point fromLngLat(double longitude,double latitude) */ jni::jobject* operator()(const mapbox::geometry::point &geometry) const { - static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/Point")).release(); - static jni::jmethodID* fromCoordinates = &jni::GetStaticMethodID(env, *javaClass, "fromCoordinates", "([D)Lcom/mapbox/services/commons/geojson/Point;"); + static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/geojson/Point")).release(); + static jni::jmethodID* fromLngLat = &jni::GetStaticMethodID(env, *javaClass, "fromLngLat", "(DD)Lcom/mapbox/geojson/Point;"); - // Create Point - jni::LocalObject> position = jni::NewLocalObject(env, toGeoJsonPosition(env, geometry.x, geometry.y)); - return reinterpret_cast(jni::CallStaticMethod(env, *javaClass, *fromCoordinates, position.get())); + return reinterpret_cast(jni::CallStaticMethod(env, *javaClass, *fromLngLat, geometry.x, geometry.y)); } /** - * LineString (double[][]) + * static LineString fromLngLats(List points) */ jni::jobject* operator()(const mapbox::geometry::line_string &geometry) const { - static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/LineString")).release(); - static jni::jmethodID* fromCoordinates = &jni::GetStaticMethodID(env, *javaClass, "fromCoordinates", "([[D)Lcom/mapbox/services/commons/geojson/LineString;"); + static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/geojson/LineString")).release(); + static jni::jmethodID* fromLngLats = &jni::GetStaticMethodID(env, *javaClass, "fromLngLats", "(Ljava/util/List;)Lcom/mapbox/geojson/LineString;"); // Create - jni::LocalObject> coordinates = jni::NewLocalObject(env, toGeoJsonCoordinates(env, geometry)); - return reinterpret_cast(jni::CallStaticMethod(env, *javaClass, *fromCoordinates, coordinates.get())); + jni::LocalObject listOfPoints = jni::NewLocalObject(env, toGeoJsonListOfPoints(env, geometry)); + return reinterpret_cast(jni::CallStaticMethod(env, *javaClass, *fromLngLats, listOfPoints.get())); } /** - * MultiPoint (double[][]) + * static MultiPoint fromLngLats(List points) */ jni::jobject* operator()(const mapbox::geometry::multi_point &geometry) const { - static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/MultiPoint")).release(); - static jni::jmethodID* fromCoordinates = &jni::GetStaticMethodID(env, *javaClass, "fromCoordinates", "([[D)Lcom/mapbox/services/commons/geojson/MultiPoint;"); + static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/geojson/MultiPoint")).release(); + static jni::jmethodID* fromLngLats = &jni::GetStaticMethodID(env, *javaClass, "fromLngLats", "(Ljava/util/List;)Lcom/mapbox/geojson/MultiPoint;"); // Create - jni::LocalObject> coordinates = jni::NewLocalObject(env, toGeoJsonCoordinates(env, geometry)); - return reinterpret_cast(jni::CallStaticMethod(env, *javaClass, *fromCoordinates, coordinates.get())); + jni::LocalObject coordinates = jni::NewLocalObject(env, toGeoJsonListOfPoints(env, geometry)); + return reinterpret_cast(jni::CallStaticMethod(env, *javaClass, *fromLngLats, coordinates.get())); } /** - * Polygon (double[][][]) + * static Polygon fromLngLats(List> coordinates) */ jni::jobject* operator()(const mapbox::geometry::polygon &geometry) const { - static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/Polygon")).release(); - static jni::jmethodID* fromCoordinates = &jni::GetStaticMethodID(env, *javaClass, "fromCoordinates", "([[[D)Lcom/mapbox/services/commons/geojson/Polygon;"); + static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/geojson/Polygon")).release(); + static jni::jmethodID* fromLngLats = &jni::GetStaticMethodID(env, *javaClass, "fromLngLats", "(Ljava/util/List;)Lcom/mapbox/geojson/Polygon;"); // Create - jni::LocalObject> shape = jni::NewLocalObject(env, toShape<>(env, geometry)); - return reinterpret_cast(jni::CallStaticMethod(env, *javaClass, *fromCoordinates, shape.get())); + jni::LocalObject shape = jni::NewLocalObject(env, toShape<>(env, geometry)); + return reinterpret_cast(jni::CallStaticMethod(env, *javaClass, *fromLngLats, shape.get())); } /** - * MultiLineString (double[][][]) + * static MultiLineString fromLngLats(List> points) */ jni::jobject* operator()(const mapbox::geometry::multi_line_string &geometry) const { - static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/MultiLineString")).release(); - static jni::jmethodID* fromCoordinates = &jni::GetStaticMethodID(env, *javaClass, "fromCoordinates", "([[[D)Lcom/mapbox/services/commons/geojson/MultiLineString;"); + static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/geojson/MultiLineString")).release(); + static jni::jmethodID* fromLngLats = &jni::GetStaticMethodID(env, *javaClass, "fromLngLats", "(Ljava/util/List;)Lcom/mapbox/geojson/MultiLineString;"); // Create - jni::LocalObject> shape = jni::NewLocalObject(env, toShape<>(env, geometry)); - return reinterpret_cast(jni::CallStaticMethod(env, *javaClass, *fromCoordinates, shape.get())); + jni::LocalObject shape = jni::NewLocalObject(env, toShape<>(env, geometry)); + return reinterpret_cast(jni::CallStaticMethod(env, *javaClass, *fromLngLats, shape.get())); } /** * MultiPolygon (double[][][][]) -> [[[D + Object array == [[[[D + * + * static MultiPolygon fromLngLats(List>> points) */ jni::jobject* operator()(const mapbox::geometry::multi_polygon &geometry) const { - static jni::jclass* listClass = jni::NewGlobalRef(env, &jni::FindClass(env, "[[[D")).release(); - jni::LocalObject> jarray = jni::NewLocalObject(env, &jni::NewObjectArray(env, geometry.size(), *listClass)); + // ArrayList + static jni::jclass* arrayListClass = jni::NewGlobalRef(env, &jni::FindClass(env, "java/util/ArrayList")).release(); + static jni::jmethodID* constructor = &jni::GetMethodID(env, *arrayListClass, "", "(I)V"); + static jni::jmethodID* add = &jni::GetMethodID(env, *arrayListClass, "add", "(ILjava/lang/Object;)V"); + jni::jobject* arrayList = &jni::NewObject(env, *arrayListClass, *constructor, geometry.size()); for(size_t i = 0; i < geometry.size(); i = i + 1) { - jni::LocalObject> shape = jni::NewLocalObject(env, toShape<>(env, geometry.at(i))); - jni::SetObjectArrayElement(env, *jarray, i, shape.get()); + jni::LocalObject shape = jni::NewLocalObject(env, toShape<>(env, geometry.at(i))); + jni::CallMethod(env, arrayList, *add, i, shape.get()); } // Create the MultiPolygon - static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/MultiPolygon")).release(); - static jni::jmethodID* fromGeometries = &jni::GetStaticMethodID(env, *javaClass, "fromCoordinates", "([[[[D)Lcom/mapbox/services/commons/geojson/MultiPolygon;"); - return reinterpret_cast(jni::CallStaticMethod(env, *javaClass, *fromGeometries, jarray.get())); + static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/geojson/MultiPolygon")).release(); + static jni::jmethodID* fromGeometries = &jni::GetStaticMethodID(env, *javaClass, "fromLngLats", "(Ljava/util/List;)Lcom/mapbox/geojson/MultiPolygon;"); + return reinterpret_cast(jni::CallStaticMethod(env, *javaClass, *fromGeometries, arrayList)); } /** * GeometryCollection */ jni::jobject* operator()(const mapbox::geometry::geometry_collection &collection) const { - static jni::jclass* geometryClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/Geometry")).release(); + static jni::jclass* geometryClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/geojson/Geometry")).release(); jni::LocalObject> jarray = jni::NewLocalObject(env, &jni::NewObjectArray(env, collection.size(), *geometryClass)); for(size_t i = 0; i < collection.size(); i = i + 1) { @@ -112,8 +115,8 @@ public: } // Turn into array list and create the GeometryCollection - static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/GeometryCollection")).release(); - static jni::jmethodID* fromGeometries = &jni::GetStaticMethodID(env, *javaClass, "fromGeometries", "(Ljava/util/List;)Lcom/mapbox/services/commons/geojson/GeometryCollection;"); + static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/geojson/GeometryCollection")).release(); + static jni::jmethodID* fromGeometries = &jni::GetStaticMethodID(env, *javaClass, "fromGeometries", "(Ljava/util/List;)Lcom/mapbox/geojson/GeometryCollection;"); jni::LocalObject list = jni::NewLocalObject(env, toArrayList<>(env, *jarray)); return reinterpret_cast(jni::CallStaticMethod(env, *javaClass, *fromGeometries, list.get())); @@ -122,47 +125,50 @@ public: private: /** - * x, y -> jarray ([x,y]) - */ - static jni::jarray* toGeoJsonPosition(JNIEnv& env, double x, double y) { - jni::jarray& jarray = jni::NewArray(env, 2); - jni::jdouble array[] = {x, y}; - jni::SetArrayRegion(env, jarray, 0, 2, array); - return &jarray; - } + * vector> -> List + */ + static jni::jobject* toGeoJsonListOfPoints(JNIEnv& env, std::vector> points) { - /** - * vector> -> jarray (double[][]) -> [D + Object array == [[D - */ - static jni::jarray* toGeoJsonCoordinates(JNIEnv& env, std::vector> points) { - static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "[D")).release(); - jni::jarray& jarray = jni::NewObjectArray(env, points.size(), *javaClass); + // ArrayList + static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "java/util/ArrayList")).release(); + static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "", "(I)V"); + static jni::jmethodID* add = &jni::GetMethodID(env, *javaClass, "add", "(ILjava/lang/Object;)V"); + jni::jobject* arrayList = &jni::NewObject(env, *javaClass, *constructor, points.size()); + + + // Point + static jni::jclass* pointJavaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/geojson/Point")).release(); + static jni::jmethodID* fromLngLat = &jni::GetStaticMethodID(env, *pointJavaClass, "fromLngLat", "(DD)Lcom/mapbox/geojson/Point;"); for(size_t i = 0; i < points.size(); i = i + 1) { mapbox::geometry::point point = points.at(i); - jni::LocalObject> position = jni::NewLocalObject(env, toGeoJsonPosition(env, point.x, point.y)); - jni::SetObjectArrayElement(env, jarray, i, position.get()); + jni::LocalObject pointObject = + jni::NewLocalObject(env, jni::CallStaticMethod(env, *pointJavaClass, *fromLngLat, point.x, point.y)); + jni::CallMethod(env, arrayList, *add, i, pointObject.get()); } - return &jarray; + return arrayList; } /** - * polygon - * multi_line_string - * -> jarray (double[][][]) -> [[D + Object array == [[[D + * geometry -> List> */ template - static jni::jarray* toShape(JNIEnv& env, SHAPE value) { - static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "[[D")).release(); - jni::jarray& jarray = jni::NewObjectArray(env, value.size(), *javaClass); + static jni::jobject* toShape(JNIEnv& env, SHAPE value) { + + // ArrayList + static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "java/util/ArrayList")).release(); + static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "", "(I)V"); + static jni::jmethodID* add = &jni::GetMethodID(env, *javaClass, "add", "(ILjava/lang/Object;)V"); + jni::jobject* arrayList = &jni::NewObject(env, *javaClass, *constructor, value.size()); + for(size_t i = 0; i < value.size(); i = i + 1) { - jni::LocalObject> coordinates = jni::NewLocalObject(env, toGeoJsonCoordinates(env, value.at(i))); - jni::SetObjectArrayElement(env, jarray, i, coordinates.get()); + jni::LocalObject listOfPoints = jni::NewLocalObject(env, toGeoJsonListOfPoints(env, value.at(i))); + jni::CallMethod(env, arrayList, *add, i, listOfPoints.get()); } - return &jarray; + return arrayList; } }; diff --git a/platform/android/src/geojson/feature.cpp b/platform/android/src/geojson/feature.cpp index a6b387cd15..d8a4e829e2 100644 --- a/platform/android/src/geojson/feature.cpp +++ b/platform/android/src/geojson/feature.cpp @@ -8,11 +8,11 @@ namespace geojson { mbgl::Feature Feature::convert(jni::JNIEnv& env, jni::Object jFeature) { // Convert - auto jGeometry = getGeometry(env, jFeature); - auto jProperties = Feature::getProperties(env, jFeature); + auto jGeometry = geometry(env, jFeature); + auto jProperties = Feature::properties(env, jFeature); std::experimental::optional id; - auto jId = Feature::getId(env, jFeature); + auto jId = Feature::id(env, jFeature); if (jId) { id = { jni::Make(env, jId) }; } @@ -31,18 +31,18 @@ mbgl::Feature Feature::convert(jni::JNIEnv& env, jni::Object jFeature) return feature; } -jni::Object Feature::getGeometry(jni::JNIEnv& env, jni::Object jFeature) { - static auto method = Feature::javaClass.GetMethod ()>(env, "getGeometry"); +jni::Object Feature::geometry(jni::JNIEnv& env, jni::Object jFeature) { + static auto method = Feature::javaClass.GetMethod ()>(env, "geometry"); return jFeature.Call(env, method); } -jni::Object Feature::getProperties(jni::JNIEnv& env, jni::Object jFeature) { - static auto method = Feature::javaClass.GetMethod ()>(env, "getProperties"); +jni::Object Feature::properties(jni::JNIEnv& env, jni::Object jFeature) { + static auto method = Feature::javaClass.GetMethod ()>(env, "properties"); return jFeature.Call(env, method); } -jni::String Feature::getId(jni::JNIEnv& env, jni::Object jFeature) { - static auto method = Feature::javaClass.GetMethod(env, "getId"); +jni::String Feature::id(jni::JNIEnv& env, jni::Object jFeature) { + static auto method = Feature::javaClass.GetMethod(env, "id"); return jFeature.Call(env, method); } diff --git a/platform/android/src/geojson/feature.hpp b/platform/android/src/geojson/feature.hpp index b5d856cc42..ab59d783e5 100644 --- a/platform/android/src/geojson/feature.hpp +++ b/platform/android/src/geojson/feature.hpp @@ -16,17 +16,17 @@ namespace geojson { class Feature : private mbgl::util::noncopyable { public: - static constexpr auto Name() { return "com/mapbox/services/commons/geojson/Feature"; }; + static constexpr auto Name() { return "com/mapbox/geojson/Feature"; }; static jni::Object fromGeometry(jni::JNIEnv&, jni::Object, jni::Object, jni::String); static mbgl::Feature convert(jni::JNIEnv&, jni::Object); - static jni::Object getGeometry(jni::JNIEnv&, jni::Object); + static jni::Object geometry(jni::JNIEnv&, jni::Object); - static jni::String getId(jni::JNIEnv&, jni::Object); + static jni::String id(jni::JNIEnv&, jni::Object); - static jni::Object getProperties(jni::JNIEnv&, jni::Object); + static jni::Object properties(jni::JNIEnv&, jni::Object); static jni::Class javaClass; diff --git a/platform/android/src/geojson/feature_collection.cpp b/platform/android/src/geojson/feature_collection.cpp index 2f156532ae..59f1e317e6 100644 --- a/platform/android/src/geojson/feature_collection.cpp +++ b/platform/android/src/geojson/feature_collection.cpp @@ -7,7 +7,7 @@ namespace android { namespace geojson { mbgl::FeatureCollection FeatureCollection::convert(jni::JNIEnv& env, jni::Object jCollection) { - auto jFeatureList = FeatureCollection::getFeatures(env, jCollection); + auto jFeatureList = FeatureCollection::features(env, jCollection); auto jFeatures = java::util::List::toArray(env, jFeatureList); auto size = size_t(jFeatures.Length(env)); @@ -23,8 +23,8 @@ mbgl::FeatureCollection FeatureCollection::convert(jni::JNIEnv& env, jni::Object return collection; } -jni::Object FeatureCollection::getFeatures(jni::JNIEnv& env, jni::Object jCollection) { - static auto method = FeatureCollection::javaClass.GetMethod ()>(env, "getFeatures"); +jni::Object FeatureCollection::features(jni::JNIEnv& env, jni::Object jCollection) { + static auto method = FeatureCollection::javaClass.GetMethod ()>(env, "features"); return jCollection.Call(env, method); } diff --git a/platform/android/src/geojson/feature_collection.hpp b/platform/android/src/geojson/feature_collection.hpp index 8e9717e82b..259ffab370 100644 --- a/platform/android/src/geojson/feature_collection.hpp +++ b/platform/android/src/geojson/feature_collection.hpp @@ -13,11 +13,11 @@ namespace geojson { class FeatureCollection : private mbgl::util::noncopyable { public: - static constexpr auto Name() { return "com/mapbox/services/commons/geojson/FeatureCollection"; }; + static constexpr auto Name() { return "com/mapbox/geojson/FeatureCollection"; }; static mbgl::FeatureCollection convert(jni::JNIEnv&, jni::Object); - static jni::Object getFeatures(jni::JNIEnv&, jni::Object); + static jni::Object features(jni::JNIEnv&, jni::Object); static jni::Class javaClass; diff --git a/platform/android/src/geojson/geometry.cpp b/platform/android/src/geojson/geometry.cpp index 33bb4ee3db..ca19d8fb03 100644 --- a/platform/android/src/geojson/geometry.cpp +++ b/platform/android/src/geojson/geometry.cpp @@ -33,7 +33,7 @@ mapbox::geojson::geometry Geometry::convert(jni::JNIEnv &env, jni::Object jGeometry) { - static auto method = Geometry::javaClass.GetMethod(env, "getType"); + static auto method = Geometry::javaClass.GetMethod(env, "type"); auto jType = jGeometry.Call(env, method); auto type = jni::Make(env, jType); jni::DeleteLocalRef(env, jType); diff --git a/platform/android/src/geojson/geometry.hpp b/platform/android/src/geojson/geometry.hpp index bdcff6bb3e..b7f8909f6f 100644 --- a/platform/android/src/geojson/geometry.hpp +++ b/platform/android/src/geojson/geometry.hpp @@ -11,7 +11,7 @@ namespace geojson { class Geometry : private mbgl::util::noncopyable { public: - static constexpr auto Name() { return "com/mapbox/services/commons/geojson/Geometry"; }; + static constexpr auto Name() { return "com/mapbox/geojson/Geometry"; }; static mapbox::geojson::geometry convert(jni::JNIEnv&, jni::Object); diff --git a/platform/android/src/geojson/line_string.cpp b/platform/android/src/geojson/line_string.cpp index d0719f2538..9e99c72c4c 100644 --- a/platform/android/src/geojson/line_string.cpp +++ b/platform/android/src/geojson/line_string.cpp @@ -1,6 +1,6 @@ #include "line_string.hpp" -#include "position.hpp" +#include "point.hpp" namespace mbgl { namespace android { @@ -10,35 +10,35 @@ mapbox::geojson::line_string LineString::convert(jni::JNIEnv &env, jni::Object*/> jPositionList) { +mapbox::geojson::line_string LineString::convert(jni::JNIEnv &env, jni::Object*/> jPointList) { mapbox::geojson::line_string lineString; - if (jPositionList) { - auto jPositionArray = java::util::List::toArray(env, jPositionList); + if (jPointList) { + auto jPointArray = java::util::List::toArray(env, jPointList); - auto size = jPositionArray.Length(env); + auto size = jPointArray.Length(env); for (std::size_t i = 0; i < size; i++) { - auto jPosition = jPositionArray.Get(env, i); - lineString.push_back(Position::convert(env, jPosition)); - jni::DeleteLocalRef(env, jPosition); + auto jPoint = jPointArray.Get(env, i); + lineString.push_back(Point::convert(env, jPoint)); + jni::DeleteLocalRef(env, jPoint); } - jni::DeleteLocalRef(env, jPositionArray); + jni::DeleteLocalRef(env, jPointArray); } return lineString; } -jni::Object LineString::getCoordinates(jni::JNIEnv &env, jni::Object jLineString) { - static auto method = LineString::javaClass.GetMethod ()>(env, "getCoordinates"); +jni::Object LineString::coordinates(jni::JNIEnv &env, jni::Object jLineString) { + static auto method = LineString::javaClass.GetMethod ()>(env, "coordinates"); return jLineString.Call(env, method); } diff --git a/platform/android/src/geojson/line_string.hpp b/platform/android/src/geojson/line_string.hpp index d3be68d0a5..86033c2e6a 100644 --- a/platform/android/src/geojson/line_string.hpp +++ b/platform/android/src/geojson/line_string.hpp @@ -14,15 +14,15 @@ namespace geojson { class LineString : private mbgl::util::noncopyable { public: - static constexpr auto Name() { return "com/mapbox/services/commons/geojson/LineString"; }; + static constexpr auto Name() { return "com/mapbox/geojson/LineString"; }; static constexpr auto Type() { return "LineString"; }; static mapbox::geojson::line_string convert(jni::JNIEnv&, jni::Object); - static mapbox::geojson::line_string convert(jni::JNIEnv&, jni::Object*/>); + static mapbox::geojson::line_string convert(jni::JNIEnv&, jni::Object*/>); - static jni::Object getCoordinates(jni::JNIEnv&, jni::Object); + static jni::Object coordinates(jni::JNIEnv&, jni::Object); static jni::Class javaClass; diff --git a/platform/android/src/geojson/multi_line_string.cpp b/platform/android/src/geojson/multi_line_string.cpp index b676144bf5..c748d4786f 100644 --- a/platform/android/src/geojson/multi_line_string.cpp +++ b/platform/android/src/geojson/multi_line_string.cpp @@ -10,27 +10,27 @@ mapbox::geojson::multi_line_string MultiLineString::convert(jni::JNIEnv &env, jn mapbox::geojson::multi_line_string multiLineString; if (jMultiLineString) { - auto jPositionListsList = MultiLineString::getCoordinates(env, jMultiLineString); - multiLineString = MultiLineString::convert(env, jPositionListsList); - jni::DeleteLocalRef(env, jPositionListsList); + auto jPointListsList = MultiLineString::coordinates(env, jMultiLineString); + multiLineString = MultiLineString::convert(env, jPointListsList); + jni::DeleteLocalRef(env, jPointListsList); } return multiLineString; } -mapbox::geojson::multi_line_string MultiLineString::convert(jni::JNIEnv &env, jni::Object>*/> jPositionListsList) { +mapbox::geojson::multi_line_string MultiLineString::convert(jni::JNIEnv &env, jni::Object>*/> jPointListsList) { mapbox::geojson::multi_line_string multiLineString; - if (jPositionListsList) { - auto jPositionListsArray = java::util::List::toArray(env, jPositionListsList); + if (jPointListsList) { + auto jPositionListsArray = java::util::List::toArray(env, jPointListsList); auto size = jPositionListsArray.Length(env); multiLineString.reserve(size); for (std::size_t i = 0; i < size; i++) { - auto jPositionList = jPositionListsArray.Get(env, i); - multiLineString.push_back(LineString::convert(env, jPositionList)); - jni::DeleteLocalRef(env, jPositionList); + auto jPointsList = jPositionListsArray.Get(env, i); + multiLineString.push_back(LineString::convert(env, jPointsList)); + jni::DeleteLocalRef(env, jPointsList); } jni::DeleteLocalRef(env, jPositionListsArray); @@ -39,8 +39,8 @@ mapbox::geojson::multi_line_string MultiLineString::convert(jni::JNIEnv &env, jn return multiLineString; } -jni::Object MultiLineString::getCoordinates(jni::JNIEnv &env, jni::Object jLineString) { - static auto method = MultiLineString::javaClass.GetMethod ()>(env, "getCoordinates"); +jni::Object MultiLineString::coordinates(jni::JNIEnv &env, jni::Object jLineString) { + static auto method = MultiLineString::javaClass.GetMethod ()>(env, "coordinates"); return jLineString.Call(env, method); } diff --git a/platform/android/src/geojson/multi_line_string.hpp b/platform/android/src/geojson/multi_line_string.hpp index af33fe72d6..358a6b5dda 100644 --- a/platform/android/src/geojson/multi_line_string.hpp +++ b/platform/android/src/geojson/multi_line_string.hpp @@ -13,15 +13,15 @@ namespace geojson { class MultiLineString : private mbgl::util::noncopyable { public: - static constexpr auto Name() { return "com/mapbox/services/commons/geojson/MultiLineString"; }; + static constexpr auto Name() { return "com/mapbox/geojson/MultiLineString"; }; static constexpr auto Type() { return "MultiLineString"; }; static mapbox::geojson::multi_line_string convert(jni::JNIEnv&, jni::Object); - static mapbox::geojson::multi_line_string convert(jni::JNIEnv&, jni::Object>*/>); + static mapbox::geojson::multi_line_string convert(jni::JNIEnv&, jni::Object>*/>); - static jni::Object getCoordinates(jni::JNIEnv&, jni::Object); + static jni::Object coordinates(jni::JNIEnv&, jni::Object); static jni::Class javaClass; diff --git a/platform/android/src/geojson/multi_point.cpp b/platform/android/src/geojson/multi_point.cpp index f3acdb1ea6..4f9ff596b2 100644 --- a/platform/android/src/geojson/multi_point.cpp +++ b/platform/android/src/geojson/multi_point.cpp @@ -12,16 +12,16 @@ mapbox::geojson::multi_point MultiPoint::convert(jni::JNIEnv &env, jni::Object(LineString::convert(env, jPositionListsList)); - jni::DeleteLocalRef(env, jPositionListsList); + auto jPointListsList = MultiPoint::coordinates(env, jMultiPoint); + multiPoint = convertExplicit(LineString::convert(env, jPointListsList)); + jni::DeleteLocalRef(env, jPointListsList); } return multiPoint; } -jni::Object MultiPoint::getCoordinates(jni::JNIEnv &env, jni::Object jMultiPoint) { - static auto method = MultiPoint::javaClass.GetMethod ()>(env, "getCoordinates"); +jni::Object MultiPoint::coordinates(jni::JNIEnv &env, jni::Object jMultiPoint) { + static auto method = MultiPoint::javaClass.GetMethod ()>(env, "coordinates"); return jMultiPoint.Call(env, method); } diff --git a/platform/android/src/geojson/multi_point.hpp b/platform/android/src/geojson/multi_point.hpp index 7a698287eb..e893e879af 100644 --- a/platform/android/src/geojson/multi_point.hpp +++ b/platform/android/src/geojson/multi_point.hpp @@ -13,13 +13,13 @@ namespace geojson { class MultiPoint : private mbgl::util::noncopyable { public: - static constexpr auto Name() { return "com/mapbox/services/commons/geojson/MultiPoint"; }; + static constexpr auto Name() { return "com/mapbox/geojson/MultiPoint"; }; static constexpr auto Type() { return "MultiPoint"; }; static mapbox::geojson::multi_point convert(jni::JNIEnv&, jni::Object); - static jni::Object getCoordinates(jni::JNIEnv&, jni::Object); + static jni::Object coordinates(jni::JNIEnv&, jni::Object); static jni::Class javaClass; diff --git a/platform/android/src/geojson/multi_polygon.cpp b/platform/android/src/geojson/multi_polygon.cpp index a55884a110..f4eb0f6b2a 100644 --- a/platform/android/src/geojson/multi_polygon.cpp +++ b/platform/android/src/geojson/multi_polygon.cpp @@ -10,27 +10,27 @@ mapbox::geojson::multi_polygon MultiPolygon::convert(jni::JNIEnv &env, jni::Obje mapbox::geojson::multi_polygon multiPolygon; if (jMultiPolygon) { - auto jPositionListsListList = MultiPolygon::getCoordinates(env, jMultiPolygon); - auto jPositionListsListArray = java::util::List::toArray(env, jPositionListsListList); + auto jPointListsListList = MultiPolygon::coordinates(env, jMultiPolygon); + auto jPointListsListArray = java::util::List::toArray(env, jPointListsListList); - auto size = jPositionListsListArray.Length(env); + auto size = jPointListsListArray.Length(env); multiPolygon.reserve(size); for (size_t i = 0; i < size; i++) { - auto jPositionListsList = jPositionListsListArray.Get(env, i); + auto jPositionListsList = jPointListsListArray.Get(env, i); multiPolygon.push_back(Polygon::convert(env, jPositionListsList)); jni::DeleteLocalRef(env, jPositionListsList); } - jni::DeleteLocalRef(env, jPositionListsListList); - jni::DeleteLocalRef(env, jPositionListsListArray); + jni::DeleteLocalRef(env, jPointListsListList); + jni::DeleteLocalRef(env, jPointListsListArray); } return multiPolygon; } -jni::Object MultiPolygon::getCoordinates(jni::JNIEnv &env, jni::Object jPolygon) { - static auto method = MultiPolygon::javaClass.GetMethod ()>(env, "getCoordinates"); +jni::Object MultiPolygon::coordinates(jni::JNIEnv &env, jni::Object jPolygon) { + static auto method = MultiPolygon::javaClass.GetMethod ()>(env, "coordinates"); return jPolygon.Call(env, method); } diff --git a/platform/android/src/geojson/multi_polygon.hpp b/platform/android/src/geojson/multi_polygon.hpp index 1f144cffd2..6e1dfacfc8 100644 --- a/platform/android/src/geojson/multi_polygon.hpp +++ b/platform/android/src/geojson/multi_polygon.hpp @@ -13,13 +13,13 @@ namespace geojson { class MultiPolygon : private mbgl::util::noncopyable { public: - static constexpr auto Name() { return "com/mapbox/services/commons/geojson/MultiPolygon"; }; + static constexpr auto Name() { return "com/mapbox/geojson/MultiPolygon"; }; static constexpr auto Type() { return "MultiPolygon"; }; static mapbox::geojson::multi_polygon convert(jni::JNIEnv&, jni::Object); - static jni::Object getCoordinates(jni::JNIEnv&, jni::Object); + static jni::Object coordinates(jni::JNIEnv&, jni::Object); static jni::Class javaClass; diff --git a/platform/android/src/geojson/point.cpp b/platform/android/src/geojson/point.cpp index 3d19a119d7..5feb1b8521 100644 --- a/platform/android/src/geojson/point.cpp +++ b/platform/android/src/geojson/point.cpp @@ -1,19 +1,41 @@ #include "point.hpp" +#include "../java/util.hpp" +#include "../java_types.hpp" namespace mbgl { namespace android { namespace geojson { mapbox::geojson::point Point::convert(jni::JNIEnv &env, jni::Object jPoint) { - auto jPosition = Point::getPosition(env, jPoint); - auto point = Position::convert(env, jPosition); - jni::DeleteLocalRef(env, jPosition); + mapbox::geojson::point point; + + if (jPoint) { + auto jDoubleList = Point::coordinates(env, jPoint); + point = Point::convert(env, jDoubleList); + jni::DeleteLocalRef(env, jDoubleList); + } + + return point; +} + +mapbox::geojson::point Point::convert(jni::JNIEnv &env, jni::Object*/> jDoubleList) { + auto jDoubleArray = java::util::List::toArray(env, jDoubleList); + + jni::jdouble lon = jni::CallMethod(env, + jDoubleArray.Get(env, 0), + *java::Number::doubleValueMethodId); + jni::jdouble lat = jni::CallMethod(env, + jDoubleArray.Get(env, 1), + *java::Number::doubleValueMethodId); + mapbox::geojson::point point(lon, lat); + jni::DeleteLocalRef(env, jDoubleArray); + return point; } -jni::Object Point::getPosition(JNIEnv& env, jni::Object jPoint) { - static auto method = Point::javaClass.GetMethod ()>(env, "getCoordinates"); - return jPoint.Call(env, method); +jni::Object Point::coordinates(jni::JNIEnv &env, jni::Object jPoint) { + static auto method = Point::javaClass.GetMethod ()>(env, "coordinates"); + return jPoint.Call(env, method); } void Point::registerNative(jni::JNIEnv &env) { diff --git a/platform/android/src/geojson/point.hpp b/platform/android/src/geojson/point.hpp index 64ac0af9cc..c6412299bf 100644 --- a/platform/android/src/geojson/point.hpp +++ b/platform/android/src/geojson/point.hpp @@ -3,23 +3,25 @@ #include #include -#include "position.hpp" - #include +#include "../java/util.hpp" + namespace mbgl { namespace android { namespace geojson { class Point : private mbgl::util::noncopyable { public: - static constexpr auto Name() { return "com/mapbox/services/commons/geojson/Point"; }; + static constexpr auto Name() { return "com/mapbox/geojson/Point"; }; static constexpr auto Type() { return "Point"; }; static mapbox::geojson::point convert(jni::JNIEnv&, jni::Object); - static jni::Object getPosition(JNIEnv&, jni::Object); + static mapbox::geojson::point convert(jni::JNIEnv&, jni::Object*/>); + + static jni::Object coordinates(JNIEnv&, jni::Object); static jni::Class javaClass; diff --git a/platform/android/src/geojson/polygon.cpp b/platform/android/src/geojson/polygon.cpp index ef00f9df3f..30ba996640 100644 --- a/platform/android/src/geojson/polygon.cpp +++ b/platform/android/src/geojson/polygon.cpp @@ -12,7 +12,7 @@ mapbox::geojson::polygon Polygon::convert(jni::JNIEnv &env, jni::Object mapbox::geojson::polygon polygon; if (jPolygon) { - auto jPositionListsList = Polygon::getCoordinates(env, jPolygon); + auto jPositionListsList = Polygon::coordinates(env, jPolygon); polygon = Polygon::convert(env, jPositionListsList); jni::DeleteLocalRef(env, jPositionListsList); } @@ -20,11 +20,11 @@ mapbox::geojson::polygon Polygon::convert(jni::JNIEnv &env, jni::Object return polygon; } -mapbox::geojson::polygon Polygon::convert(jni::JNIEnv &env, jni::Object>*/> jPositionListsList) { +mapbox::geojson::polygon Polygon::convert(jni::JNIEnv &env, jni::Object>*/> jPointListsList) { mapbox::geojson::polygon polygon; - if (jPositionListsList) { - auto multiLine = MultiLineString::convert(env, jPositionListsList); + if (jPointListsList) { + auto multiLine = MultiLineString::convert(env, jPointListsList); polygon.reserve(multiLine.size()); for (auto&& line : multiLine) { polygon.emplace_back(convertExplicit(std::move(line))); @@ -35,8 +35,8 @@ mapbox::geojson::polygon Polygon::convert(jni::JNIEnv &env, jni::Object Polygon::getCoordinates(jni::JNIEnv &env, jni::Object jPolygon) { - static auto method = Polygon::javaClass.GetMethod ()>(env, "getCoordinates"); +jni::Object Polygon::coordinates(jni::JNIEnv &env, jni::Object jPolygon) { + static auto method = Polygon::javaClass.GetMethod ()>(env, "coordinates"); return jPolygon.Call(env, method); } diff --git a/platform/android/src/geojson/polygon.hpp b/platform/android/src/geojson/polygon.hpp index e5362cedf1..a8b2b93827 100644 --- a/platform/android/src/geojson/polygon.hpp +++ b/platform/android/src/geojson/polygon.hpp @@ -13,15 +13,15 @@ namespace geojson { class Polygon : private mbgl::util::noncopyable { public: - static constexpr auto Name() { return "com/mapbox/services/commons/geojson/Polygon"; }; + static constexpr auto Name() { return "com/mapbox/geojson/Polygon"; }; static constexpr auto Type() { return "Polygon"; }; static mapbox::geojson::polygon convert(jni::JNIEnv &, jni::Object); - static mapbox::geojson::polygon convert(jni::JNIEnv&, jni::Object>*/>); + static mapbox::geojson::polygon convert(jni::JNIEnv&, jni::Object>*/>); - static jni::Object getCoordinates(jni::JNIEnv&, jni::Object); + static jni::Object coordinates(jni::JNIEnv&, jni::Object); static jni::Class javaClass; diff --git a/platform/android/src/geojson/position.cpp b/platform/android/src/geojson/position.cpp deleted file mode 100644 index c0e6da3887..0000000000 --- a/platform/android/src/geojson/position.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "position.hpp" - -namespace mbgl { -namespace android { -namespace geojson { - -mapbox::geojson::point Position::convert(jni::JNIEnv &env, jni::Object jPosition) { - static auto method = Position::javaClass.GetMethod ()>(env, "getCoordinates"); - // Array with 0: longitude, 1: latitude (and optionally 2: altitude) - auto coordinates = jPosition.Call(env, method); - jdouble lngLat[2]; - coordinates.GetRegion(env, 0, lngLat); - mapbox::geojson::point point(lngLat[0], lngLat[1]); - jni::DeleteLocalRef(env, coordinates); - return point; -} - -void Position::registerNative(jni::JNIEnv &env) { - // Lookup the class - javaClass = *jni::Class::Find(env).NewGlobalRef(env).release(); -} - -jni::Class Position::javaClass; - -} // namespace geojson -} // namespace android -} // namespace mbgl \ No newline at end of file diff --git a/platform/android/src/geojson/position.hpp b/platform/android/src/geojson/position.hpp deleted file mode 100644 index 7017a8172a..0000000000 --- a/platform/android/src/geojson/position.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include -#include - -#include - -namespace mbgl { -namespace android { -namespace geojson { - -class Position : private mbgl::util::noncopyable { -public: - static constexpr auto Name() { return "com/mapbox/services/commons/models/Position"; }; - - static constexpr auto Type() { return "Position"; }; - - static mapbox::geojson::point convert(jni::JNIEnv&, jni::Object); - - static jni::Class javaClass; - - static void registerNative(jni::JNIEnv&); -}; - -} // namespace geojson -} // namespace android -} // namespace mbgl \ No newline at end of file diff --git a/platform/android/src/jni.cpp b/platform/android/src/jni.cpp index 88ad0edb9e..c2fd1c95ad 100755 --- a/platform/android/src/jni.cpp +++ b/platform/android/src/jni.cpp @@ -20,7 +20,6 @@ #include "geojson/multi_polygon.hpp" #include "geojson/point.hpp" #include "geojson/polygon.hpp" -#include "geojson/position.hpp" #include "geometry/lat_lng.hpp" #include "geometry/lat_lng_bounds.hpp" #include "geometry/lat_lng_quad.hpp" @@ -128,7 +127,6 @@ void registerNatives(JavaVM *vm) { geojson::MultiPolygon::registerNative(env); geojson::Point::registerNative(env); geojson::Polygon::registerNative(env); - geojson::Position::registerNative(env); // Geometry LatLng::registerNative(env); -- cgit v1.2.1 From c3bf7c55a1f648e57c3853d555ff5f63c989f8c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguye=CC=82=CC=83n?= Date: Wed, 7 Feb 2018 12:27:01 -0800 Subject: [ios, macos] Added option to restrict tile source to bounds --- platform/darwin/docs/guides/For Style Authors.md.ejs | 2 ++ platform/darwin/src/MGLTileSource.h | 14 ++++++++++++++ platform/darwin/src/MGLTileSource.mm | 12 ++++++++++++ platform/darwin/test/MGLTileSetTests.mm | 14 ++++++++++++++ platform/ios/CHANGELOG.md | 1 + platform/ios/docs/guides/For Style Authors.md | 2 ++ platform/macos/CHANGELOG.md | 1 + platform/macos/docs/guides/For Style Authors.md | 2 ++ 8 files changed, 48 insertions(+) diff --git a/platform/darwin/docs/guides/For Style Authors.md.ejs b/platform/darwin/docs/guides/For Style Authors.md.ejs index 3f6c3fc2a4..c2eaf337e0 100644 --- a/platform/darwin/docs/guides/For Style Authors.md.ejs +++ b/platform/darwin/docs/guides/For Style Authors.md.ejs @@ -160,6 +160,7 @@ the following terms for concepts defined in the style specification: In the style specification | In the SDK ---------------------------|--------- +bounds | coordinate bounds filter | predicate function type | interpolation mode id | identifier @@ -200,6 +201,7 @@ In style JSON | In TileJSON | In the SDK `tiles` | `tiles` | `tileURLTemplates` parameter in `-[MGLTileSource initWithIdentifier:tileURLTemplates:options:]` `minzoom` | `minzoom` | `MGLTileSourceOptionMinimumZoomLevel` `maxzoom` | `maxzoom` | `MGLTileSourceOptionMaximumZoomLevel` +`bounds` | `bounds` | `MGLTileSourceOptionCoordinateBounds` `tileSize` | — | `MGLTileSourceOptionTileSize` `attribution` | `attribution` | `MGLTileSourceOptionAttributionHTMLString` (but consider specifying `MGLTileSourceOptionAttributionInfos` instead for improved security) `scheme` | `scheme` | `MGLTileSourceOptionTileCoordinateSystem` diff --git a/platform/darwin/src/MGLTileSource.h b/platform/darwin/src/MGLTileSource.h index caeafcd2f6..3dc268db60 100644 --- a/platform/darwin/src/MGLTileSource.h +++ b/platform/darwin/src/MGLTileSource.h @@ -41,6 +41,20 @@ extern MGL_EXPORT const MGLTileSourceOption MGLTileSourceOptionMinimumZoomLevel; */ extern MGL_EXPORT const MGLTileSourceOption MGLTileSourceOptionMaximumZoomLevel; +/** + An `NSValue` object containing an `MGLCoordinateBounds` struct that specifies + the geographic extent of the source. + + If this option is specified, the SDK avoids requesting any tile that falls + outside of the coordinate bounds. Otherwise, the SDK requests any tile needed + to cover the viewport, as it does by default. + + This option corresponds to the `bounds` key in the + TileJSON + specification. + */ +extern MGL_EXPORT const MGLTileSourceOption MGLTileSourceOptionCoordinateBounds; + #if TARGET_OS_IPHONE /** An HTML string defining the buttons to be displayed in an action sheet when the diff --git a/platform/darwin/src/MGLTileSource.mm b/platform/darwin/src/MGLTileSource.mm index 5644ad9a06..bc985bd728 100644 --- a/platform/darwin/src/MGLTileSource.mm +++ b/platform/darwin/src/MGLTileSource.mm @@ -1,7 +1,9 @@ #import "MGLTileSource_Private.h" #import "MGLAttributionInfo_Private.h" +#import "MGLGeometry_Private.h" #import "NSString+MGLAdditions.h" +#import "NSValue+MGLAdditions.h" #if TARGET_OS_IPHONE #import @@ -13,6 +15,7 @@ const MGLTileSourceOption MGLTileSourceOptionMinimumZoomLevel = @"MGLTileSourceOptionMinimumZoomLevel"; const MGLTileSourceOption MGLTileSourceOptionMaximumZoomLevel = @"MGLTileSourceOptionMaximumZoomLevel"; +const MGLTileSourceOption MGLTileSourceOptionCoordinateBounds = @"MGLTileSourceOptionCoordinateBounds"; const MGLTileSourceOption MGLTileSourceOptionAttributionHTMLString = @"MGLTileSourceOptionAttributionHTMLString"; const MGLTileSourceOption MGLTileSourceOptionAttributionInfos = @"MGLTileSourceOptionAttributionInfos"; const MGLTileSourceOption MGLTileSourceOptionTileCoordinateSystem = @"MGLTileSourceOptionTileCoordinateSystem"; @@ -70,6 +73,15 @@ mbgl::Tileset MGLTileSetFromTileURLTemplates(NS_ARRAY_OF(NSString *) *tileURLTem [NSException raise:NSInvalidArgumentException format:@"MGLTileSourceOptionMinimumZoomLevel must be less than MGLTileSourceOptionMaximumZoomLevel."]; } + + if (NSValue *coordinateBounds = options[MGLTileSourceOptionCoordinateBounds]) { + if (![coordinateBounds isKindOfClass:[NSValue class]] + && strcmp(coordinateBounds.objCType, @encode(MGLCoordinateBounds)) == 0) { + [NSException raise:NSInvalidArgumentException + format:@"MGLTileSourceOptionCoordinateBounds must be set to an NSValue containing an MGLCoordinateBounds."]; + } + tileSet.bounds = MGLLatLngBoundsFromCoordinateBounds(coordinateBounds.MGLCoordinateBoundsValue); + } if (NSString *attribution = options[MGLTileSourceOptionAttributionHTMLString]) { if (![attribution isKindOfClass:[NSString class]]) { diff --git a/platform/darwin/test/MGLTileSetTests.mm b/platform/darwin/test/MGLTileSetTests.mm index 40eab5f974..4d5e1fcd05 100644 --- a/platform/darwin/test/MGLTileSetTests.mm +++ b/platform/darwin/test/MGLTileSetTests.mm @@ -2,6 +2,7 @@ #import #import "MGLTileSource_Private.h" +#import "MGLGeometry_Private.h" #include @@ -40,6 +41,19 @@ XCTAssertEqual(tileSet.zoomRange.min, 1); XCTAssertEqual(tileSet.zoomRange.max, 2); + // when the tile set has a bounds set + MGLCoordinateBounds bounds = MGLCoordinateBoundsMake(CLLocationCoordinate2DMake(12, 34), CLLocationCoordinate2DMake(56, 78)); + tileSet = MGLTileSetFromTileURLTemplates(@[@"tile.1"], @{ + MGLTileSourceOptionCoordinateBounds: @(bounds), + }); + + // the mbgl object reflects the set values for the bounds + XCTAssert(!!tileSet.bounds, @"The bounds are set after setting the bounds"); + if (tileSet.bounds) { + MGLCoordinateBounds actual = MGLCoordinateBoundsFromLatLngBounds(*tileSet.bounds); + XCTAssert(MGLCoordinateBoundsEqualToCoordinateBounds(bounds, actual), @"The bounds round-trip"); + } + // when the tile set has an attribution NSString *attribution = @"my tileset © ©️🎈"; tileSet = MGLTileSetFromTileURLTemplates(tileURLTemplates, @{ diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 75a77720ae..5482b6d5eb 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -16,6 +16,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Properties such as `MGLSymbolStyleLayer.iconAllowsOverlap` and `MGLSymbolStyleLayer.iconIgnoresPlacement` now account for symbols in other sources. ([#10436](https://github.com/mapbox/mapbox-gl-native/pull/10436)) * Improved the reliability of collision detection between symbols near the edges of tiles, as well as between symbols when the map is tilted. It is no longer necessary to enable `MGLSymbolStyleLayer.symbolAvoidsEdges` to prevent symbols in adjacent tiles from overlapping with each other. ([#10436](https://github.com/mapbox/mapbox-gl-native/pull/10436)) * Symbols can fade in and out as the map pans, rotates, or tilts. ([#10436](https://github.com/mapbox/mapbox-gl-native/pull/10436)) +* Added the `MGLTileSourceOptionTileCoordinateBounds` option to create an `MGLTileSource` that only supplies tiles within a specific geographic bounding box. ([#11141](https://github.com/mapbox/mapbox-gl-native/pull/11141)) * Fixed an issue preventing a dynamically-added `MGLRasterStyleLayer` from drawing until the map pans. ([#10270](https://github.com/mapbox/mapbox-gl-native/pull/10270)) * Fixed an issue preventing `MGLImageSource`s from drawing on the map when the map is zoomed in and tilted. ([#10677](https://github.com/mapbox/mapbox-gl-native/pull/10677)) diff --git a/platform/ios/docs/guides/For Style Authors.md b/platform/ios/docs/guides/For Style Authors.md index 51cd87a766..72d6f144a0 100644 --- a/platform/ios/docs/guides/For Style Authors.md +++ b/platform/ios/docs/guides/For Style Authors.md @@ -109,6 +109,7 @@ the following terms for concepts defined in the style specification: In the style specification | In the SDK ---------------------------|--------- +bounds | coordinate bounds filter | predicate function type | interpolation mode id | identifier @@ -149,6 +150,7 @@ In style JSON | In TileJSON | In the SDK `tiles` | `tiles` | `tileURLTemplates` parameter in `-[MGLTileSource initWithIdentifier:tileURLTemplates:options:]` `minzoom` | `minzoom` | `MGLTileSourceOptionMinimumZoomLevel` `maxzoom` | `maxzoom` | `MGLTileSourceOptionMaximumZoomLevel` +`bounds` | `bounds` | `MGLTileSourceOptionCoordinateBounds` `tileSize` | — | `MGLTileSourceOptionTileSize` `attribution` | `attribution` | `MGLTileSourceOptionAttributionHTMLString` (but consider specifying `MGLTileSourceOptionAttributionInfos` instead for improved security) `scheme` | `scheme` | `MGLTileSourceOptionTileCoordinateSystem` diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index c8373b2bf5..440d4d9787 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -10,6 +10,7 @@ * Properties such as `MGLSymbolStyleLayer.iconAllowsOverlap` and `MGLSymbolStyleLayer.iconIgnoresPlacement` now account for symbols in other sources. ([#10436](https://github.com/mapbox/mapbox-gl-native/pull/10436)) * Improved the reliability of collision detection between symbols near the edges of tiles, as well as between symbols when the map is tilted. It is no longer necessary to enable `MGLSymbolStyleLayer.symbolAvoidsEdges` to prevent symbols in adjacent tiles from overlapping with each other. ([#10436](https://github.com/mapbox/mapbox-gl-native/pull/10436)) * Symbols can fade in and out as the map pans, rotates, or tilts. ([#10436](https://github.com/mapbox/mapbox-gl-native/pull/10436)) +* Added the `MGLTileSourceOptionTileCoordinateBounds` option to create an `MGLTileSource` that only supplies tiles within a specific geographic bounding box. ([#11141](https://github.com/mapbox/mapbox-gl-native/pull/11141)) * Fixed an issue preventing a dynamically-added `MGLRasterStyleLayer` from drawing until the map pans. ([#10270](https://github.com/mapbox/mapbox-gl-native/pull/10270)) * Fixed an issue preventing `MGLImageSource`s from drawing on the map when the map is zoomed in and tilted. ([#10677](https://github.com/mapbox/mapbox-gl-native/pull/10677)) diff --git a/platform/macos/docs/guides/For Style Authors.md b/platform/macos/docs/guides/For Style Authors.md index 4a16066a2a..6897d640b3 100644 --- a/platform/macos/docs/guides/For Style Authors.md +++ b/platform/macos/docs/guides/For Style Authors.md @@ -96,6 +96,7 @@ the following terms for concepts defined in the style specification: In the style specification | In the SDK ---------------------------|--------- +bounds | coordinate bounds filter | predicate function type | interpolation mode id | identifier @@ -136,6 +137,7 @@ In style JSON | In TileJSON | In the SDK `tiles` | `tiles` | `tileURLTemplates` parameter in `-[MGLTileSource initWithIdentifier:tileURLTemplates:options:]` `minzoom` | `minzoom` | `MGLTileSourceOptionMinimumZoomLevel` `maxzoom` | `maxzoom` | `MGLTileSourceOptionMaximumZoomLevel` +`bounds` | `bounds` | `MGLTileSourceOptionCoordinateBounds` `tileSize` | — | `MGLTileSourceOptionTileSize` `attribution` | `attribution` | `MGLTileSourceOptionAttributionHTMLString` (but consider specifying `MGLTileSourceOptionAttributionInfos` instead for improved security) `scheme` | `scheme` | `MGLTileSourceOptionTileCoordinateSystem` -- cgit v1.2.1 From 64374711a09f27c41c93eb6b72d0f6a560234083 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 | 2 + include/mbgl/style/conversion/tileset.hpp | 31 ++++++ include/mbgl/util/geo.hpp | 18 ++-- include/mbgl/util/projection.hpp | 4 + include/mbgl/util/tileset.hpp | 14 ++- src/mbgl/algorithm/update_renderables.hpp | 6 +- src/mbgl/annotation/render_annotation_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 | 104 +++++++++++++++++++++ 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 +++++++++++ 18 files changed, 436 insertions(+), 17 deletions(-) create mode 100644 src/mbgl/style/conversion/tileset.cpp 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 33b3072f3a..0c109d080d 100644 --- a/cmake/core-files.cmake +++ b/cmake/core-files.cmake @@ -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 diff --git a/cmake/test-files.cmake b/cmake/test-files.cmake index 027c34f31e..dfd646e624 100644 --- a/cmake/test-files.cmake +++ b/cmake/test-files.cmake @@ -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 @@ -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 diff --git a/include/mbgl/style/conversion/tileset.hpp b/include/mbgl/style/conversion/tileset.hpp index 377170aa6a..6577e39576 100644 --- a/include/mbgl/style/conversion/tileset.hpp +++ b/include/mbgl/style/conversion/tileset.hpp @@ -10,6 +10,11 @@ namespace conversion { template <> struct Converter { public: + + bool validateLatitude(const double lat) const { + return lat < 90 && lat > -90; + } + template optional operator()(const V& value, Error& error) const { Tileset result; @@ -72,6 +77,32 @@ public: 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/include/mbgl/util/geo.hpp b/include/mbgl/util/geo.hpp index 6d725b102b..dacdb968f3 100644 --- a/include/mbgl/util/geo.hpp +++ b/include/mbgl/util/geo.hpp @@ -154,19 +154,15 @@ public: 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; diff --git a/include/mbgl/util/projection.hpp b/include/mbgl/util/projection.hpp index f64502c5bc..a9c12e595b 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 61aa47d4ea..7bef0e89ed 100644 --- a/include/mbgl/util/tileset.hpp +++ b/include/mbgl/util/tileset.hpp @@ -2,7 +2,9 @@ #include #include - +#include +#include +#include #include #include #include @@ -17,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 }, @@ -25,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/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 ba80be0da0..0aff64583d 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_geojson_source.cpp b/src/mbgl/renderer/sources/render_geojson_source.cpp index 504db78ea3..c13cd49f46 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 bcd719365d..5e63e929e2 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 ca3071c6b0..a3da3e7cbd 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 3e2311089d..c9e3b0630a 100644 --- a/src/mbgl/renderer/tile_pyramid.cpp +++ b/src/mbgl/renderer/tile_pyramid.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -15,6 +16,7 @@ #include +#include #include namespace mbgl { @@ -62,6 +64,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) { @@ -134,7 +137,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 73a8d34c1c..e34b050273 100644 --- a/src/mbgl/renderer/tile_pyramid.hpp +++ b/src/mbgl/renderer/tile_pyramid.hpp @@ -40,6 +40,7 @@ public: 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 new file mode 100644 index 0000000000..6e559c0cac --- /dev/null +++ b/src/mbgl/style/conversion/tileset.cpp @@ -0,0 +1,104 @@ +#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; + + 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 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 scheme = toString(*schemeValue); + if (scheme && *scheme == "tms") { + result.scheme = Tileset::Scheme::TMS; + } + } + + auto minzoomValue = objectMember(value, "minzoom"); + if (minzoomValue) { + optional minzoom = toNumber(*minzoomValue); + if (!minzoom || *minzoom < 0 || *minzoom > std::numeric_limits::max()) { + error = { "invalid minzoom" }; + return {}; + } + result.zoomRange.min = *minzoom; + } + + auto maxzoomValue = objectMember(value, "maxzoom"); + if (maxzoomValue) { + optional maxzoom = toNumber(*maxzoomValue); + if (!maxzoom || *maxzoom < 0 || *maxzoom > std::numeric_limits::max()) { + error = { "invalid maxzoom" }; + return {}; + } + result.zoomRange.max = *maxzoom; + } + + auto attributionValue = objectMember(value, "attribution"); + if (attributionValue) { + optional 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 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; +} + +} // namespace conversion +} // namespace style +} // namespace mbgl 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 From 4ab07f5be73f802e1f8f9d88eafa7077dbd5cac8 Mon Sep 17 00:00:00 2001 From: Asheem Mamoowala Date: Mon, 5 Feb 2018 19:31:03 -0500 Subject: Reset tileset-based render sources when any tileset properties changed. (#11042) --- src/mbgl/renderer/sources/render_raster_source.cpp | 15 ++++++++------- src/mbgl/renderer/sources/render_raster_source.hpp | 2 +- src/mbgl/renderer/sources/render_vector_source.cpp | 15 ++++++++------- src/mbgl/renderer/sources/render_vector_source.hpp | 2 +- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/mbgl/renderer/sources/render_raster_source.cpp b/src/mbgl/renderer/sources/render_raster_source.cpp index 5e63e929e2..46e3c2eff6 100644 --- a/src/mbgl/renderer/sources/render_raster_source.cpp +++ b/src/mbgl/renderer/sources/render_raster_source.cpp @@ -29,14 +29,10 @@ void RenderRasterSource::update(Immutable baseImpl_, enabled = needsRendering; - optional tileset = impl().getTileset(); + optional _tileset = impl().getTileset(); - if (!tileset) { - return; - } - - if (tileURLTemplates != tileset->tiles) { - tileURLTemplates = tileset->tiles; + if (tileset != _tileset) { + tileset = _tileset; // TODO: this removes existing buckets, and will cause flickering. // Should instead refresh tile data in place. @@ -44,6 +40,11 @@ void RenderRasterSource::update(Immutable baseImpl_, tilePyramid.renderTiles.clear(); tilePyramid.cache.clear(); } + // Allow clearing the tile pyramid first, before the early return in case + // the new tileset is not yet available or has an error in loading + if (!_tileset) { + return; + } tilePyramid.update(layers, needsRendering, diff --git a/src/mbgl/renderer/sources/render_raster_source.hpp b/src/mbgl/renderer/sources/render_raster_source.hpp index 01de812309..aebc49bf8a 100644 --- a/src/mbgl/renderer/sources/render_raster_source.hpp +++ b/src/mbgl/renderer/sources/render_raster_source.hpp @@ -39,7 +39,7 @@ private: const style::RasterSource::Impl& impl() const; TilePyramid tilePyramid; - optional> tileURLTemplates; + optional tileset; }; template <> diff --git a/src/mbgl/renderer/sources/render_vector_source.cpp b/src/mbgl/renderer/sources/render_vector_source.cpp index a3da3e7cbd..310dfe68b8 100644 --- a/src/mbgl/renderer/sources/render_vector_source.cpp +++ b/src/mbgl/renderer/sources/render_vector_source.cpp @@ -32,14 +32,10 @@ void RenderVectorSource::update(Immutable baseImpl_, enabled = needsRendering; - optional tileset = impl().getTileset(); + optional _tileset = impl().getTileset(); - if (!tileset) { - return; - } - - if (tileURLTemplates != tileset->tiles) { - tileURLTemplates = tileset->tiles; + if (tileset != _tileset) { + tileset = _tileset; // TODO: this removes existing buckets, and will cause flickering. // Should instead refresh tile data in place. @@ -47,6 +43,11 @@ void RenderVectorSource::update(Immutable baseImpl_, tilePyramid.renderTiles.clear(); tilePyramid.cache.clear(); } + // Allow clearing the tile pyramid first, before the early return in case + // the new tileset is not yet available or has an error in loading + if (!_tileset) { + return; + } tilePyramid.update(layers, needsRendering, diff --git a/src/mbgl/renderer/sources/render_vector_source.hpp b/src/mbgl/renderer/sources/render_vector_source.hpp index 5e5c6d1108..f237b4b825 100644 --- a/src/mbgl/renderer/sources/render_vector_source.hpp +++ b/src/mbgl/renderer/sources/render_vector_source.hpp @@ -39,7 +39,7 @@ private: const style::VectorSource::Impl& impl() const; TilePyramid tilePyramid; - optional> tileURLTemplates; + optional tileset; }; template <> -- cgit v1.2.1 From 101b55b0f070ba86c8d5df089dc03393c9abe8da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguye=CC=82=CC=83n?= Date: Wed, 7 Feb 2018 12:27:01 -0800 Subject: [ios, macos] Added option to restrict tile source to bounds Cherry-picked from c3bf7c55a1f648e57c3853d555ff5f63c989f8c4. --- platform/darwin/docs/guides/For Style Authors.md.ejs | 2 ++ platform/darwin/src/MGLTileSource.h | 14 ++++++++++++++ platform/darwin/src/MGLTileSource.mm | 12 ++++++++++++ platform/darwin/test/MGLTileSetTests.mm | 14 ++++++++++++++ platform/ios/CHANGELOG.md | 6 +++++- platform/ios/docs/guides/For Style Authors.md | 2 ++ platform/macos/CHANGELOG.md | 4 ++++ platform/macos/docs/guides/For Style Authors.md | 2 ++ 8 files changed, 55 insertions(+), 1 deletion(-) diff --git a/platform/darwin/docs/guides/For Style Authors.md.ejs b/platform/darwin/docs/guides/For Style Authors.md.ejs index 06a8907704..ba6f14647c 100644 --- a/platform/darwin/docs/guides/For Style Authors.md.ejs +++ b/platform/darwin/docs/guides/For Style Authors.md.ejs @@ -160,6 +160,7 @@ the following terms for concepts defined in the style specification: In the style specification | In the SDK ---------------------------|--------- +bounds | coordinate bounds filter | predicate function type | interpolation mode id | identifier @@ -200,6 +201,7 @@ In style JSON | In TileJSON | In the SDK `tiles` | `tiles` | `tileURLTemplates` parameter in `-[MGLTileSource initWithIdentifier:tileURLTemplates:options:]` `minzoom` | `minzoom` | `MGLTileSourceOptionMinimumZoomLevel` `maxzoom` | `maxzoom` | `MGLTileSourceOptionMaximumZoomLevel` +`bounds` | `bounds` | `MGLTileSourceOptionCoordinateBounds` `tileSize` | — | `MGLTileSourceOptionTileSize` `attribution` | `attribution` | `MGLTileSourceOptionAttributionHTMLString` (but consider specifying `MGLTileSourceOptionAttributionInfos` instead for improved security) `scheme` | `scheme` | `MGLTileSourceOptionTileCoordinateSystem` diff --git a/platform/darwin/src/MGLTileSource.h b/platform/darwin/src/MGLTileSource.h index caeafcd2f6..3dc268db60 100644 --- a/platform/darwin/src/MGLTileSource.h +++ b/platform/darwin/src/MGLTileSource.h @@ -41,6 +41,20 @@ extern MGL_EXPORT const MGLTileSourceOption MGLTileSourceOptionMinimumZoomLevel; */ extern MGL_EXPORT const MGLTileSourceOption MGLTileSourceOptionMaximumZoomLevel; +/** + An `NSValue` object containing an `MGLCoordinateBounds` struct that specifies + the geographic extent of the source. + + If this option is specified, the SDK avoids requesting any tile that falls + outside of the coordinate bounds. Otherwise, the SDK requests any tile needed + to cover the viewport, as it does by default. + + This option corresponds to the `bounds` key in the + TileJSON + specification. + */ +extern MGL_EXPORT const MGLTileSourceOption MGLTileSourceOptionCoordinateBounds; + #if TARGET_OS_IPHONE /** An HTML string defining the buttons to be displayed in an action sheet when the diff --git a/platform/darwin/src/MGLTileSource.mm b/platform/darwin/src/MGLTileSource.mm index 5644ad9a06..bc985bd728 100644 --- a/platform/darwin/src/MGLTileSource.mm +++ b/platform/darwin/src/MGLTileSource.mm @@ -1,7 +1,9 @@ #import "MGLTileSource_Private.h" #import "MGLAttributionInfo_Private.h" +#import "MGLGeometry_Private.h" #import "NSString+MGLAdditions.h" +#import "NSValue+MGLAdditions.h" #if TARGET_OS_IPHONE #import @@ -13,6 +15,7 @@ const MGLTileSourceOption MGLTileSourceOptionMinimumZoomLevel = @"MGLTileSourceOptionMinimumZoomLevel"; const MGLTileSourceOption MGLTileSourceOptionMaximumZoomLevel = @"MGLTileSourceOptionMaximumZoomLevel"; +const MGLTileSourceOption MGLTileSourceOptionCoordinateBounds = @"MGLTileSourceOptionCoordinateBounds"; const MGLTileSourceOption MGLTileSourceOptionAttributionHTMLString = @"MGLTileSourceOptionAttributionHTMLString"; const MGLTileSourceOption MGLTileSourceOptionAttributionInfos = @"MGLTileSourceOptionAttributionInfos"; const MGLTileSourceOption MGLTileSourceOptionTileCoordinateSystem = @"MGLTileSourceOptionTileCoordinateSystem"; @@ -70,6 +73,15 @@ mbgl::Tileset MGLTileSetFromTileURLTemplates(NS_ARRAY_OF(NSString *) *tileURLTem [NSException raise:NSInvalidArgumentException format:@"MGLTileSourceOptionMinimumZoomLevel must be less than MGLTileSourceOptionMaximumZoomLevel."]; } + + if (NSValue *coordinateBounds = options[MGLTileSourceOptionCoordinateBounds]) { + if (![coordinateBounds isKindOfClass:[NSValue class]] + && strcmp(coordinateBounds.objCType, @encode(MGLCoordinateBounds)) == 0) { + [NSException raise:NSInvalidArgumentException + format:@"MGLTileSourceOptionCoordinateBounds must be set to an NSValue containing an MGLCoordinateBounds."]; + } + tileSet.bounds = MGLLatLngBoundsFromCoordinateBounds(coordinateBounds.MGLCoordinateBoundsValue); + } if (NSString *attribution = options[MGLTileSourceOptionAttributionHTMLString]) { if (![attribution isKindOfClass:[NSString class]]) { diff --git a/platform/darwin/test/MGLTileSetTests.mm b/platform/darwin/test/MGLTileSetTests.mm index 40eab5f974..4d5e1fcd05 100644 --- a/platform/darwin/test/MGLTileSetTests.mm +++ b/platform/darwin/test/MGLTileSetTests.mm @@ -2,6 +2,7 @@ #import #import "MGLTileSource_Private.h" +#import "MGLGeometry_Private.h" #include @@ -40,6 +41,19 @@ XCTAssertEqual(tileSet.zoomRange.min, 1); XCTAssertEqual(tileSet.zoomRange.max, 2); + // when the tile set has a bounds set + MGLCoordinateBounds bounds = MGLCoordinateBoundsMake(CLLocationCoordinate2DMake(12, 34), CLLocationCoordinate2DMake(56, 78)); + tileSet = MGLTileSetFromTileURLTemplates(@[@"tile.1"], @{ + MGLTileSourceOptionCoordinateBounds: @(bounds), + }); + + // the mbgl object reflects the set values for the bounds + XCTAssert(!!tileSet.bounds, @"The bounds are set after setting the bounds"); + if (tileSet.bounds) { + MGLCoordinateBounds actual = MGLCoordinateBoundsFromLatLngBounds(*tileSet.bounds); + XCTAssert(MGLCoordinateBoundsEqualToCoordinateBounds(bounds, actual), @"The bounds round-trip"); + } + // when the tile set has an attribution NSString *attribution = @"my tileset © ©️🎈"; tileSet = MGLTileSetFromTileURLTemplates(tileURLTemplates, @{ diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 292075f0c0..7eb3fd8bf9 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -2,7 +2,11 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONTRIBUTING.md](../../CONTRIBUTING.md) to get started. -## 3.7.3 +## 3.7.4 + +* Added the `MGLTileSourceOptionTileCoordinateBounds` option to create an `MGLTileSource` that only supplies tiles within a specific geographic bounding box. ([#11141](https://github.com/mapbox/mapbox-gl-native/pull/11141)) + +## 3.7.3 - January 10, 2018 * Fixed a crash while zooming while annotations are present on the map. ([#10791](https://github.com/mapbox/mapbox-gl-native/pull/10791)) * CJK characters can be displayed in a locally installed font or a custom font bundled with the application, reducing map download times. Specify the font name using the `MGLIdeographicFontFamilyName` key in the application’s Info.plist file. ([#10522](https://github.com/mapbox/mapbox-gl-native/pull/10522)) diff --git a/platform/ios/docs/guides/For Style Authors.md b/platform/ios/docs/guides/For Style Authors.md index 7eabfed777..ebf4eb23d6 100644 --- a/platform/ios/docs/guides/For Style Authors.md +++ b/platform/ios/docs/guides/For Style Authors.md @@ -109,6 +109,7 @@ the following terms for concepts defined in the style specification: In the style specification | In the SDK ---------------------------|--------- +bounds | coordinate bounds filter | predicate function type | interpolation mode id | identifier @@ -149,6 +150,7 @@ In style JSON | In TileJSON | In the SDK `tiles` | `tiles` | `tileURLTemplates` parameter in `-[MGLTileSource initWithIdentifier:tileURLTemplates:options:]` `minzoom` | `minzoom` | `MGLTileSourceOptionMinimumZoomLevel` `maxzoom` | `maxzoom` | `MGLTileSourceOptionMaximumZoomLevel` +`bounds` | `bounds` | `MGLTileSourceOptionCoordinateBounds` `tileSize` | — | `MGLTileSourceOptionTileSize` `attribution` | `attribution` | `MGLTileSourceOptionAttributionHTMLString` (but consider specifying `MGLTileSourceOptionAttributionInfos` instead for improved security) `scheme` | `scheme` | `MGLTileSourceOptionTileCoordinateSystem` diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index 7a2c47cede..24abafdaaf 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog for Mapbox Maps SDK for macOS +## v0.6.2 + +* Added the `MGLTileSourceOptionTileCoordinateBounds` option to create an `MGLTileSource` that only supplies tiles within a specific geographic bounding box. ([#11141](https://github.com/mapbox/mapbox-gl-native/pull/11141)) + ## v0.6.1 - January 16, 2018 This version of the Mapbox macOS SDK corresponds to version 3.7.3 of the Mapbox Maps SDK for iOS. diff --git a/platform/macos/docs/guides/For Style Authors.md b/platform/macos/docs/guides/For Style Authors.md index 3cacc81376..d1b08f74a9 100644 --- a/platform/macos/docs/guides/For Style Authors.md +++ b/platform/macos/docs/guides/For Style Authors.md @@ -96,6 +96,7 @@ the following terms for concepts defined in the style specification: In the style specification | In the SDK ---------------------------|--------- +bounds | coordinate bounds filter | predicate function type | interpolation mode id | identifier @@ -136,6 +137,7 @@ In style JSON | In TileJSON | In the SDK `tiles` | `tiles` | `tileURLTemplates` parameter in `-[MGLTileSource initWithIdentifier:tileURLTemplates:options:]` `minzoom` | `minzoom` | `MGLTileSourceOptionMinimumZoomLevel` `maxzoom` | `maxzoom` | `MGLTileSourceOptionMaximumZoomLevel` +`bounds` | `bounds` | `MGLTileSourceOptionCoordinateBounds` `tileSize` | — | `MGLTileSourceOptionTileSize` `attribution` | `attribution` | `MGLTileSourceOptionAttributionHTMLString` (but consider specifying `MGLTileSourceOptionAttributionInfos` instead for improved security) `scheme` | `scheme` | `MGLTileSourceOptionTileCoordinateSystem` -- cgit v1.2.1 From 5a799117ec580155b9192dbb3a1df6406f57f175 Mon Sep 17 00:00:00 2001 From: tobrun Date: Tue, 6 Feb 2018 14:42:25 +0100 Subject: [android] - add optional location provider lost to proguard configuration --- platform/android/MapboxGLAndroidSDK/proguard-rules.pro | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/platform/android/MapboxGLAndroidSDK/proguard-rules.pro b/platform/android/MapboxGLAndroidSDK/proguard-rules.pro index b5a1d82c81..3b8adac5a8 100644 --- a/platform/android/MapboxGLAndroidSDK/proguard-rules.pro +++ b/platform/android/MapboxGLAndroidSDK/proguard-rules.pro @@ -12,4 +12,7 @@ # config for okhttp 3.8.0, https://github.com/square/okhttp/pull/3354 -dontwarn okio.** -dontwarn javax.annotation.Nullable --dontwarn javax.annotation.ParametersAreNonnullByDefault \ No newline at end of file +-dontwarn javax.annotation.ParametersAreNonnullByDefault + +# config for optional location provider https://github.com/mapbox/mapbox-gl-native/issues/10960 +-dontwarn com.mapzen.android.lost.api** \ No newline at end of file -- cgit v1.2.1 From cab2086e1c74eeb1fe23c2d4a5e03776be1592b9 Mon Sep 17 00:00:00 2001 From: tobrun Date: Thu, 8 Feb 2018 13:37:37 +0100 Subject: [android] - don't recreate surface as part of view resize --- .../maps/renderer/textureview/TextureViewRenderThread.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java index c34833e9ce..1e76ffe3fb 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java @@ -4,7 +4,6 @@ import android.graphics.SurfaceTexture; import android.support.annotation.NonNull; import android.support.annotation.UiThread; import android.view.TextureView; - import com.mapbox.mapboxsdk.maps.renderer.egl.EGLConfigChooser; import java.lang.ref.WeakReference; @@ -219,13 +218,6 @@ class TextureViewRenderThread extends Thread implements TextureView.SurfaceTextu break; } - // Check if the size has changed - if (sizeChanged) { - recreateSurface = true; - sizeChanged = false; - break; - } - // Reset the request render flag now, so we can catch new requests // while rendering requestRender = false; @@ -273,6 +265,12 @@ class TextureViewRenderThread extends Thread implements TextureView.SurfaceTextu continue; } + if (sizeChanged) { + mapRenderer.onSurfaceChanged(gl, w, h); + sizeChanged = false; + continue; + } + // Don't continue without a surface if (eglHolder.eglSurface == EGL10.EGL_NO_SURFACE) { continue; -- cgit v1.2.1 From fb65d258314cef797afacad9359f8ce1ae461a94 Mon Sep 17 00:00:00 2001 From: "Thiago Marcos P. Santos" Date: Wed, 24 Jan 2018 22:25:29 +0200 Subject: [core] Make RendererObserver a public interface Needed by backends implementing asyncronous rendering --- cmake/core-files.cmake | 2 +- include/mbgl/renderer/renderer_observer.hpp | 35 +++++++++++++++++++++++++++++ src/mbgl/renderer/renderer_observer.hpp | 35 ----------------------------- 3 files changed, 36 insertions(+), 36 deletions(-) create mode 100644 include/mbgl/renderer/renderer_observer.hpp delete mode 100644 src/mbgl/renderer/renderer_observer.hpp diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake index 24d5262799..a0f75a6a59 100644 --- a/cmake/core-files.cmake +++ b/cmake/core-files.cmake @@ -170,6 +170,7 @@ set(MBGL_CORE_FILES include/mbgl/renderer/renderer.hpp include/mbgl/renderer/renderer_backend.hpp include/mbgl/renderer/renderer_frontend.hpp + include/mbgl/renderer/renderer_observer.hpp src/mbgl/renderer/backend_scope.cpp src/mbgl/renderer/bucket.hpp src/mbgl/renderer/bucket_parameters.cpp @@ -206,7 +207,6 @@ set(MBGL_CORE_FILES src/mbgl/renderer/renderer_backend.cpp src/mbgl/renderer/renderer_impl.cpp src/mbgl/renderer/renderer_impl.hpp - src/mbgl/renderer/renderer_observer.hpp src/mbgl/renderer/style_diff.cpp src/mbgl/renderer/style_diff.hpp src/mbgl/renderer/tile_mask.hpp diff --git a/include/mbgl/renderer/renderer_observer.hpp b/include/mbgl/renderer/renderer_observer.hpp new file mode 100644 index 0000000000..551b5c803e --- /dev/null +++ b/include/mbgl/renderer/renderer_observer.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include + +namespace mbgl { + +class RendererObserver { +public: + virtual ~RendererObserver() = default; + + enum class RenderMode : uint32_t { + Partial, + Full + }; + + // Signals that a repaint is required + virtual void onInvalidate() {} + + // Resource failed to download / parse + virtual void onResourceError(std::exception_ptr) {} + + // First frame + virtual void onWillStartRenderingMap() {} + + // Start of frame, initial is the first frame for this map + virtual void onWillStartRenderingFrame() {} + + // End of frame, boolean flags that a repaint is required + virtual void onDidFinishRenderingFrame(RenderMode, bool) {} + + // Final frame + virtual void onDidFinishRenderingMap() {} +}; + +} // namespace mbgl diff --git a/src/mbgl/renderer/renderer_observer.hpp b/src/mbgl/renderer/renderer_observer.hpp deleted file mode 100644 index 551b5c803e..0000000000 --- a/src/mbgl/renderer/renderer_observer.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include - -namespace mbgl { - -class RendererObserver { -public: - virtual ~RendererObserver() = default; - - enum class RenderMode : uint32_t { - Partial, - Full - }; - - // Signals that a repaint is required - virtual void onInvalidate() {} - - // Resource failed to download / parse - virtual void onResourceError(std::exception_ptr) {} - - // First frame - virtual void onWillStartRenderingMap() {} - - // Start of frame, initial is the first frame for this map - virtual void onWillStartRenderingFrame() {} - - // End of frame, boolean flags that a repaint is required - virtual void onDidFinishRenderingFrame(RenderMode, bool) {} - - // Final frame - virtual void onDidFinishRenderingMap() {} -}; - -} // namespace mbgl -- cgit v1.2.1 From 18cf796b527e4014599c380e5a43a37a6a11818d Mon Sep 17 00:00:00 2001 From: "Thiago Marcos P. Santos" Date: Wed, 24 Jan 2018 21:53:48 +0200 Subject: [qt] Removed unused call --- platform/qt/app/mapwindow.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/platform/qt/app/mapwindow.cpp b/platform/qt/app/mapwindow.cpp index fc346cdcde..89047bd948 100644 --- a/platform/qt/app/mapwindow.cpp +++ b/platform/qt/app/mapwindow.cpp @@ -443,9 +443,5 @@ void MapWindow::paintGL() { m_frameDraws++; m_map->resize(size(), size() * pixelRatio()); -#if QT_VERSION >= 0x050400 - // When we're using QOpenGLWidget, we need to tell Mapbox GL about the framebuffer we're using. - m_map->setFramebufferObject(defaultFramebufferObject()); -#endif m_map->render(); } -- cgit v1.2.1 From e7e8cb4547aff3d7fe2e9c1fb04e82bfb2a7dc47 Mon Sep 17 00:00:00 2001 From: "Thiago Marcos P. Santos" Date: Wed, 17 Jan 2018 18:13:12 +0200 Subject: [qt] Render asynchronously --- platform/qt/qt.cmake | 8 +- platform/qt/src/qmapboxgl.cpp | 212 +++++----------------- platform/qt/src/qmapboxgl_map_observer.cpp | 95 ++++++++++ platform/qt/src/qmapboxgl_map_observer.hpp | 45 +++++ platform/qt/src/qmapboxgl_map_renderer.cpp | 43 +++++ platform/qt/src/qmapboxgl_map_renderer.hpp | 42 +++++ platform/qt/src/qmapboxgl_p.hpp | 57 ++---- platform/qt/src/qmapboxgl_renderer_backend.cpp | 40 ++++ platform/qt/src/qmapboxgl_renderer_backend.hpp | 33 ++++ platform/qt/src/qmapboxgl_renderer_frontend_p.cpp | 37 ---- platform/qt/src/qmapboxgl_renderer_frontend_p.hpp | 35 ---- platform/qt/src/qt_logging.cpp | 0 12 files changed, 360 insertions(+), 287 deletions(-) create mode 100644 platform/qt/src/qmapboxgl_map_observer.cpp create mode 100644 platform/qt/src/qmapboxgl_map_observer.hpp create mode 100644 platform/qt/src/qmapboxgl_map_renderer.cpp create mode 100644 platform/qt/src/qmapboxgl_map_renderer.hpp create mode 100644 platform/qt/src/qmapboxgl_renderer_backend.cpp create mode 100644 platform/qt/src/qmapboxgl_renderer_backend.hpp delete mode 100644 platform/qt/src/qmapboxgl_renderer_frontend_p.cpp delete mode 100644 platform/qt/src/qmapboxgl_renderer_frontend_p.hpp mode change 100755 => 100644 platform/qt/src/qt_logging.cpp diff --git a/platform/qt/qt.cmake b/platform/qt/qt.cmake index aaae199650..d1d597bda6 100644 --- a/platform/qt/qt.cmake +++ b/platform/qt/qt.cmake @@ -61,8 +61,12 @@ add_library(qmapboxgl SHARED platform/qt/src/qmapbox.cpp platform/qt/src/qmapboxgl.cpp platform/qt/src/qmapboxgl_p.hpp - platform/qt/src/qmapboxgl_renderer_frontend_p.hpp - platform/qt/src/qmapboxgl_renderer_frontend_p.cpp + platform/qt/src/qmapboxgl_map_observer.cpp + platform/qt/src/qmapboxgl_map_observer.hpp + platform/qt/src/qmapboxgl_map_renderer.cpp + platform/qt/src/qmapboxgl_map_renderer.hpp + platform/qt/src/qmapboxgl_renderer_backend.hpp + platform/qt/src/qmapboxgl_renderer_backend.cpp platform/default/mbgl/util/default_styles.hpp ) diff --git a/platform/qt/src/qmapboxgl.cpp b/platform/qt/src/qmapboxgl.cpp index 2675d87862..af94eb9f71 100644 --- a/platform/qt/src/qmapboxgl.cpp +++ b/platform/qt/src/qmapboxgl.cpp @@ -1,6 +1,7 @@ #include "qmapboxgl.hpp" #include "qmapboxgl_p.hpp" -#include "qmapboxgl_renderer_frontend_p.hpp" + +#include "qmapboxgl_map_observer.hpp" #include "qt_conversion.hpp" #include "qt_geojson.hpp" @@ -43,11 +44,8 @@ #if QT_VERSION >= 0x050000 #include -#include -#include #else #include -#include #endif #include @@ -1058,22 +1056,24 @@ void QMapboxGL::rotateBy(const QPointF &first, const QPointF &second) map->resize(size / 2, size); \endcode */ -void QMapboxGL::resize(const QSize& size, const QSize& framebufferSize) +void QMapboxGL::resize(const QSize& size_, const QSize& framebufferSize) { - if (d_ptr->size == size && d_ptr->fbSize == framebufferSize) return; + auto size = sanitizedSize(size_); - d_ptr->size = size; - d_ptr->fbSize = framebufferSize; + if (d_ptr->mapObj->getSize() == size) + return; - d_ptr->mapObj->setSize(sanitizedSize(size)); + d_ptr->mapRenderer->updateFramebufferSize(sanitizedSize(framebufferSize)); + d_ptr->mapObj->setSize(size); } /*! If Mapbox GL needs to rebind the default \a fbo, it will use the ID supplied here. */ -void QMapboxGL::setFramebufferObject(quint32 fbo) { - d_ptr->fbObject = fbo; +void QMapboxGL::setFramebufferObject(quint32) +{ + // FIXME: No-op, implicit. } /*! @@ -1486,8 +1486,7 @@ void QMapboxGL::render() } #endif - d_ptr->dirty = false; - d_ptr->render(); + d_ptr->mapRenderer->render(); } /*! @@ -1496,7 +1495,7 @@ void QMapboxGL::render() */ void QMapboxGL::connectionEstablished() { - d_ptr->connectionEstablished(); + mbgl::NetworkStatus::Reachable(); } /*! @@ -1526,11 +1525,8 @@ void QMapboxGL::connectionEstablished() \a copyrightsHtml is a string with a HTML snippet. */ -class QMapboxGLRendererFrontend; - -QMapboxGLPrivate::QMapboxGLPrivate(QMapboxGL *q, const QMapboxGLSettings &settings, const QSize &size_, qreal pixelRatio) +QMapboxGLPrivate::QMapboxGLPrivate(QMapboxGL *q, const QMapboxGLSettings &settings, const QSize &size, qreal pixelRatio) : QObject(q) - , size(size_) , q_ptr(q) , fileSourceObj(sharedDefaultFileSource( settings.cacheDatabasePath().toStdString(), @@ -1538,174 +1534,48 @@ QMapboxGLPrivate::QMapboxGLPrivate(QMapboxGL *q, const QMapboxGLSettings &settin settings.cacheDatabaseMaximumSize())) , threadPool(mbgl::sharedThreadPool()) { - // Setup resource transform if needed + // Setup the FileSource + fileSourceObj->setAccessToken(settings.accessToken().toStdString()); + fileSourceObj->setAPIBaseURL(settings.apiBaseUrl().toStdString()); + if (settings.resourceTransform()) { - m_resourceTransform = - std::make_unique< mbgl::Actor >( *mbgl::Scheduler::GetCurrent(), - [callback = settings.resourceTransform()] - (mbgl::Resource::Kind , const std::string&& url_) -> std::string { - return callback(std::move(url_)); - } - ); - fileSourceObj->setResourceTransform(m_resourceTransform->self()); + m_resourceTransform = std::make_unique>(*mbgl::Scheduler::GetCurrent(), + [callback = settings.resourceTransform()] (mbgl::Resource::Kind, const std::string &&url_) -> std::string { + return callback(std::move(url_)); + }); + fileSourceObj->setResourceTransform(m_resourceTransform->self()); } - // Setup and connect the renderer frontend - frontend = std::make_unique( - std::make_unique(*this, pixelRatio, *fileSourceObj, *threadPool, - static_cast(settings.contextMode())), - *this); - connect(frontend.get(), SIGNAL(updated()), this, SLOT(invalidate())); + // Setup MapObserver + mapObserver = std::make_unique(this); + + qRegisterMetaType("QMapboxGL::MapChange"); + + connect(mapObserver.get(), SIGNAL(mapChanged(QMapboxGL::MapChange)), q_ptr, SIGNAL(mapChanged(QMapboxGL::MapChange))); + connect(mapObserver.get(), SIGNAL(copyrightsChanged(QString)), q_ptr, SIGNAL(copyrightsChanged(QString))); + + // Setup RendererBackend + mapRenderer = std::make_unique(pixelRatio, *fileSourceObj, *threadPool, settings.contextMode()); + // Setup the Map object mapObj = std::make_unique( - *frontend, - *this, sanitizedSize(size), + *this, // RendererFrontend + *mapObserver, + sanitizedSize(size), pixelRatio, *fileSourceObj, *threadPool, mbgl::MapMode::Continuous, static_cast(settings.constrainMode()), static_cast(settings.viewportMode())); - qRegisterMetaType("QMapboxGL::MapChange"); - - fileSourceObj->setAccessToken(settings.accessToken().toStdString()); - fileSourceObj->setAPIBaseURL(settings.apiBaseUrl().toStdString()); - - connect(this, SIGNAL(needsRendering()), q_ptr, SIGNAL(needsRendering()), Qt::QueuedConnection); - connect(this, SIGNAL(mapChanged(QMapboxGL::MapChange)), q_ptr, SIGNAL(mapChanged(QMapboxGL::MapChange)), Qt::QueuedConnection); - connect(this, SIGNAL(copyrightsChanged(QString)), q_ptr, SIGNAL(copyrightsChanged(QString)), Qt::QueuedConnection); + connect(this, SIGNAL(needsRendering()), q_ptr, SIGNAL(needsRendering())); } QMapboxGLPrivate::~QMapboxGLPrivate() { } -mbgl::Size QMapboxGLPrivate::getFramebufferSize() const { - return sanitizedSize(fbSize); -} - -void QMapboxGLPrivate::updateAssumedState() { - assumeFramebufferBinding(fbObject); -#if QT_VERSION >= 0x050600 - assumeViewport(0, 0, getFramebufferSize()); -#endif -} - -void QMapboxGLPrivate::bind() { - setFramebufferBinding(fbObject); - setViewport(0, 0, getFramebufferSize()); -} - -void QMapboxGLPrivate::invalidate() -{ - if (!dirty) { - emit needsRendering(); - dirty = true; - } -} - -void QMapboxGLPrivate::render() -{ - frontend->render(); -} - -void QMapboxGLPrivate::onCameraWillChange(mbgl::MapObserver::CameraChangeMode mode) -{ - if (mode == mbgl::MapObserver::CameraChangeMode::Immediate) { - emit mapChanged(QMapboxGL::MapChangeRegionWillChange); - } else { - emit mapChanged(QMapboxGL::MapChangeRegionWillChangeAnimated); - } -} - -void QMapboxGLPrivate::onCameraIsChanging() -{ - emit mapChanged(QMapboxGL::MapChangeRegionIsChanging); -} - -void QMapboxGLPrivate::onCameraDidChange(mbgl::MapObserver::CameraChangeMode mode) -{ - if (mode == mbgl::MapObserver::CameraChangeMode::Immediate) { - emit mapChanged(QMapboxGL::MapChangeRegionDidChange); - } else { - emit mapChanged(QMapboxGL::MapChangeRegionDidChangeAnimated); - } -} - -void QMapboxGLPrivate::onWillStartLoadingMap() -{ - emit mapChanged(QMapboxGL::MapChangeWillStartLoadingMap); -} - -void QMapboxGLPrivate::onDidFinishLoadingMap() -{ - emit mapChanged(QMapboxGL::MapChangeDidFinishLoadingMap); -} - -void QMapboxGLPrivate::onDidFailLoadingMap(std::exception_ptr) -{ - emit mapChanged(QMapboxGL::MapChangeDidFailLoadingMap); -} - -void QMapboxGLPrivate::onWillStartRenderingFrame() -{ - emit mapChanged(QMapboxGL::MapChangeWillStartRenderingFrame); -} - -void QMapboxGLPrivate::onDidFinishRenderingFrame(mbgl::MapObserver::RenderMode mode) -{ - if (mode == mbgl::MapObserver::RenderMode::Partial) { - emit mapChanged(QMapboxGL::MapChangeDidFinishRenderingFrame); - } else { - emit mapChanged(QMapboxGL::MapChangeDidFinishRenderingFrameFullyRendered); - } -} - -void QMapboxGLPrivate::onWillStartRenderingMap() -{ - emit mapChanged(QMapboxGL::MapChangeWillStartLoadingMap); -} - -void QMapboxGLPrivate::onDidFinishRenderingMap(mbgl::MapObserver::RenderMode mode) -{ - if (mode == mbgl::MapObserver::RenderMode::Partial) { - emit mapChanged(QMapboxGL::MapChangeDidFinishRenderingMap); - } else { - emit mapChanged(QMapboxGL::MapChangeDidFinishRenderingMapFullyRendered); - } -} - -void QMapboxGLPrivate::onDidFinishLoadingStyle() -{ - emit mapChanged(QMapboxGL::MapChangeDidFinishLoadingStyle); -} - -void QMapboxGLPrivate::onSourceChanged(mbgl::style::Source&) -{ - std::string attribution; - for (const auto& source : mapObj->getStyle().getSources()) { - // Avoid duplicates by using the most complete attribution HTML snippet. - if (source->getAttribution() && (attribution.size() < source->getAttribution()->size())) - attribution = *source->getAttribution(); - } - emit copyrightsChanged(QString::fromStdString(attribution)); - emit mapChanged(QMapboxGL::MapChangeSourceDidChange); -} - -/*! - Initializes an OpenGL extension function such as Vertex Array Objects (VAOs), - required by Mapbox GL Native engine. -*/ -mbgl::gl::ProcAddress QMapboxGLPrivate::getExtensionFunctionPointer(const char* name) { -#if QT_VERSION >= 0x050000 - QOpenGLContext* thisContext = QOpenGLContext::currentContext(); - return thisContext->getProcAddress(name); -#else - const QGLContext* thisContext = QGLContext::currentContext(); - return reinterpret_cast(thisContext->getProcAddress(name)); -#endif -} - -void QMapboxGLPrivate::connectionEstablished() +void QMapboxGLPrivate::update(std::shared_ptr parameters) { - mbgl::NetworkStatus::Reachable(); + mapRenderer->updateParameters(std::move(parameters)); + emit needsRendering(); } diff --git a/platform/qt/src/qmapboxgl_map_observer.cpp b/platform/qt/src/qmapboxgl_map_observer.cpp new file mode 100644 index 0000000000..e180ed8fda --- /dev/null +++ b/platform/qt/src/qmapboxgl_map_observer.cpp @@ -0,0 +1,95 @@ +#include "qmapboxgl_map_observer.hpp" + +#include "qmapboxgl_p.hpp" + +QMapboxGLMapObserver::QMapboxGLMapObserver(QMapboxGLPrivate *d) + : d_ptr(d) +{ +} + +QMapboxGLMapObserver::~QMapboxGLMapObserver() +{ +} + +void QMapboxGLMapObserver::onCameraWillChange(mbgl::MapObserver::CameraChangeMode mode) +{ + if (mode == mbgl::MapObserver::CameraChangeMode::Immediate) { + emit mapChanged(QMapboxGL::MapChangeRegionWillChange); + } else { + emit mapChanged(QMapboxGL::MapChangeRegionWillChangeAnimated); + } +} + +void QMapboxGLMapObserver::onCameraIsChanging() +{ + emit mapChanged(QMapboxGL::MapChangeRegionIsChanging); +} + +void QMapboxGLMapObserver::onCameraDidChange(mbgl::MapObserver::CameraChangeMode mode) +{ + if (mode == mbgl::MapObserver::CameraChangeMode::Immediate) { + emit mapChanged(QMapboxGL::MapChangeRegionDidChange); + } else { + emit mapChanged(QMapboxGL::MapChangeRegionDidChangeAnimated); + } +} + +void QMapboxGLMapObserver::onWillStartLoadingMap() +{ + emit mapChanged(QMapboxGL::MapChangeWillStartLoadingMap); +} + +void QMapboxGLMapObserver::onDidFinishLoadingMap() +{ + emit mapChanged(QMapboxGL::MapChangeDidFinishLoadingMap); +} + +void QMapboxGLMapObserver::onDidFailLoadingMap(std::exception_ptr) +{ + emit mapChanged(QMapboxGL::MapChangeDidFailLoadingMap); +} + +void QMapboxGLMapObserver::onWillStartRenderingFrame() +{ + emit mapChanged(QMapboxGL::MapChangeWillStartRenderingFrame); +} + +void QMapboxGLMapObserver::onDidFinishRenderingFrame(mbgl::MapObserver::RenderMode mode) +{ + if (mode == mbgl::MapObserver::RenderMode::Partial) { + emit mapChanged(QMapboxGL::MapChangeDidFinishRenderingFrame); + } else { + emit mapChanged(QMapboxGL::MapChangeDidFinishRenderingFrameFullyRendered); + } +} + +void QMapboxGLMapObserver::onWillStartRenderingMap() +{ + emit mapChanged(QMapboxGL::MapChangeWillStartLoadingMap); +} + +void QMapboxGLMapObserver::onDidFinishRenderingMap(mbgl::MapObserver::RenderMode mode) +{ + if (mode == mbgl::MapObserver::RenderMode::Partial) { + emit mapChanged(QMapboxGL::MapChangeDidFinishRenderingMap); + } else { + emit mapChanged(QMapboxGL::MapChangeDidFinishRenderingMapFullyRendered); + } +} + +void QMapboxGLMapObserver::onDidFinishLoadingStyle() +{ + emit mapChanged(QMapboxGL::MapChangeDidFinishLoadingStyle); +} + +void QMapboxGLMapObserver::onSourceChanged(mbgl::style::Source&) +{ + std::string attribution; + for (const auto& source : d_ptr->mapObj->getStyle().getSources()) { + // Avoid duplicates by using the most complete attribution HTML snippet. + if (source->getAttribution() && (attribution.size() < source->getAttribution()->size())) + attribution = *source->getAttribution(); + } + emit copyrightsChanged(QString::fromStdString(attribution)); + emit mapChanged(QMapboxGL::MapChangeSourceDidChange); +} diff --git a/platform/qt/src/qmapboxgl_map_observer.hpp b/platform/qt/src/qmapboxgl_map_observer.hpp new file mode 100644 index 0000000000..c9d0581a90 --- /dev/null +++ b/platform/qt/src/qmapboxgl_map_observer.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include "qmapboxgl.hpp" + +#include +#include + +#include + +#include +#include + +class QMapboxGLPrivate; + +class QMapboxGLMapObserver : public QObject, public mbgl::MapObserver +{ + Q_OBJECT + +public: + explicit QMapboxGLMapObserver(QMapboxGLPrivate *); + virtual ~QMapboxGLMapObserver(); + + // mbgl::MapObserver implementation. + void onCameraWillChange(mbgl::MapObserver::CameraChangeMode) final; + void onCameraIsChanging() final; + void onCameraDidChange(mbgl::MapObserver::CameraChangeMode) final; + void onWillStartLoadingMap() final; + void onDidFinishLoadingMap() final; + void onDidFailLoadingMap(std::exception_ptr) final; + void onWillStartRenderingFrame() final; + void onDidFinishRenderingFrame(mbgl::MapObserver::RenderMode) final; + void onWillStartRenderingMap() final; + void onDidFinishRenderingMap(mbgl::MapObserver::RenderMode) final; + void onDidFinishLoadingStyle() final; + void onSourceChanged(mbgl::style::Source&) final; + +signals: + void mapChanged(QMapboxGL::MapChange); + void copyrightsChanged(const QString ©rightsHtml); + +private: + Q_DISABLE_COPY(QMapboxGLMapObserver) + + QMapboxGLPrivate *d_ptr; +}; diff --git a/platform/qt/src/qmapboxgl_map_renderer.cpp b/platform/qt/src/qmapboxgl_map_renderer.cpp new file mode 100644 index 0000000000..c0293c2079 --- /dev/null +++ b/platform/qt/src/qmapboxgl_map_renderer.cpp @@ -0,0 +1,43 @@ +#include "qmapboxgl_map_renderer.hpp" + +#include + +QMapboxGLMapRenderer::QMapboxGLMapRenderer(qreal pixelRatio, + mbgl::DefaultFileSource &fs, mbgl::ThreadPool &tp, QMapboxGLSettings::GLContextMode mode) + : m_renderer(std::make_unique(m_backend, pixelRatio, fs, tp, static_cast(mode))) +{ +} + +QMapboxGLMapRenderer::~QMapboxGLMapRenderer() +{ +} + +void QMapboxGLMapRenderer::updateParameters(std::shared_ptr newParameters) +{ + std::lock_guard lock(m_updateMutex); + m_updateParameters = std::move(newParameters); +} + +void QMapboxGLMapRenderer::updateFramebufferSize(const mbgl::Size &size) +{ + std::lock_guard lock(m_updateMutex); + m_backend.setFramebufferSize(size); +} + +void QMapboxGLMapRenderer::render() +{ + std::shared_ptr params; + { + // Lock on the parameters + std::unique_lock lock(m_updateMutex); + if (!m_updateParameters) return; + + // Hold on to the update parameters during render + params = m_updateParameters; + } + + // The OpenGL implementation automatically enables the OpenGL context for us. + mbgl::BackendScope scope(m_backend, mbgl::BackendScope::ScopeType::Implicit); + + m_renderer->render(*params); +} diff --git a/platform/qt/src/qmapboxgl_map_renderer.hpp b/platform/qt/src/qmapboxgl_map_renderer.hpp new file mode 100644 index 0000000000..d0c70b97ca --- /dev/null +++ b/platform/qt/src/qmapboxgl_map_renderer.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include "qmapboxgl.hpp" +#include "qmapboxgl_renderer_backend.hpp" + +#include +#include +#include +#include + +#include +#include + +namespace mbgl { +class Renderer; +class UpdateParameters; +} // namespace mbgl + +class QMapboxGLRendererBackend; + +class QMapboxGLMapRenderer +{ +public: + QMapboxGLMapRenderer(qreal pixelRatio, mbgl::DefaultFileSource &, + mbgl::ThreadPool &, QMapboxGLSettings::GLContextMode); + virtual ~QMapboxGLMapRenderer(); + + void render(); + + // Thread-safe, called by the Frontend + void updateParameters(std::shared_ptr); + void updateFramebufferSize(const mbgl::Size &size); + +private: + Q_DISABLE_COPY(QMapboxGLMapRenderer) + + std::mutex m_updateMutex; + std::shared_ptr m_updateParameters; + + QMapboxGLRendererBackend m_backend; + std::unique_ptr m_renderer; +}; diff --git a/platform/qt/src/qmapboxgl_p.hpp b/platform/qt/src/qmapboxgl_p.hpp index f947c09f48..55377eb51e 100644 --- a/platform/qt/src/qmapboxgl_p.hpp +++ b/platform/qt/src/qmapboxgl_p.hpp @@ -1,22 +1,23 @@ #pragma once #include "qmapboxgl.hpp" -#include "qmapboxgl_renderer_frontend_p.hpp" +#include "qmapboxgl_map_observer.hpp" +#include "qmapboxgl_map_renderer.hpp" #include #include -#include -#include +#include #include -#include #include +#include +#include #include #include #include -class QMapboxGLPrivate : public QObject, public mbgl::RendererBackend, public mbgl::MapObserver +class QMapboxGLPrivate : public QObject, public mbgl::RendererFrontend { Q_OBJECT @@ -24,55 +25,27 @@ public: explicit QMapboxGLPrivate(QMapboxGL *, const QMapboxGLSettings &, const QSize &size, qreal pixelRatio); virtual ~QMapboxGLPrivate(); - - // mbgl::RendererBackend implementation. - void bind() final; - mbgl::Size getFramebufferSize() const final; - void updateAssumedState() final; - void activate() final {} - void deactivate() final {} - - // mbgl::MapObserver implementation. - void onCameraWillChange(mbgl::MapObserver::CameraChangeMode) final; - void onCameraIsChanging() final; - void onCameraDidChange(mbgl::MapObserver::CameraChangeMode) final; - void onWillStartLoadingMap() final; - void onDidFinishLoadingMap() final; - void onDidFailLoadingMap(std::exception_ptr) final; - void onWillStartRenderingFrame() final; - void onDidFinishRenderingFrame(mbgl::MapObserver::RenderMode) final; - void onWillStartRenderingMap() final; - void onDidFinishRenderingMap(mbgl::MapObserver::RenderMode) final; - void onDidFinishLoadingStyle() final; - void onSourceChanged(mbgl::style::Source&) final; + // mbgl::RendererFrontend implementation. + void reset() final {} + void setObserver(mbgl::RendererObserver &) final {} + void update(std::shared_ptr) final; mbgl::EdgeInsets margins; - QSize size { 0, 0 }; - QSize fbSize { 0, 0 }; - quint32 fbObject = 0; QMapboxGL *q_ptr { nullptr }; std::shared_ptr fileSourceObj; std::shared_ptr threadPool; - std::unique_ptr frontend; std::unique_ptr mapObj; - bool dirty { false }; - -private: - mbgl::gl::ProcAddress getExtensionFunctionPointer(const char*) override; - -public slots: - void connectionEstablished(); - void invalidate(); - void render(); + std::unique_ptr mapObserver; + std::unique_ptr mapRenderer; signals: void needsRendering(); - void mapChanged(QMapboxGL::MapChange); - void copyrightsChanged(const QString ©rightsHtml); private: - std::unique_ptr< mbgl::Actor > m_resourceTransform; + Q_DISABLE_COPY(QMapboxGLPrivate) + + std::unique_ptr> m_resourceTransform; }; diff --git a/platform/qt/src/qmapboxgl_renderer_backend.cpp b/platform/qt/src/qmapboxgl_renderer_backend.cpp new file mode 100644 index 0000000000..6cc7de53fe --- /dev/null +++ b/platform/qt/src/qmapboxgl_renderer_backend.cpp @@ -0,0 +1,40 @@ +#include "qmapboxgl_renderer_backend.hpp" + +#include + +#if QT_VERSION >= 0x050000 +#include +#else +#include +#endif + +void QMapboxGLRendererBackend::updateAssumedState() +{ + assumeFramebufferBinding(ImplicitFramebufferBinding); + assumeViewport(0, 0, { 800, 600 }); +} + +mbgl::Size QMapboxGLRendererBackend::getFramebufferSize() const +{ + return m_size; +} + +void QMapboxGLRendererBackend::setFramebufferSize(const mbgl::Size &size) +{ + m_size = size; +} + +/*! + Initializes an OpenGL extension function such as Vertex Array Objects (VAOs), + required by Mapbox GL Native engine. +*/ +mbgl::gl::ProcAddress QMapboxGLRendererBackend::getExtensionFunctionPointer(const char* name) +{ +#if QT_VERSION >= 0x050000 + QOpenGLContext* thisContext = QOpenGLContext::currentContext(); + return thisContext->getProcAddress(name); +#else + const QGLContext* thisContext = QGLContext::currentContext(); + return reinterpret_cast(thisContext->getProcAddress(name)); +#endif +} diff --git a/platform/qt/src/qmapboxgl_renderer_backend.hpp b/platform/qt/src/qmapboxgl_renderer_backend.hpp new file mode 100644 index 0000000000..fb38556b55 --- /dev/null +++ b/platform/qt/src/qmapboxgl_renderer_backend.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include "qmapboxgl.hpp" + +#include +#include +#include + +class QMapboxGLRendererBackend : public mbgl::RendererBackend +{ +public: + QMapboxGLRendererBackend() = default; + virtual ~QMapboxGLRendererBackend() = default; + + // mbgl::RendererBackend implementation + void updateAssumedState() final; + void bind() final {} + mbgl::Size getFramebufferSize() const final; + + void setFramebufferSize(const mbgl::Size &); + +protected: + mbgl::gl::ProcAddress getExtensionFunctionPointer(const char*) final; + + // No-op, implicit mode. + void activate() final {} + void deactivate() final {} + +private: + mbgl::Size m_size = { 0, 0 }; + + Q_DISABLE_COPY(QMapboxGLRendererBackend) +}; diff --git a/platform/qt/src/qmapboxgl_renderer_frontend_p.cpp b/platform/qt/src/qmapboxgl_renderer_frontend_p.cpp deleted file mode 100644 index ea60851eb4..0000000000 --- a/platform/qt/src/qmapboxgl_renderer_frontend_p.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include "qmapboxgl_renderer_frontend_p.hpp" - -#include -#include - -QMapboxGLRendererFrontend::QMapboxGLRendererFrontend(std::unique_ptr renderer_, mbgl::RendererBackend& backend_) - : renderer(std::move(renderer_)) - , backend(backend_) { -} - -QMapboxGLRendererFrontend::~QMapboxGLRendererFrontend() = default; - -void QMapboxGLRendererFrontend::reset() { - if (renderer) { - renderer.reset(); - } -} - -void QMapboxGLRendererFrontend::update(std::shared_ptr updateParameters_) { - updateParameters = updateParameters_; - emit updated(); -} - -void QMapboxGLRendererFrontend::setObserver(mbgl::RendererObserver& observer_) { - if (!renderer) return; - - renderer->setObserver(&observer_); -} - -void QMapboxGLRendererFrontend::render() { - if (!renderer || !updateParameters) return; - - // The OpenGL implementation automatically enables the OpenGL context for us. - mbgl::BackendScope scope { backend, mbgl::BackendScope::ScopeType::Implicit }; - - renderer->render(*updateParameters); -} diff --git a/platform/qt/src/qmapboxgl_renderer_frontend_p.hpp b/platform/qt/src/qmapboxgl_renderer_frontend_p.hpp deleted file mode 100644 index c5e2bacc34..0000000000 --- a/platform/qt/src/qmapboxgl_renderer_frontend_p.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include -#include - -#include - -namespace mbgl { - class Renderer; -} // namespace mbgl - -class QMapboxGLRendererFrontend : public QObject, public mbgl::RendererFrontend -{ - Q_OBJECT - -public: - explicit QMapboxGLRendererFrontend(std::unique_ptr, mbgl::RendererBackend&); - ~QMapboxGLRendererFrontend() override; - - void reset() override; - void setObserver(mbgl::RendererObserver&) override; - - void update(std::shared_ptr) override; - -public slots: - void render(); - -signals: - void updated(); - -private: - std::unique_ptr renderer; - mbgl::RendererBackend& backend; - std::shared_ptr updateParameters; -}; diff --git a/platform/qt/src/qt_logging.cpp b/platform/qt/src/qt_logging.cpp old mode 100755 new mode 100644 -- cgit v1.2.1 From a062f3e17b6f42252a283b50bd36de3411571b7d Mon Sep 17 00:00:00 2001 From: "Thiago Marcos P. Santos" Date: Wed, 24 Jan 2018 23:17:25 +0200 Subject: [qt] Update map rendering via RendererObserver --- platform/qt/src/qmapboxgl.cpp | 18 ++++++++- platform/qt/src/qmapboxgl_map_renderer.cpp | 5 +++ platform/qt/src/qmapboxgl_map_renderer.hpp | 2 + platform/qt/src/qmapboxgl_p.hpp | 8 +++- platform/qt/src/qmapboxgl_renderer_observer.hpp | 51 +++++++++++++++++++++++++ 5 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 platform/qt/src/qmapboxgl_renderer_observer.hpp diff --git a/platform/qt/src/qmapboxgl.cpp b/platform/qt/src/qmapboxgl.cpp index af94eb9f71..cf6be2c9a7 100644 --- a/platform/qt/src/qmapboxgl.cpp +++ b/platform/qt/src/qmapboxgl.cpp @@ -2,6 +2,7 @@ #include "qmapboxgl_p.hpp" #include "qmapboxgl_map_observer.hpp" +#include "qmapboxgl_renderer_observer.hpp" #include "qt_conversion.hpp" #include "qt_geojson.hpp" @@ -1486,6 +1487,7 @@ void QMapboxGL::render() } #endif + d_ptr->renderQueued.clear(); d_ptr->mapRenderer->render(); } @@ -1567,7 +1569,8 @@ QMapboxGLPrivate::QMapboxGLPrivate(QMapboxGL *q, const QMapboxGLSettings &settin static_cast(settings.constrainMode()), static_cast(settings.viewportMode())); - connect(this, SIGNAL(needsRendering()), q_ptr, SIGNAL(needsRendering())); + // Needs to be Queued to give time to discard redundant draw calls via the `renderQueued` flag. + connect(this, SIGNAL(needsRendering()), q_ptr, SIGNAL(needsRendering()), Qt::QueuedConnection); } QMapboxGLPrivate::~QMapboxGLPrivate() @@ -1577,5 +1580,16 @@ QMapboxGLPrivate::~QMapboxGLPrivate() void QMapboxGLPrivate::update(std::shared_ptr parameters) { mapRenderer->updateParameters(std::move(parameters)); - emit needsRendering(); + + if (!renderQueued.test_and_set()) { + emit needsRendering(); + } +} + +void QMapboxGLPrivate::setObserver(mbgl::RendererObserver &observer) +{ + m_rendererObserver = std::make_shared( + *mbgl::util::RunLoop::Get(), observer); + + mapRenderer->setObserver(m_rendererObserver); } diff --git a/platform/qt/src/qmapboxgl_map_renderer.cpp b/platform/qt/src/qmapboxgl_map_renderer.cpp index c0293c2079..a343d56668 100644 --- a/platform/qt/src/qmapboxgl_map_renderer.cpp +++ b/platform/qt/src/qmapboxgl_map_renderer.cpp @@ -41,3 +41,8 @@ void QMapboxGLMapRenderer::render() m_renderer->render(*params); } + +void QMapboxGLMapRenderer::setObserver(std::shared_ptr observer) +{ + m_renderer->setObserver(observer.get()); +} diff --git a/platform/qt/src/qmapboxgl_map_renderer.hpp b/platform/qt/src/qmapboxgl_map_renderer.hpp index d0c70b97ca..5769432f0f 100644 --- a/platform/qt/src/qmapboxgl_map_renderer.hpp +++ b/platform/qt/src/qmapboxgl_map_renderer.hpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -26,6 +27,7 @@ public: virtual ~QMapboxGLMapRenderer(); void render(); + void setObserver(std::shared_ptr); // Thread-safe, called by the Frontend void updateParameters(std::shared_ptr); diff --git a/platform/qt/src/qmapboxgl_p.hpp b/platform/qt/src/qmapboxgl_p.hpp index 55377eb51e..b9a4b37ae5 100644 --- a/platform/qt/src/qmapboxgl_p.hpp +++ b/platform/qt/src/qmapboxgl_p.hpp @@ -15,6 +15,7 @@ #include #include +#include #include class QMapboxGLPrivate : public QObject, public mbgl::RendererFrontend @@ -27,7 +28,7 @@ public: // mbgl::RendererFrontend implementation. void reset() final {} - void setObserver(mbgl::RendererObserver &) final {} + void setObserver(mbgl::RendererObserver &) final; void update(std::shared_ptr) final; mbgl::EdgeInsets margins; @@ -40,7 +41,12 @@ public: std::unique_ptr mapObserver; std::unique_ptr mapRenderer; + std::shared_ptr rendererObserver; + QMapboxGLSettings::GLContextMode mode; + qreal pixelRatio; + + std::atomic_flag renderQueued = ATOMIC_FLAG_INIT; signals: void needsRendering(); diff --git a/platform/qt/src/qmapboxgl_renderer_observer.hpp b/platform/qt/src/qmapboxgl_renderer_observer.hpp new file mode 100644 index 0000000000..ee340113ff --- /dev/null +++ b/platform/qt/src/qmapboxgl_renderer_observer.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include + +// Forwards RendererObserver signals to the given +// Delegate RendererObserver on the given RunLoop +class QMapboxGLRendererObserver : public mbgl::RendererObserver { +public: + QMapboxGLRendererObserver(mbgl::util::RunLoop& mapRunLoop, mbgl::RendererObserver& delegate_) + : mailbox(std::make_shared(mapRunLoop)) + , delegate(delegate_, mailbox) { + } + + ~QMapboxGLRendererObserver() { + mailbox->close(); + } + + void onInvalidate() final { + delegate.invoke(&mbgl::RendererObserver::onInvalidate); + } + + void onResourceError(std::exception_ptr err) final { + delegate.invoke(&mbgl::RendererObserver::onResourceError, err); + } + + void onWillStartRenderingMap() final { + delegate.invoke(&mbgl::RendererObserver::onWillStartRenderingMap); + } + + void onWillStartRenderingFrame() final { + delegate.invoke(&mbgl::RendererObserver::onWillStartRenderingFrame); + } + + void onDidFinishRenderingFrame(RenderMode mode, bool repaintNeeded) final { + delegate.invoke(&mbgl::RendererObserver::onDidFinishRenderingFrame, mode, repaintNeeded); + } + + void onDidFinishRenderingMap() final { + delegate.invoke(&mbgl::RendererObserver::onDidFinishRenderingMap); + } + +private: + std::shared_ptr mailbox; + mbgl::ActorRef delegate; +}; -- cgit v1.2.1 From bb00f558fb31f000eefa1bb825bb580445cedc10 Mon Sep 17 00:00:00 2001 From: "Thiago Marcos P. Santos" Date: Thu, 25 Jan 2018 02:19:36 +0200 Subject: [qt] Make MapboxGLMapRenderer a scheduler to receive messages when rendering --- platform/qt/src/qmapboxgl_map_renderer.cpp | 26 ++++++++++++++++++++++++-- platform/qt/src/qmapboxgl_map_renderer.hpp | 9 ++++++++- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/platform/qt/src/qmapboxgl_map_renderer.cpp b/platform/qt/src/qmapboxgl_map_renderer.cpp index a343d56668..65fb37f094 100644 --- a/platform/qt/src/qmapboxgl_map_renderer.cpp +++ b/platform/qt/src/qmapboxgl_map_renderer.cpp @@ -12,6 +12,12 @@ QMapboxGLMapRenderer::~QMapboxGLMapRenderer() { } +void QMapboxGLMapRenderer::schedule(std::weak_ptr mailbox) +{ + std::lock_guard lock(m_taskQueueMutex); + m_taskQueue.push(mailbox); +} + void QMapboxGLMapRenderer::updateParameters(std::shared_ptr newParameters) { std::lock_guard lock(m_updateMutex); @@ -29,8 +35,11 @@ void QMapboxGLMapRenderer::render() std::shared_ptr params; { // Lock on the parameters - std::unique_lock lock(m_updateMutex); - if (!m_updateParameters) return; + std::lock_guard lock(m_updateMutex); + + if (!m_updateParameters) { + return; + } // Hold on to the update parameters during render params = m_updateParameters; @@ -39,7 +48,20 @@ void QMapboxGLMapRenderer::render() // The OpenGL implementation automatically enables the OpenGL context for us. mbgl::BackendScope scope(m_backend, mbgl::BackendScope::ScopeType::Implicit); + Scheduler::SetCurrent(this); + m_renderer->render(*params); + + std::queue> taskQueue; + { + std::unique_lock lock(m_taskQueueMutex); + std::swap(taskQueue, m_taskQueue); + } + + while (!taskQueue.empty()) { + mbgl::Mailbox::maybeReceive(taskQueue.front()); + taskQueue.pop(); + } } void QMapboxGLMapRenderer::setObserver(std::shared_ptr observer) diff --git a/platform/qt/src/qmapboxgl_map_renderer.hpp b/platform/qt/src/qmapboxgl_map_renderer.hpp index 5769432f0f..e5ead3df32 100644 --- a/platform/qt/src/qmapboxgl_map_renderer.hpp +++ b/platform/qt/src/qmapboxgl_map_renderer.hpp @@ -11,6 +11,7 @@ #include #include +#include namespace mbgl { class Renderer; @@ -19,13 +20,16 @@ class UpdateParameters; class QMapboxGLRendererBackend; -class QMapboxGLMapRenderer +class QMapboxGLMapRenderer : public mbgl::Scheduler { public: QMapboxGLMapRenderer(qreal pixelRatio, mbgl::DefaultFileSource &, mbgl::ThreadPool &, QMapboxGLSettings::GLContextMode); virtual ~QMapboxGLMapRenderer(); + // mbgl::Scheduler implementation. + void schedule(std::weak_ptr scheduled) final; + void render(); void setObserver(std::shared_ptr); @@ -41,4 +45,7 @@ private: QMapboxGLRendererBackend m_backend; std::unique_ptr m_renderer; + + std::mutex m_taskQueueMutex; + std::queue> m_taskQueue; }; -- cgit v1.2.1 From 4f8a23cd8fe920ca7009e0ca6689d0c5bcb8c7eb Mon Sep 17 00:00:00 2001 From: "Thiago Marcos P. Santos" Date: Thu, 25 Jan 2018 02:20:09 +0200 Subject: [qt] Expose an interface to create a renderer on a separated thread If not called, it will render on the main thread as usual. --- platform/qt/include/qmapboxgl.hpp | 5 +++++ platform/qt/src/qmapboxgl.cpp | 38 ++++++++++++++++++++++++++++++++------ 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/platform/qt/include/qmapboxgl.hpp b/platform/qt/include/qmapboxgl.hpp index 8b319b0453..a1758ba6e6 100644 --- a/platform/qt/include/qmapboxgl.hpp +++ b/platform/qt/include/qmapboxgl.hpp @@ -226,6 +226,11 @@ public: void setFilter(const QString &layer, const QVariant &filter); + // When rendering on a different thread, + // should be called on this thread + void createRenderer(); + void destroyRenderer(); + public slots: void render(); void connectionEstablished(); diff --git a/platform/qt/src/qmapboxgl.cpp b/platform/qt/src/qmapboxgl.cpp index cf6be2c9a7..89904ddf88 100644 --- a/platform/qt/src/qmapboxgl.cpp +++ b/platform/qt/src/qmapboxgl.cpp @@ -1468,6 +1468,23 @@ void QMapboxGL::setFilter(const QString& layer, const QVariant& filter) qWarning() << "Layer doesn't support filters"; } +void QMapboxGL::createRenderer() +{ + d_ptr->mapRenderer = std::make_unique( + d_ptr->pixelRatio, + *d_ptr->fileSourceObj, + *d_ptr->threadPool, + d_ptr->mode + ); + + d_ptr->mapRenderer->setObserver(d_ptr->rendererObserver); +} + +void QMapboxGL::destroyRenderer() +{ + d_ptr->mapRenderer.reset(); +} + /*! Renders the map using OpenGL draw calls. It will make sure to bind the framebuffer object before drawing; otherwise a valid OpenGL context is @@ -1479,6 +1496,10 @@ void QMapboxGL::setFilter(const QString& layer, const QVariant& filter) */ void QMapboxGL::render() { + if (!d_ptr->mapRenderer) { + createRenderer(); + } + #if defined(__APPLE__) && QT_VERSION < 0x050000 // FIXME Qt 4.x provides an incomplete FBO at start. // See https://bugreports.qt.io/browse/QTBUG-36802 for details. @@ -1527,7 +1548,7 @@ void QMapboxGL::connectionEstablished() \a copyrightsHtml is a string with a HTML snippet. */ -QMapboxGLPrivate::QMapboxGLPrivate(QMapboxGL *q, const QMapboxGLSettings &settings, const QSize &size, qreal pixelRatio) +QMapboxGLPrivate::QMapboxGLPrivate(QMapboxGL *q, const QMapboxGLSettings &settings, const QSize &size, qreal pixelRatio_) : QObject(q) , q_ptr(q) , fileSourceObj(sharedDefaultFileSource( @@ -1535,6 +1556,8 @@ QMapboxGLPrivate::QMapboxGLPrivate(QMapboxGL *q, const QMapboxGLSettings &settin settings.assetPath().toStdString(), settings.cacheDatabaseMaximumSize())) , threadPool(mbgl::sharedThreadPool()) + , mode(settings.contextMode()) + , pixelRatio(pixelRatio_) { // Setup the FileSource fileSourceObj->setAccessToken(settings.accessToken().toStdString()); @@ -1556,9 +1579,6 @@ QMapboxGLPrivate::QMapboxGLPrivate(QMapboxGL *q, const QMapboxGLSettings &settin connect(mapObserver.get(), SIGNAL(mapChanged(QMapboxGL::MapChange)), q_ptr, SIGNAL(mapChanged(QMapboxGL::MapChange))); connect(mapObserver.get(), SIGNAL(copyrightsChanged(QString)), q_ptr, SIGNAL(copyrightsChanged(QString))); - // Setup RendererBackend - mapRenderer = std::make_unique(pixelRatio, *fileSourceObj, *threadPool, settings.contextMode()); - // Setup the Map object mapObj = std::make_unique( *this, // RendererFrontend @@ -1579,6 +1599,10 @@ QMapboxGLPrivate::~QMapboxGLPrivate() void QMapboxGLPrivate::update(std::shared_ptr parameters) { + if (!mapRenderer) { + return; + } + mapRenderer->updateParameters(std::move(parameters)); if (!renderQueued.test_and_set()) { @@ -1588,8 +1612,10 @@ void QMapboxGLPrivate::update(std::shared_ptr parameters void QMapboxGLPrivate::setObserver(mbgl::RendererObserver &observer) { - m_rendererObserver = std::make_shared( + rendererObserver = std::make_shared( *mbgl::util::RunLoop::Get(), observer); - mapRenderer->setObserver(m_rendererObserver); + if (mapRenderer) { + mapRenderer->setObserver(rendererObserver); + } } -- cgit v1.2.1 From a4e95c3da322113f8db732133cb9541a38accbaf Mon Sep 17 00:00:00 2001 From: "Thiago Marcos P. Santos" Date: Fri, 26 Jan 2018 00:42:03 +0200 Subject: [qt] Only use the MapRenderer as scheduler if there is no other Optimization of when running on the Main Thread that has a RunLoop. --- platform/qt/src/qmapboxgl_map_renderer.cpp | 26 +++++++++++++++++--------- platform/qt/src/qmapboxgl_map_renderer.hpp | 2 ++ 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/platform/qt/src/qmapboxgl_map_renderer.cpp b/platform/qt/src/qmapboxgl_map_renderer.cpp index 65fb37f094..81293d7da1 100644 --- a/platform/qt/src/qmapboxgl_map_renderer.cpp +++ b/platform/qt/src/qmapboxgl_map_renderer.cpp @@ -5,6 +5,7 @@ QMapboxGLMapRenderer::QMapboxGLMapRenderer(qreal pixelRatio, mbgl::DefaultFileSource &fs, mbgl::ThreadPool &tp, QMapboxGLSettings::GLContextMode mode) : m_renderer(std::make_unique(m_backend, pixelRatio, fs, tp, static_cast(mode))) + , m_threadWithScheduler(Scheduler::GetCurrent() != nullptr) { } @@ -48,19 +49,26 @@ void QMapboxGLMapRenderer::render() // The OpenGL implementation automatically enables the OpenGL context for us. mbgl::BackendScope scope(m_backend, mbgl::BackendScope::ScopeType::Implicit); - Scheduler::SetCurrent(this); + // If we don't have a Scheduler on this thread, which + // is usually the case for render threads, use this + // object as scheduler. + if (!m_threadWithScheduler) { + Scheduler::SetCurrent(this); + } m_renderer->render(*params); - std::queue> taskQueue; - { - std::unique_lock lock(m_taskQueueMutex); - std::swap(taskQueue, m_taskQueue); - } + if (!m_threadWithScheduler) { + std::queue> taskQueue; + { + std::unique_lock lock(m_taskQueueMutex); + std::swap(taskQueue, m_taskQueue); + } - while (!taskQueue.empty()) { - mbgl::Mailbox::maybeReceive(taskQueue.front()); - taskQueue.pop(); + while (!taskQueue.empty()) { + mbgl::Mailbox::maybeReceive(taskQueue.front()); + taskQueue.pop(); + } } } diff --git a/platform/qt/src/qmapboxgl_map_renderer.hpp b/platform/qt/src/qmapboxgl_map_renderer.hpp index e5ead3df32..c15840a85d 100644 --- a/platform/qt/src/qmapboxgl_map_renderer.hpp +++ b/platform/qt/src/qmapboxgl_map_renderer.hpp @@ -48,4 +48,6 @@ private: std::mutex m_taskQueueMutex; std::queue> m_taskQueue; + + bool m_threadWithScheduler; }; -- cgit v1.2.1 From 673730ccd423aed8deeba6889409f02cb1e9071d Mon Sep 17 00:00:00 2001 From: "Thiago Marcos P. Santos" Date: Fri, 26 Jan 2018 18:20:46 +0200 Subject: [qt] Implement FBO handling Needed for rendering 3D extrusions properly. --- platform/qt/app/mapwindow.cpp | 5 +++- platform/qt/include/qmapboxgl.hpp | 6 ++--- platform/qt/src/qmapboxgl.cpp | 37 ++++++++++++-------------- platform/qt/src/qmapboxgl_map_renderer.cpp | 5 ++-- platform/qt/src/qmapboxgl_map_renderer.hpp | 4 ++- platform/qt/src/qmapboxgl_renderer_backend.cpp | 13 +++++++-- platform/qt/src/qmapboxgl_renderer_backend.hpp | 5 ++-- platform/qt/test/qmapboxgl.test.cpp | 4 +-- 8 files changed, 45 insertions(+), 34 deletions(-) diff --git a/platform/qt/app/mapwindow.cpp b/platform/qt/app/mapwindow.cpp index 89047bd948..f6d5473192 100644 --- a/platform/qt/app/mapwindow.cpp +++ b/platform/qt/app/mapwindow.cpp @@ -442,6 +442,9 @@ void MapWindow::initializeGL() void MapWindow::paintGL() { m_frameDraws++; - m_map->resize(size(), size() * pixelRatio()); + m_map->resize(size()); +#if QT_VERSION >= 0x050400 + m_map->setFramebufferObject(defaultFramebufferObject(), size() * pixelRatio()); +#endif m_map->render(); } diff --git a/platform/qt/include/qmapboxgl.hpp b/platform/qt/include/qmapboxgl.hpp index a1758ba6e6..d3ba643248 100644 --- a/platform/qt/include/qmapboxgl.hpp +++ b/platform/qt/include/qmapboxgl.hpp @@ -191,8 +191,7 @@ public: void scaleBy(double scale, const QPointF ¢er = QPointF()); void rotateBy(const QPointF &first, const QPointF &second); - void resize(const QSize &size, const QSize &framebufferSize); - void setFramebufferObject(quint32 fbo); + void resize(const QSize &size); double metersPerPixelAtLatitude(double latitude, double zoom) const; QMapbox::ProjectedMeters projectedMetersForCoordinate(const QMapbox::Coordinate &) const; @@ -227,9 +226,10 @@ public: void setFilter(const QString &layer, const QVariant &filter); // When rendering on a different thread, - // should be called on this thread + // should be called on the render thread. void createRenderer(); void destroyRenderer(); + void setFramebufferObject(quint32 fbo, const QSize &size); public slots: void render(); diff --git a/platform/qt/src/qmapboxgl.cpp b/platform/qt/src/qmapboxgl.cpp index 89904ddf88..df5673e35b 100644 --- a/platform/qt/src/qmapboxgl.cpp +++ b/platform/qt/src/qmapboxgl.cpp @@ -1047,36 +1047,19 @@ void QMapboxGL::rotateBy(const QPointF &first, const QPointF &second) } /*! - Resize the map to \a size and scale to fit at \a framebufferSize. For - high DPI screens, the size will be smaller than the \a framebufferSize. - - This fallowing example will double the pixel density of the map for - a given \c size: - - \code - map->resize(size / 2, size); - \endcode + Resize the map to \a size_ and scale to fit at the framebuffer. For + high DPI screens, the size will be smaller than the framebuffer. */ -void QMapboxGL::resize(const QSize& size_, const QSize& framebufferSize) +void QMapboxGL::resize(const QSize& size_) { auto size = sanitizedSize(size_); if (d_ptr->mapObj->getSize() == size) return; - d_ptr->mapRenderer->updateFramebufferSize(sanitizedSize(framebufferSize)); d_ptr->mapObj->setSize(size); } -/*! - If Mapbox GL needs to rebind the default \a fbo, it will use the - ID supplied here. -*/ -void QMapboxGL::setFramebufferObject(quint32) -{ - // FIXME: No-op, implicit. -} - /*! Adds an \a icon to the annotation icon pool. This can be later used by the annotation functions to shown any drawing on the map by referencing its \a name. @@ -1512,6 +1495,20 @@ void QMapboxGL::render() d_ptr->mapRenderer->render(); } +/*! + If Mapbox GL needs to rebind the default \a fbo, it will use the + ID supplied here. \a size is the size of the framebuffer, which + on high DPI screens is usually bigger than the map size. +*/ +void QMapboxGL::setFramebufferObject(quint32 fbo, const QSize& size) +{ + if (!d_ptr->mapRenderer) { + createRenderer(); + } + + d_ptr->mapRenderer->updateFramebuffer(fbo, sanitizedSize(size)); +} + /*! Informs the map that the network connection has been established, causing all network requests that previously timed out to be retried immediately. diff --git a/platform/qt/src/qmapboxgl_map_renderer.cpp b/platform/qt/src/qmapboxgl_map_renderer.cpp index 81293d7da1..f9120379cb 100644 --- a/platform/qt/src/qmapboxgl_map_renderer.cpp +++ b/platform/qt/src/qmapboxgl_map_renderer.cpp @@ -25,10 +25,9 @@ void QMapboxGLMapRenderer::updateParameters(std::shared_ptr lock(m_updateMutex); - m_backend.setFramebufferSize(size); + m_backend.updateFramebuffer(fbo, size); } void QMapboxGLMapRenderer::render() diff --git a/platform/qt/src/qmapboxgl_map_renderer.hpp b/platform/qt/src/qmapboxgl_map_renderer.hpp index c15840a85d..f7523604c7 100644 --- a/platform/qt/src/qmapboxgl_map_renderer.hpp +++ b/platform/qt/src/qmapboxgl_map_renderer.hpp @@ -9,6 +9,8 @@ #include #include +#include + #include #include #include @@ -31,11 +33,11 @@ public: void schedule(std::weak_ptr scheduled) final; void render(); + void updateFramebuffer(quint32 fbo, const mbgl::Size &size); void setObserver(std::shared_ptr); // Thread-safe, called by the Frontend void updateParameters(std::shared_ptr); - void updateFramebufferSize(const mbgl::Size &size); private: Q_DISABLE_COPY(QMapboxGLMapRenderer) diff --git a/platform/qt/src/qmapboxgl_renderer_backend.cpp b/platform/qt/src/qmapboxgl_renderer_backend.cpp index 6cc7de53fe..917741f5ce 100644 --- a/platform/qt/src/qmapboxgl_renderer_backend.cpp +++ b/platform/qt/src/qmapboxgl_renderer_backend.cpp @@ -11,7 +11,15 @@ void QMapboxGLRendererBackend::updateAssumedState() { assumeFramebufferBinding(ImplicitFramebufferBinding); - assumeViewport(0, 0, { 800, 600 }); + assumeViewport(0, 0, m_size); +} + +void QMapboxGLRendererBackend::bind() +{ + assert(mbgl::BackendScope::exists()); + + setFramebufferBinding(m_fbo); + setViewport(0, 0, m_size); } mbgl::Size QMapboxGLRendererBackend::getFramebufferSize() const @@ -19,8 +27,9 @@ mbgl::Size QMapboxGLRendererBackend::getFramebufferSize() const return m_size; } -void QMapboxGLRendererBackend::setFramebufferSize(const mbgl::Size &size) +void QMapboxGLRendererBackend::updateFramebuffer(quint32 fbo, const mbgl::Size &size) { + m_fbo = fbo; m_size = size; } diff --git a/platform/qt/src/qmapboxgl_renderer_backend.hpp b/platform/qt/src/qmapboxgl_renderer_backend.hpp index fb38556b55..de66b035fc 100644 --- a/platform/qt/src/qmapboxgl_renderer_backend.hpp +++ b/platform/qt/src/qmapboxgl_renderer_backend.hpp @@ -14,10 +14,10 @@ public: // mbgl::RendererBackend implementation void updateAssumedState() final; - void bind() final {} + void bind() final; mbgl::Size getFramebufferSize() const final; - void setFramebufferSize(const mbgl::Size &); + void updateFramebuffer(quint32 fbo, const mbgl::Size &); protected: mbgl::gl::ProcAddress getExtensionFunctionPointer(const char*) final; @@ -27,6 +27,7 @@ protected: void deactivate() final {} private: + quint32 m_fbo = 0; mbgl::Size m_size = { 0, 0 }; Q_DISABLE_COPY(QMapboxGLRendererBackend) diff --git a/platform/qt/test/qmapboxgl.test.cpp b/platform/qt/test/qmapboxgl.test.cpp index 932460b932..2a56b346a3 100644 --- a/platform/qt/test/qmapboxgl.test.cpp +++ b/platform/qt/test/qmapboxgl.test.cpp @@ -15,8 +15,8 @@ QMapboxGLTest::QMapboxGLTest() : size(512, 512), fbo((assert(widget.context()->i this, SLOT(onMapChanged(QMapboxGL::MapChange))); connect(&map, SIGNAL(needsRendering()), this, SLOT(onNeedsRendering())); - map.resize(fbo.size(), fbo.size()); - map.setFramebufferObject(fbo.handle()); + map.resize(fbo.size()); + map.setFramebufferObject(fbo.handle(), fbo.size()); map.setCoordinateZoom(QMapbox::Coordinate(60.170448, 24.942046), 14); } -- cgit v1.2.1 From 6542e6ee37c98318f9eb6cced317d24b283e2f18 Mon Sep 17 00:00:00 2001 From: "Thiago Marcos P. Santos" Date: Fri, 26 Jan 2018 21:33:56 +0200 Subject: [qt] Make sure that methods are being called on the right thread --- platform/qt/src/qmapboxgl.cpp | 131 ++++++++++++++++++----------- platform/qt/src/qmapboxgl_map_renderer.cpp | 5 ++ platform/qt/src/qmapboxgl_map_renderer.hpp | 3 + platform/qt/src/qmapboxgl_p.hpp | 31 ++++--- 4 files changed, 109 insertions(+), 61 deletions(-) diff --git a/platform/qt/src/qmapboxgl.cpp b/platform/qt/src/qmapboxgl.cpp index df5673e35b..c37e1bc9ba 100644 --- a/platform/qt/src/qmapboxgl.cpp +++ b/platform/qt/src/qmapboxgl.cpp @@ -1453,19 +1453,12 @@ void QMapboxGL::setFilter(const QString& layer, const QVariant& filter) void QMapboxGL::createRenderer() { - d_ptr->mapRenderer = std::make_unique( - d_ptr->pixelRatio, - *d_ptr->fileSourceObj, - *d_ptr->threadPool, - d_ptr->mode - ); - - d_ptr->mapRenderer->setObserver(d_ptr->rendererObserver); + d_ptr->createRenderer(); } void QMapboxGL::destroyRenderer() { - d_ptr->mapRenderer.reset(); + d_ptr->destroyRenderer(); } /*! @@ -1479,20 +1472,7 @@ void QMapboxGL::destroyRenderer() */ void QMapboxGL::render() { - if (!d_ptr->mapRenderer) { - createRenderer(); - } - -#if defined(__APPLE__) && QT_VERSION < 0x050000 - // FIXME Qt 4.x provides an incomplete FBO at start. - // See https://bugreports.qt.io/browse/QTBUG-36802 for details. - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - return; - } -#endif - - d_ptr->renderQueued.clear(); - d_ptr->mapRenderer->render(); + d_ptr->render(); } /*! @@ -1502,11 +1482,7 @@ void QMapboxGL::render() */ void QMapboxGL::setFramebufferObject(quint32 fbo, const QSize& size) { - if (!d_ptr->mapRenderer) { - createRenderer(); - } - - d_ptr->mapRenderer->updateFramebuffer(fbo, sanitizedSize(size)); + d_ptr->setFramebufferObject(fbo, size); } /*! @@ -1547,47 +1523,46 @@ void QMapboxGL::connectionEstablished() QMapboxGLPrivate::QMapboxGLPrivate(QMapboxGL *q, const QMapboxGLSettings &settings, const QSize &size, qreal pixelRatio_) : QObject(q) - , q_ptr(q) - , fileSourceObj(sharedDefaultFileSource( + , m_fileSourceObj(sharedDefaultFileSource( settings.cacheDatabasePath().toStdString(), settings.assetPath().toStdString(), settings.cacheDatabaseMaximumSize())) - , threadPool(mbgl::sharedThreadPool()) - , mode(settings.contextMode()) - , pixelRatio(pixelRatio_) + , m_threadPool(mbgl::sharedThreadPool()) + , m_mode(settings.contextMode()) + , m_pixelRatio(pixelRatio_) { // Setup the FileSource - fileSourceObj->setAccessToken(settings.accessToken().toStdString()); - fileSourceObj->setAPIBaseURL(settings.apiBaseUrl().toStdString()); + m_fileSourceObj->setAccessToken(settings.accessToken().toStdString()); + m_fileSourceObj->setAPIBaseURL(settings.apiBaseUrl().toStdString()); if (settings.resourceTransform()) { m_resourceTransform = std::make_unique>(*mbgl::Scheduler::GetCurrent(), [callback = settings.resourceTransform()] (mbgl::Resource::Kind, const std::string &&url_) -> std::string { return callback(std::move(url_)); }); - fileSourceObj->setResourceTransform(m_resourceTransform->self()); + m_fileSourceObj->setResourceTransform(m_resourceTransform->self()); } // Setup MapObserver - mapObserver = std::make_unique(this); + m_mapObserver = std::make_unique(this); qRegisterMetaType("QMapboxGL::MapChange"); - connect(mapObserver.get(), SIGNAL(mapChanged(QMapboxGL::MapChange)), q_ptr, SIGNAL(mapChanged(QMapboxGL::MapChange))); - connect(mapObserver.get(), SIGNAL(copyrightsChanged(QString)), q_ptr, SIGNAL(copyrightsChanged(QString))); + connect(m_mapObserver.get(), SIGNAL(mapChanged(QMapboxGL::MapChange)), q, SIGNAL(mapChanged(QMapboxGL::MapChange))); + connect(m_mapObserver.get(), SIGNAL(copyrightsChanged(QString)), q, SIGNAL(copyrightsChanged(QString))); // Setup the Map object mapObj = std::make_unique( *this, // RendererFrontend - *mapObserver, + *m_mapObserver, sanitizedSize(size), - pixelRatio, *fileSourceObj, *threadPool, + m_pixelRatio, *m_fileSourceObj, *m_threadPool, mbgl::MapMode::Continuous, static_cast(settings.constrainMode()), static_cast(settings.viewportMode())); // Needs to be Queued to give time to discard redundant draw calls via the `renderQueued` flag. - connect(this, SIGNAL(needsRendering()), q_ptr, SIGNAL(needsRendering()), Qt::QueuedConnection); + connect(this, SIGNAL(needsRendering()), q, SIGNAL(needsRendering()), Qt::QueuedConnection); } QMapboxGLPrivate::~QMapboxGLPrivate() @@ -1596,23 +1571,83 @@ QMapboxGLPrivate::~QMapboxGLPrivate() void QMapboxGLPrivate::update(std::shared_ptr parameters) { - if (!mapRenderer) { + std::lock_guard lock(m_mapRendererMutex); + + if (!m_mapRenderer) { return; } - mapRenderer->updateParameters(std::move(parameters)); + m_mapRenderer->updateParameters(std::move(parameters)); - if (!renderQueued.test_and_set()) { + if (!m_renderQueued.test_and_set()) { emit needsRendering(); } } void QMapboxGLPrivate::setObserver(mbgl::RendererObserver &observer) { - rendererObserver = std::make_shared( + m_rendererObserver = std::make_shared( *mbgl::util::RunLoop::Get(), observer); - if (mapRenderer) { - mapRenderer->setObserver(rendererObserver); + std::lock_guard lock(m_mapRendererMutex); + + if (m_mapRenderer) { + m_mapRenderer->setObserver(m_rendererObserver); } } + +void QMapboxGLPrivate::createRenderer() +{ + std::lock_guard lock(m_mapRendererMutex); + + if (m_mapRenderer) { + return; + } + + m_mapRenderer = std::make_unique( + m_pixelRatio, + *m_fileSourceObj, + *m_threadPool, + m_mode + ); + + m_mapRenderer->setObserver(m_rendererObserver); +} + +void QMapboxGLPrivate::destroyRenderer() +{ + std::lock_guard lock(m_mapRendererMutex); + + m_mapRenderer.reset(); +} + +void QMapboxGLPrivate::render() +{ + std::lock_guard lock(m_mapRendererMutex); + + if (!m_mapRenderer) { + createRenderer(); + } + +#if defined(__APPLE__) && QT_VERSION < 0x050000 + // FIXME Qt 4.x provides an incomplete FBO at start. + // See https://bugreports.qt.io/browse/QTBUG-36802 for details. + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + return; + } +#endif + + m_renderQueued.clear(); + m_mapRenderer->render(); +} + +void QMapboxGLPrivate::setFramebufferObject(quint32 fbo, const QSize& size) +{ + std::lock_guard lock(m_mapRendererMutex); + + if (!m_mapRenderer) { + createRenderer(); + } + + m_mapRenderer->updateFramebuffer(fbo, sanitizedSize(size)); +} diff --git a/platform/qt/src/qmapboxgl_map_renderer.cpp b/platform/qt/src/qmapboxgl_map_renderer.cpp index f9120379cb..af6823acb8 100644 --- a/platform/qt/src/qmapboxgl_map_renderer.cpp +++ b/platform/qt/src/qmapboxgl_map_renderer.cpp @@ -11,6 +11,7 @@ QMapboxGLMapRenderer::QMapboxGLMapRenderer(qreal pixelRatio, QMapboxGLMapRenderer::~QMapboxGLMapRenderer() { + MBGL_VERIFY_THREAD(tid); } void QMapboxGLMapRenderer::schedule(std::weak_ptr mailbox) @@ -27,11 +28,15 @@ void QMapboxGLMapRenderer::updateParameters(std::shared_ptr params; { // Lock on the parameters diff --git a/platform/qt/src/qmapboxgl_map_renderer.hpp b/platform/qt/src/qmapboxgl_map_renderer.hpp index f7523604c7..aed6434cb0 100644 --- a/platform/qt/src/qmapboxgl_map_renderer.hpp +++ b/platform/qt/src/qmapboxgl_map_renderer.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include @@ -40,6 +41,8 @@ public: void updateParameters(std::shared_ptr); private: + MBGL_STORE_THREAD(tid) + Q_DISABLE_COPY(QMapboxGLMapRenderer) std::mutex m_updateMutex; diff --git a/platform/qt/src/qmapboxgl_p.hpp b/platform/qt/src/qmapboxgl_p.hpp index b9a4b37ae5..31fb5138bf 100644 --- a/platform/qt/src/qmapboxgl_p.hpp +++ b/platform/qt/src/qmapboxgl_p.hpp @@ -31,27 +31,32 @@ public: void setObserver(mbgl::RendererObserver &) final; void update(std::shared_ptr) final; - mbgl::EdgeInsets margins; - - QMapboxGL *q_ptr { nullptr }; + // These need to be called on the same thread. + void createRenderer(); + void destroyRenderer(); + void render(); + void setFramebufferObject(quint32 fbo, const QSize& size); - std::shared_ptr fileSourceObj; - std::shared_ptr threadPool; + mbgl::EdgeInsets margins; std::unique_ptr mapObj; - std::unique_ptr mapObserver; - std::unique_ptr mapRenderer; - std::shared_ptr rendererObserver; - - QMapboxGLSettings::GLContextMode mode; - qreal pixelRatio; - - std::atomic_flag renderQueued = ATOMIC_FLAG_INIT; signals: void needsRendering(); private: Q_DISABLE_COPY(QMapboxGLPrivate) + std::recursive_mutex m_mapRendererMutex; + std::shared_ptr m_rendererObserver; + + std::unique_ptr m_mapObserver; + std::shared_ptr m_fileSourceObj; + std::shared_ptr m_threadPool; + std::unique_ptr m_mapRenderer; std::unique_ptr> m_resourceTransform; + + QMapboxGLSettings::GLContextMode m_mode; + qreal m_pixelRatio; + + std::atomic_flag m_renderQueued = ATOMIC_FLAG_INIT; }; -- cgit v1.2.1 From 36a0c98713674206fa6cbe97be66a3945f7af03b Mon Sep 17 00:00:00 2001 From: "Thiago Marcos P. Santos" Date: Sun, 28 Jan 2018 00:42:20 +0200 Subject: [qt] Refresh the docs --- platform/qt/src/qmapboxgl.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/platform/qt/src/qmapboxgl.cpp b/platform/qt/src/qmapboxgl.cpp index c37e1bc9ba..fd12476a0f 100644 --- a/platform/qt/src/qmapboxgl.cpp +++ b/platform/qt/src/qmapboxgl.cpp @@ -1451,11 +1451,23 @@ void QMapboxGL::setFilter(const QString& layer, const QVariant& filter) qWarning() << "Layer doesn't support filters"; } +/*! + Creates the infrastructure needed for rendering the map. It + should be called before any call to render(). + + Must be called on the render thread. +*/ void QMapboxGL::createRenderer() { d_ptr->createRenderer(); } +/*! + Destroys the infrastructure needed for rendering the map, + releasing resources. + + Must be called on the render thread. +*/ void QMapboxGL::destroyRenderer() { d_ptr->destroyRenderer(); @@ -1469,6 +1481,8 @@ void QMapboxGL::destroyRenderer() This function should be called only after the signal needsRendering() is emitted at least once. + + Must be called on the render thread. */ void QMapboxGL::render() { @@ -1479,6 +1493,8 @@ void QMapboxGL::render() If Mapbox GL needs to rebind the default \a fbo, it will use the ID supplied here. \a size is the size of the framebuffer, which on high DPI screens is usually bigger than the map size. + + Must be called on the render thread. */ void QMapboxGL::setFramebufferObject(quint32 fbo, const QSize& size) { -- cgit v1.2.1 From d229cbb0312351aa1a188616a15ac141cc497863 Mon Sep 17 00:00:00 2001 From: "Thiago Marcos P. Santos" Date: Tue, 6 Feb 2018 19:31:06 +0200 Subject: [qt] Add an interface for Static rendering --- platform/qt/include/qmapboxgl.hpp | 15 +++++ platform/qt/src/qmapboxgl.cpp | 99 ++++++++++++++++++++++++++++-- platform/qt/src/qmapboxgl_map_renderer.cpp | 4 ++ platform/qt/src/qmapboxgl_map_renderer.hpp | 7 ++- platform/qt/src/qmapboxgl_p.hpp | 3 + 5 files changed, 123 insertions(+), 5 deletions(-) diff --git a/platform/qt/include/qmapboxgl.hpp b/platform/qt/include/qmapboxgl.hpp index d3ba643248..bc18eaba59 100644 --- a/platform/qt/include/qmapboxgl.hpp +++ b/platform/qt/include/qmapboxgl.hpp @@ -26,6 +26,11 @@ public: SharedGLContext }; + enum MapMode { + Continuous = 0, + Static + }; + enum ConstrainMode { NoConstrain = 0, ConstrainHeightOnly, @@ -40,6 +45,9 @@ public: GLContextMode contextMode() const; void setContextMode(GLContextMode); + MapMode mapMode() const; + void setMapMode(MapMode); + ConstrainMode constrainMode() const; void setConstrainMode(ConstrainMode); @@ -66,6 +74,7 @@ public: private: GLContextMode m_contextMode; + MapMode m_mapMode; ConstrainMode m_constrainMode; ViewportMode m_viewportMode; @@ -235,11 +244,17 @@ public slots: void render(); void connectionEstablished(); + // Commit changes, load all the resources + // and renders the map when completed. + void startStaticRender(); + signals: void needsRendering(); void mapChanged(QMapboxGL::MapChange); void copyrightsChanged(const QString ©rightsHtml); + void staticRenderFinished(const QString &error); + private: Q_DISABLE_COPY(QMapboxGL) diff --git a/platform/qt/src/qmapboxgl.cpp b/platform/qt/src/qmapboxgl.cpp index fd12476a0f..414b65255c 100644 --- a/platform/qt/src/qmapboxgl.cpp +++ b/platform/qt/src/qmapboxgl.cpp @@ -64,6 +64,10 @@ using namespace QMapbox; static_assert(mbgl::underlying_type(QMapboxGLSettings::UniqueGLContext) == mbgl::underlying_type(mbgl::GLContextMode::Unique), "error"); static_assert(mbgl::underlying_type(QMapboxGLSettings::SharedGLContext) == mbgl::underlying_type(mbgl::GLContextMode::Shared), "error"); +// mbgl::MapMode +static_assert(mbgl::underlying_type(QMapboxGLSettings::Continuous) == mbgl::underlying_type(mbgl::MapMode::Continuous), "error"); +static_assert(mbgl::underlying_type(QMapboxGLSettings::Static) == mbgl::underlying_type(mbgl::MapMode::Static), "error"); + // mbgl::ConstrainMode static_assert(mbgl::underlying_type(QMapboxGLSettings::NoConstrain) == mbgl::underlying_type(mbgl::ConstrainMode::None), "error"); static_assert(mbgl::underlying_type(QMapboxGLSettings::ConstrainHeightOnly) == mbgl::underlying_type(mbgl::ConstrainMode::HeightOnly), "error"); @@ -163,6 +167,27 @@ std::unique_ptr toStyleImage(const QString &id, const QImage \sa contextMode() */ +/*! + \enum QMapboxGLSettings::MapMode + + This enum sets the map rendering mode + + \value Continuous The map will render as data arrives from the network and + react immediately to state changes. + + This is the default mode and the preferred when the map is intended to be + interactive. + + \value Static The map will no longer react to state changes and will only + be rendered when QMapboxGL::startStaticRender is called. After all the + resources are loaded, the QMapboxGL::staticRenderFinished signal is emitted. + + This mode is useful for taking a snapshot of the finished rendering result + of the map into a QImage. + + \sa mapMode() +*/ + /*! \enum QMapboxGLSettings::ConstrainMode @@ -199,6 +224,7 @@ std::unique_ptr toStyleImage(const QString &id, const QImage */ QMapboxGLSettings::QMapboxGLSettings() : m_contextMode(QMapboxGLSettings::SharedGLContext) + , m_mapMode(QMapboxGLSettings::Continuous) , m_constrainMode(QMapboxGLSettings::ConstrainHeightOnly) , m_viewportMode(QMapboxGLSettings::DefaultViewport) , m_cacheMaximumSize(mbgl::util::DEFAULT_MAX_CACHE_SIZE) @@ -228,6 +254,31 @@ void QMapboxGLSettings::setContextMode(GLContextMode mode) m_contextMode = mode; } +/*! + Returns the map mode. Static mode will emit a signal for + rendering a map only when the map is fully loaded. + Animations like style transitions and labels fading won't + be seen. + + The Continuous mode will emit the signal for every new + change on the map and it is usually what you expect for + a interactive map. + + By default, it is set to QMapboxGLSettings::Continuous. +*/ +QMapboxGLSettings::MapMode QMapboxGLSettings::mapMode() const +{ + return m_mapMode; +} + +/*! + Sets the map \a mode. +*/ +void QMapboxGLSettings::setMapMode(MapMode mode) +{ + m_mapMode = mode; +} + /*! Returns the constrain mode. This is used to limit the map to wrap around the globe horizontally. @@ -1473,6 +1524,29 @@ void QMapboxGL::destroyRenderer() d_ptr->destroyRenderer(); } +/*! + Start a static rendering of the current state of the map. This + should only be called when the map is initialized in static mode. + + \sa QMapboxGLSettings::MapMode +*/ +void QMapboxGL::startStaticRender() +{ + d_ptr->mapObj->renderStill([this](std::exception_ptr err) { + QString what; + + try { + if (err) { + std::rethrow_exception(err); + } + } catch(const std::exception& e) { + what = e.what(); + } + + emit staticRenderFinished(what); + }); +} + /*! Renders the map using OpenGL draw calls. It will make sure to bind the framebuffer object before drawing; otherwise a valid OpenGL context is @@ -1520,6 +1594,16 @@ void QMapboxGL::connectionEstablished() \sa render() */ +/*! + \fn void QMapboxGL::staticRenderFinished(const QString &error) + + This signal is emitted when a static map is fully drawn. Usually the next + step is to extract the map from a framebuffer into a container like a + QImage. \a error is set to a message when an error occurs. + + \sa startStaticRender() +*/ + /*! \fn void QMapboxGL::mapChanged(QMapboxGL::MapChange change) @@ -1573,7 +1657,7 @@ QMapboxGLPrivate::QMapboxGLPrivate(QMapboxGL *q, const QMapboxGLSettings &settin *m_mapObserver, sanitizedSize(size), m_pixelRatio, *m_fileSourceObj, *m_threadPool, - mbgl::MapMode::Continuous, + static_cast(settings.mapMode()), static_cast(settings.constrainMode()), static_cast(settings.viewportMode())); @@ -1595,9 +1679,7 @@ void QMapboxGLPrivate::update(std::shared_ptr parameters m_mapRenderer->updateParameters(std::move(parameters)); - if (!m_renderQueued.test_and_set()) { - emit needsRendering(); - } + requestRendering(); } void QMapboxGLPrivate::setObserver(mbgl::RendererObserver &observer) @@ -1627,6 +1709,8 @@ void QMapboxGLPrivate::createRenderer() m_mode ); + connect(m_mapRenderer.get(), SIGNAL(needsRendering()), this, SLOT(requestRendering())); + m_mapRenderer->setObserver(m_rendererObserver); } @@ -1667,3 +1751,10 @@ void QMapboxGLPrivate::setFramebufferObject(quint32 fbo, const QSize& size) m_mapRenderer->updateFramebuffer(fbo, sanitizedSize(size)); } + +void QMapboxGLPrivate::requestRendering() +{ + if (!m_renderQueued.test_and_set()) { + emit needsRendering(); + } +} diff --git a/platform/qt/src/qmapboxgl_map_renderer.cpp b/platform/qt/src/qmapboxgl_map_renderer.cpp index af6823acb8..7a9d1f6f78 100644 --- a/platform/qt/src/qmapboxgl_map_renderer.cpp +++ b/platform/qt/src/qmapboxgl_map_renderer.cpp @@ -18,6 +18,10 @@ void QMapboxGLMapRenderer::schedule(std::weak_ptr mailbox) { std::lock_guard lock(m_taskQueueMutex); m_taskQueue.push(mailbox); + + // Need to force the main thread to wake + // up this thread and process the events. + emit needsRendering(); } void QMapboxGLMapRenderer::updateParameters(std::shared_ptr newParameters) diff --git a/platform/qt/src/qmapboxgl_map_renderer.hpp b/platform/qt/src/qmapboxgl_map_renderer.hpp index aed6434cb0..adba11de51 100644 --- a/platform/qt/src/qmapboxgl_map_renderer.hpp +++ b/platform/qt/src/qmapboxgl_map_renderer.hpp @@ -23,8 +23,10 @@ class UpdateParameters; class QMapboxGLRendererBackend; -class QMapboxGLMapRenderer : public mbgl::Scheduler +class QMapboxGLMapRenderer : public QObject, public mbgl::Scheduler { + Q_OBJECT + public: QMapboxGLMapRenderer(qreal pixelRatio, mbgl::DefaultFileSource &, mbgl::ThreadPool &, QMapboxGLSettings::GLContextMode); @@ -40,6 +42,9 @@ public: // Thread-safe, called by the Frontend void updateParameters(std::shared_ptr); +signals: + void needsRendering(); + private: MBGL_STORE_THREAD(tid) diff --git a/platform/qt/src/qmapboxgl_p.hpp b/platform/qt/src/qmapboxgl_p.hpp index 31fb5138bf..51c7cb8fc4 100644 --- a/platform/qt/src/qmapboxgl_p.hpp +++ b/platform/qt/src/qmapboxgl_p.hpp @@ -40,6 +40,9 @@ public: mbgl::EdgeInsets margins; std::unique_ptr mapObj; +public slots: + void requestRendering(); + signals: void needsRendering(); -- cgit v1.2.1 From fec4cc8bd0a645549118f41263c75feab280d344 Mon Sep 17 00:00:00 2001 From: Pablo Guardiola Date: Mon, 12 Feb 2018 09:59:26 +0100 Subject: [android] remove unnecessary @jar and @aar dependencies suffixes (#11161) (#11165) * [android] remove unnecessary @jar and @aar dependencies suffixes * [android] remove lint baseline file from mapbox gl android sdk module and fix lint errors --- platform/android/MapboxGLAndroidSDK/.gitignore | 2 - platform/android/MapboxGLAndroidSDK/build.gradle | 2 - .../MapboxGLAndroidSDK/lint-baseline-local.xml | 37 ------------------ .../MapboxGLAndroidSDK/lint/lint-baseline-ci.xml | 44 ---------------------- .../mapbox/mapboxsdk/maps/widgets/CompassView.java | 4 +- .../src/main/res/values-he/strings.xml | 16 -------- .../src/main/res/values-iw/strings.xml | 16 ++++++++ platform/android/gradle/dependencies.gradle | 8 ++-- 8 files changed, 22 insertions(+), 107 deletions(-) delete mode 100644 platform/android/MapboxGLAndroidSDK/.gitignore delete mode 100644 platform/android/MapboxGLAndroidSDK/lint-baseline-local.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/lint/lint-baseline-ci.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/values-he/strings.xml create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/values-iw/strings.xml diff --git a/platform/android/MapboxGLAndroidSDK/.gitignore b/platform/android/MapboxGLAndroidSDK/.gitignore deleted file mode 100644 index cec211fe81..0000000000 --- a/platform/android/MapboxGLAndroidSDK/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -lint-baseline.xml -lint/lint-baseline-local.xml \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/build.gradle b/platform/android/MapboxGLAndroidSDK/build.gradle index 0900f700c8..12c7cc1e58 100644 --- a/platform/android/MapboxGLAndroidSDK/build.gradle +++ b/platform/android/MapboxGLAndroidSDK/build.gradle @@ -114,7 +114,6 @@ android { lintOptions { disable 'MissingTranslation', 'TypographyQuotes', 'ObsoleteLintCustomCheck', 'MissingPermission' - baseline file("lint-baseline-local.xml") checkAllWarnings true warningsAsErrors false } @@ -148,4 +147,3 @@ apply from: "${rootDir}/gradle/gradle-javadoc.gradle" apply from: "${rootDir}/gradle/gradle-publish.gradle" apply from: "${rootDir}/gradle/gradle-checkstyle.gradle" apply from: "${rootDir}/gradle/gradle-tests-staticblockremover.gradle" -apply from: "${rootDir}/gradle/gradle-lint.gradle" diff --git a/platform/android/MapboxGLAndroidSDK/lint-baseline-local.xml b/platform/android/MapboxGLAndroidSDK/lint-baseline-local.xml deleted file mode 100644 index 0a76f53505..0000000000 --- a/platform/android/MapboxGLAndroidSDK/lint-baseline-local.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/platform/android/MapboxGLAndroidSDK/lint/lint-baseline-ci.xml b/platform/android/MapboxGLAndroidSDK/lint/lint-baseline-ci.xml deleted file mode 100644 index fd65c9f627..0000000000 --- a/platform/android/MapboxGLAndroidSDK/lint/lint-baseline-ci.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java index 1e604c9bef..45f72af1c5 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java @@ -6,10 +6,10 @@ import android.support.annotation.NonNull; import android.support.v4.view.ViewCompat; import android.support.v4.view.ViewPropertyAnimatorCompat; import android.support.v4.view.ViewPropertyAnimatorListenerAdapter; +import android.support.v7.widget.AppCompatImageView; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; -import android.widget.ImageView; import com.mapbox.mapboxsdk.maps.MapboxMap; @@ -22,7 +22,7 @@ import com.mapbox.mapboxsdk.maps.MapboxMap; * use {@link com.mapbox.mapboxsdk.maps.UiSettings}. *

*/ -public final class CompassView extends ImageView implements Runnable { +public final class CompassView extends AppCompatImageView implements Runnable { public static final long TIME_WAIT_IDLE = 500; public static final long TIME_MAP_NORTH_ANIMATION = 150; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values-he/strings.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values-he/strings.xml deleted file mode 100644 index 11b20f5dc5..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/res/values-he/strings.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - מצפן. הפעל בכדי לקבע את כיוון המפה צפונה. - סמל שיוך. הפעל כדי להציג תיבת דו-שיח של שיוך. - סמן מיקום. מציג את המיקום הנוכחי שלך על המפה. - מציג מפה שנוצרה עם Mapbox. גלול באמצעות גרירה עם שתי אצבעות, זום באמצעות צביטה עם שתי אצבעות. - Mapbox Maps SDK for Android - שפר את המפות של Mapbox  - אתם מסייעים לשפר את המפות של OpenStreetMap ו Mapbox באמצעות שיתוף אנונימי של נתוני השימוש. - מסכים/מה - לא מסכים/מה - מידע נוסף - לא מותקן דפדפן אינטרנט במכשיר, לא ניתן לפתוח את דף האינטרנט. - בתנאי ש- OfflineRegionDefinition אינו מתאים לגבולות העולם: %s - הגדרות טלמטריות - diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values-iw/strings.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values-iw/strings.xml new file mode 100644 index 0000000000..11b20f5dc5 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values-iw/strings.xml @@ -0,0 +1,16 @@ + + + מצפן. הפעל בכדי לקבע את כיוון המפה צפונה. + סמל שיוך. הפעל כדי להציג תיבת דו-שיח של שיוך. + סמן מיקום. מציג את המיקום הנוכחי שלך על המפה. + מציג מפה שנוצרה עם Mapbox. גלול באמצעות גרירה עם שתי אצבעות, זום באמצעות צביטה עם שתי אצבעות. + Mapbox Maps SDK for Android + שפר את המפות של Mapbox  + אתם מסייעים לשפר את המפות של OpenStreetMap ו Mapbox באמצעות שיתוף אנונימי של נתוני השימוש. + מסכים/מה + לא מסכים/מה + מידע נוסף + לא מותקן דפדפן אינטרנט במכשיר, לא ניתן לפתוח את דף האינטרנט. + בתנאי ש- OfflineRegionDefinition אינו מתאים לגבולות העולם: %s + הגדרות טלמטריות + diff --git a/platform/android/gradle/dependencies.gradle b/platform/android/gradle/dependencies.gradle index 7ce0cd6196..99fa3f3508 100644 --- a/platform/android/gradle/dependencies.gradle +++ b/platform/android/gradle/dependencies.gradle @@ -23,12 +23,12 @@ ext { ] dependenciesList = [ - mapboxJavaServices : "com.mapbox.mapboxsdk:mapbox-sdk-services:${versions.mapboxServices}@jar", - mapboxJavaGeoJSON : "com.mapbox.mapboxsdk:mapbox-sdk-geojson:${versions.mapboxServices}@jar", - mapboxAndroidTelemetry: "com.mapbox.mapboxsdk:mapbox-android-telemetry:${versions.mapboxTelemetry}@aar", + mapboxJavaServices : "com.mapbox.mapboxsdk:mapbox-sdk-services:${versions.mapboxServices}", + mapboxJavaGeoJSON : "com.mapbox.mapboxsdk:mapbox-sdk-geojson:${versions.mapboxServices}", + mapboxAndroidTelemetry: "com.mapbox.mapboxsdk:mapbox-android-telemetry:${versions.mapboxTelemetry}", // for testApp - mapboxJavaTurf : "com.mapbox.mapboxsdk:mapbox-sdk-turf:${versions.mapboxServices}@jar", + mapboxJavaTurf : "com.mapbox.mapboxsdk:mapbox-sdk-turf:${versions.mapboxServices}", junit : "junit:junit:${versions.junit}", mockito : "org.mockito:mockito-core:${versions.mockito}", -- cgit v1.2.1 From f0b12d581a266f8877fba32be97fe82561bb2392 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Fri, 9 Feb 2018 14:48:14 +0100 Subject: [android] - update changelog for 5.4.1 --- platform/android/CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index ed8667f42d..6f81db7e3b 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -2,6 +2,13 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started. +## 5.4.1 - February 9, 2018 + - Don't recreate TextureView surface as part of view resizing, solves OOM crashes [#11148](https://github.com/mapbox/mapbox-gl-native/pull/11148) + - Don't invoke OnLowMemory before map is ready, solves startup crash on low memory devices [#11109](https://github.com/mapbox/mapbox-gl-native/pull/11109) + - Programmatically create GLSurfaceView, solves fragment bug [#11124](https://github.com/mapbox/mapbox-gl-native/pull/11124) + - Proguard config for optional location provider, solves obfuscation warnings [#11127](https://github.com/mapbox/mapbox-gl-native/pull/11127) + - MapView weak reference in global layout listener, solves memory leak [#11128](https://github.com/mapbox/mapbox-gl-native/pull/11128) + ## 5.4.0 - January 30, 2018 - Blacklist Adreno 2xx GPU for VAO support [#11047](https://github.com/mapbox/mapbox-gl-native/pull/11047) - Bearing tracking mode GPS_NORTH_FACING [#11095](https://github.com/mapbox/mapbox-gl-native/pull/11095) -- cgit v1.2.1 From 9941b562389aac04a6bcf926bef176ef15b1148e Mon Sep 17 00:00:00 2001 From: Tobrun Date: Fri, 2 Feb 2018 10:30:05 +0100 Subject: [android] - don't invoke onLowMemory on map when the map isn't fully created yet --- .../src/main/java/com/mapbox/mapboxsdk/maps/MapView.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java index 6db11db49f..a8e065c45e 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java @@ -477,7 +477,9 @@ public class MapView extends FrameLayout { */ @UiThread public void onLowMemory() { - nativeMapView.onLowMemory(); + if (nativeMapView != null) { + nativeMapView.onLowMemory(); + } } /** -- cgit v1.2.1 From f422f794467dd4a43e2ed2d79c298141769defc9 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Fri, 9 Feb 2018 14:48:14 +0100 Subject: [android] - update changelog for 5.4.1 --- platform/android/CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index 1e51725ed0..7827bc129f 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -2,6 +2,13 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started. +## 5.4.1 - February 9, 2018 + - Don't recreate TextureView surface as part of view resizing, solves OOM crashes [#11148](https://github.com/mapbox/mapbox-gl-native/pull/11148) + - Don't invoke OnLowMemory before map is ready, solves startup crash on low memory devices [#11109](https://github.com/mapbox/mapbox-gl-native/pull/11109) + - Programmatically create GLSurfaceView, solves fragment bug [#11124](https://github.com/mapbox/mapbox-gl-native/pull/11124) + - Proguard config for optional location provider, solves obfuscation warnings [#11127](https://github.com/mapbox/mapbox-gl-native/pull/11127) + - MapView weak reference in global layout listener, solves memory leak [#11128](https://github.com/mapbox/mapbox-gl-native/pull/11128) + ## 5.4.0 - January 30, 2018 - Blacklist Adreno 2xx GPU for VAO support [#11047](https://github.com/mapbox/mapbox-gl-native/pull/11047) - Bearing tracking mode GPS_NORTH_FACING [#11095](https://github.com/mapbox/mapbox-gl-native/pull/11095) -- cgit v1.2.1 From cdaf90d2e75a8d8f6924caf657ca5ff3d1ae6adb Mon Sep 17 00:00:00 2001 From: Tobrun Date: Fri, 9 Feb 2018 15:07:15 +0100 Subject: [android] - update SNAPSHOT version to v5.4.2 --- platform/android/MapboxGLAndroidSDK/gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/android/MapboxGLAndroidSDK/gradle.properties b/platform/android/MapboxGLAndroidSDK/gradle.properties index 011052b521..8839f21d8f 100644 --- a/platform/android/MapboxGLAndroidSDK/gradle.properties +++ b/platform/android/MapboxGLAndroidSDK/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.mapbox.mapboxsdk -VERSION_NAME=5.4.1-SNAPSHOT +VERSION_NAME=5.4.2-SNAPSHOT POM_DESCRIPTION=Mapbox GL Android SDK POM_URL=https://github.com/mapbox/mapbox-gl-native -- cgit v1.2.1 From 94734441ce34fa8cfaa2a5148ece676d75610e69 Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Mon, 12 Feb 2018 12:06:36 -0800 Subject: Update & simplify INSTALL.md (#11155) * Update & simplify INSTALL.md * Add "Launch Xcode" step * Update INSTALL.md --- INSTALL.md | 58 +++++++++++++++++++++++----------------------------------- 1 file changed, 23 insertions(+), 35 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index fb619770a1..a28348d15c 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -7,50 +7,38 @@ that you can download instantly and get started with fast](https://www.mapbox.co Still with us? These are the instructions you'll need to build Mapbox GL Native from source on a variety of platforms and set up a development environment. -Your journey will start with getting the source code, then installing the -dependencies, and then setting up a development environment, which varies -depending on your operating system and what platform you want to develop for. +Your journey will start with installing dependencies, then getting the source code, and +then setting up a development environment, which varies depending on your +operating system and what platform you want to develop for. -## 1: Getting the source +## 1: Installing dependencies -Clone the git repository: +### macOS - git clone https://github.com/mapbox/mapbox-gl-native.git - cd mapbox-gl-native + 1. Install [Xcode](https://developer.apple.com/xcode/) + 2. Launch Xcode and install any updates + 3. Install [Homebrew](http://brew.sh) + 4. Install [Node.js](https://nodejs.org/), [CMake](https://cmake.org/), and [ccache](https://ccache.samba.org) with `brew install nodejs cmake ccache` + 5. Install [xcpretty](https://github.com/supermarin/xcpretty) with `[sudo] gem install xcpretty` (optional, used for prettifying command line builds) -## 2: Installing dependencies +### Linux -These dependencies are required for all operating systems and all platform -targets. - - - Modern C++ compiler that supports `-std=c++14`\* - - clang++ 3.5 or later _or_ - - g++-4.9 or later - - [CMake](https://cmake.org/) 3.1 or later (for build only) - - [cURL](https://curl.haxx.se) (for build only) - - [Node.js](https://nodejs.org/) 4.2.1 or later (for build only) - - [`pkg-config`](https://wiki.freedesktop.org/www/Software/pkg-config/) (for build only) - -**Note**: We partially support C++14 because GCC 4.9 does not fully implement the -final draft of the C++14 standard. More information in [DEVELOPING.md](DEVELOPING.md). - -Depending on your operating system and target, you'll need additional -dependencies: - -### Additional dependencies for Linux +Install the following: + - `clang++` 3.5 or later or `g++` 4.9 or later + - [git](https://git-scm.com/) + - [CMake](https://cmake.org/) 3.1 or later + - [cURL](https://curl.haxx.se) + - [Node.js](https://nodejs.org/) 4.2.1 or later - [`libcurl`](http://curl.haxx.se/libcurl/) (depends on OpenSSL) + - [ccache](https://ccache.samba.org) (optional, improves recompilation performance) -### Additional dependencies for macOS - - - Apple Command Line Tools (available at [Apple Developer](https://developer.apple.com/download/more/)) - - [Homebrew](http://brew.sh) - - [Cask](http://caskroom.io/) (if building for Android) - - [xcpretty](https://github.com/supermarin/xcpretty) (`gem install xcpretty`) +## 2: Getting the source -### Optional dependencies + Clone the git repository: -- [ccache](https://ccache.samba.org) (for build only; improves recompilation performance) + git clone https://github.com/mapbox/mapbox-gl-native.git + cd mapbox-gl-native ## 3: Setting up a development environment & building @@ -58,7 +46,7 @@ See the relevant SDK documentation for next steps: * [Maps SDK for Android](platform/android/) * [Maps SDK for iOS](platform/ios/) -* [Maps SDK for iOS](platform/macos/) +* [Maps SDK for macOS](platform/macos/) * [Mapbox Qt SDK](platform/qt/) * [Mapbox GL Native on Linux](platform/linux/) * [node-mapbox-gl-native](platform/node/) -- cgit v1.2.1 From 560afaf1fb8f1cd20cef261da4992cd8a5478f88 Mon Sep 17 00:00:00 2001 From: Asheem Mamoowala Date: Mon, 12 Feb 2018 12:32:22 -0800 Subject: Cherry pick #11176 for Agua patch --- include/mbgl/style/conversion/tileset.hpp | 2 +- test/style/conversion/tileset.test.cpp | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/include/mbgl/style/conversion/tileset.hpp b/include/mbgl/style/conversion/tileset.hpp index 6577e39576..6ec46aa7b6 100644 --- a/include/mbgl/style/conversion/tileset.hpp +++ b/include/mbgl/style/conversion/tileset.hpp @@ -12,7 +12,7 @@ struct Converter { public: bool validateLatitude(const double lat) const { - return lat < 90 && lat > -90; + return lat <= 90 && lat >= -90; } template diff --git a/test/style/conversion/tileset.test.cpp b/test/style/conversion/tileset.test.cpp index 8002cd038f..9487277cca 100644 --- a/test/style/conversion/tileset.test.cpp +++ b/test/style/conversion/tileset.test.cpp @@ -52,6 +52,16 @@ TEST(Tileset, InvalidBounds) { } } +TEST(Tileset, ValidWorldBounds) { + Error error; + mbgl::optional converted = convertJSON(R"JSON({ + "tiles": ["http://mytiles"], + "bounds": [-180, -90, 180, 90] + })JSON", error); + EXPECT_TRUE((bool) converted); + EXPECT_EQ(converted->bounds, LatLngBounds::hull({90, -180}, {-90, 180})); +} + TEST(Tileset, FullConversion) { Error error; Tileset converted = *convertJSON(R"JSON({ -- cgit v1.2.1 From 9ee99c91bbf995302ead77a3f6fa22443f50a910 Mon Sep 17 00:00:00 2001 From: Julian Rex Date: Mon, 12 Feb 2018 16:37:21 -0500 Subject: [ios] Adds camera change delegate methods with reason parameter. (#11151) Added missing notification handler for UIApplicationWillResignActiveNotification. --- platform/ios/app/MBXViewController.m | 4 +- platform/ios/ios.xcodeproj/project.pbxproj | 18 ++- platform/ios/src/MGLCameraChangeReason.h | 61 +++++++ platform/ios/src/MGLMapView.mm | 178 ++++++++++++++++----- platform/ios/src/MGLMapViewDelegate.h | 119 +++++++++++--- .../test/MGLMapViewDelegateIntegrationTests.swift | 11 ++ platform/ios/uitest/MapViewTests.m | 7 +- 7 files changed, 325 insertions(+), 73 deletions(-) create mode 100644 platform/ios/src/MGLCameraChangeReason.h diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m index 2c3d26b489..1046644f8c 100644 --- a/platform/ios/app/MBXViewController.m +++ b/platform/ios/app/MBXViewController.m @@ -1279,6 +1279,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { - (void)styleDynamicPointCollection { [self.mapView setCenterCoordinate:CLLocationCoordinate2DMake(36.9979, -109.0441) zoomLevel:14 animated:NO]; + CLLocationCoordinate2D coordinates[] = { {37.00145594210082, -109.04960632324219}, {37.00173012609867, -109.0404224395752}, @@ -1884,7 +1885,8 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { [self updateHUD]; } -- (void)mapView:(MGLMapView *)mapView regionDidChangeAnimated:(BOOL)animated { +- (void)mapView:(MGLMapView *)mapView regionDidChangeWithReason:(MGLCameraChangeReason)reason animated:(BOOL)animated +{ [self updateHUD]; } diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index 2325f3d3ce..bbd4067534 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -251,6 +251,8 @@ AC518E00201BB55A00EBC820 /* MGLTelemetryConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = AC518DFD201BB55A00EBC820 /* MGLTelemetryConfig.h */; }; AC518E03201BB56000EBC820 /* MGLTelemetryConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = AC518DFE201BB55A00EBC820 /* MGLTelemetryConfig.m */; }; AC518E04201BB56100EBC820 /* MGLTelemetryConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = AC518DFE201BB55A00EBC820 /* MGLTelemetryConfig.m */; }; + CA55CD41202C16AA00CE7095 /* MGLCameraChangeReason.h in Headers */ = {isa = PBXBuildFile; fileRef = CA55CD3E202C16AA00CE7095 /* MGLCameraChangeReason.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CA55CD42202C16AA00CE7095 /* MGLCameraChangeReason.h in Headers */ = {isa = PBXBuildFile; fileRef = CA55CD3E202C16AA00CE7095 /* MGLCameraChangeReason.h */; settings = {ATTRIBUTES = (Public, ); }; }; DA00FC8E1D5EEB0D009AABC8 /* MGLAttributionInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = DA00FC8C1D5EEB0D009AABC8 /* MGLAttributionInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; DA00FC8F1D5EEB0D009AABC8 /* MGLAttributionInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = DA00FC8C1D5EEB0D009AABC8 /* MGLAttributionInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; DA00FC901D5EEB0D009AABC8 /* MGLAttributionInfo.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA00FC8D1D5EEB0D009AABC8 /* MGLAttributionInfo.mm */; }; @@ -756,6 +758,7 @@ 96F3F73B1F5711F1003E2D2C /* MGLUserLocationHeadingIndicator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLUserLocationHeadingIndicator.h; sourceTree = ""; }; AC518DFD201BB55A00EBC820 /* MGLTelemetryConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLTelemetryConfig.h; sourceTree = ""; }; AC518DFE201BB55A00EBC820 /* MGLTelemetryConfig.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLTelemetryConfig.m; sourceTree = ""; }; + CA55CD3E202C16AA00CE7095 /* MGLCameraChangeReason.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLCameraChangeReason.h; sourceTree = ""; }; DA00FC8C1D5EEB0D009AABC8 /* MGLAttributionInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAttributionInfo.h; sourceTree = ""; }; DA00FC8D1D5EEB0D009AABC8 /* MGLAttributionInfo.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLAttributionInfo.mm; sourceTree = ""; }; DA0CD58F1CF56F6A00A5F5A5 /* MGLFeatureTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLFeatureTests.mm; path = ../../darwin/test/MGLFeatureTests.mm; sourceTree = ""; }; @@ -1449,19 +1452,20 @@ DA8848331CBAFB2A00AB86E3 /* Kit */ = { isa = PBXGroup; children = ( - 355ADFF91E9281C300F3939D /* Views */, - 35CE617F1D4165C2004F2359 /* Categories */, DAD165841CF4D06B001FF4B9 /* Annotations */, + 35CE617F1D4165C2004F2359 /* Categories */, + DA88487F1CBB033F00AB86E3 /* Fabric */, + DA8848881CBB036000AB86E3 /* SMCalloutView */, DAD165851CF4D08B001FF4B9 /* Telemetry */, + 355ADFF91E9281C300F3939D /* Views */, + CA55CD3E202C16AA00CE7095 /* MGLCameraChangeReason.h */, DA704CC01F65A475004B3F28 /* MGLMapAccessibilityElement.h */, DA704CC11F65A475004B3F28 /* MGLMapAccessibilityElement.mm */, - DA8848361CBAFB8500AB86E3 /* MGLMapView.h */, DA17BE2F1CC4BAC300402C41 /* MGLMapView_Private.h */, + DA8848361CBAFB8500AB86E3 /* MGLMapView.h */, + DA88484A1CBAFB9800AB86E3 /* MGLMapView.mm */, DA8848371CBAFB8500AB86E3 /* MGLMapView+IBAdditions.h */, DA737EE01D056A4E005BDA16 /* MGLMapViewDelegate.h */, - DA88484A1CBAFB9800AB86E3 /* MGLMapView.mm */, - DA88487F1CBB033F00AB86E3 /* Fabric */, - DA8848881CBB036000AB86E3 /* SMCalloutView */, ); name = Kit; path = src; @@ -1764,6 +1768,7 @@ 350098DC1D484E60004B2AF0 /* NSValue+MGLStyleAttributeAdditions.h in Headers */, DA8848231CBAFA6200AB86E3 /* MGLOfflineStorage_Private.h in Headers */, 404326891D5B9B27007111BD /* MGLAnnotationContainerView_Private.h in Headers */, + CA55CD41202C16AA00CE7095 /* MGLCameraChangeReason.h in Headers */, 1FB7DAAF1F2A4DBD00410606 /* MGLVectorSource+MGLAdditions.h in Headers */, DA88483B1CBAFB8500AB86E3 /* MGLCalloutView.h in Headers */, 35E0CFE61D3E501500188327 /* MGLStyle_Private.h in Headers */, @@ -1896,6 +1901,7 @@ 3510FFF11D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h in Headers */, 35D3A1E71E9BE7EC002B38EE /* MGLScaleBar.h in Headers */, 35E0CFE71D3E501500188327 /* MGLStyle_Private.h in Headers */, + CA55CD42202C16AA00CE7095 /* MGLCameraChangeReason.h in Headers */, DABFB86D1CBE9A0F00D62B32 /* MGLAnnotationImage.h in Headers */, DABFB8721CBE9A0F00D62B32 /* MGLUserLocation.h in Headers */, 927FBD001F4DB05500F8BF1F /* MGLMapSnapshotter.h in Headers */, diff --git a/platform/ios/src/MGLCameraChangeReason.h b/platform/ios/src/MGLCameraChangeReason.h new file mode 100644 index 0000000000..6c6b3636ba --- /dev/null +++ b/platform/ios/src/MGLCameraChangeReason.h @@ -0,0 +1,61 @@ +#import "MGLFoundation.h" + +/** + :nodoc: + Bitmask values that describe why a camera move occurred. + + Values of this type are passed to the `MGLMapView`'s delegate in the following methods: + + - `-mapView:shouldChangeFromCamera:toCamera:reason:` + - `-mapView:regionWillChangeWithReason:animated:` + - `-mapView:regionIsChangingWithReason:` + - `-mapView:regionDidChangeWithReason:animated:` + + It's important to note that it's almost impossible to perform a rotate without zooming (in or out), + so if you'll often find `MGLCameraChangeReasonGesturePinch` set alongside `MGLCameraChangeReasonGestureRotate`. + + Since there are several reasons why a zoom or rotation has occurred, it is worth considering + creating a combined constant, for example: + + ``` + static const MGLCameraChangeReason anyZoom = MGLCameraChangeReasonGesturePinch | + MGLCameraChangeReasonGestureZoomIn | + MGLCameraChangeReasonGestureZoomOut | + MGLCameraChangeReasonGestureOneFingerZoom; + + static const MGLCameraChangeReason anyRotation = MGLCameraChangeReasonResetNorth | MGLCameraChangeReasonGestureRotate; + ``` + */ +typedef NS_OPTIONS(NSUInteger, MGLCameraChangeReason) +{ + /// :nodoc: The reason for the camera change has not be specified. + MGLCameraChangeReasonNone = 0, + + /// :nodoc: Set when a public API that moves the camera is called. This may be set for some gestures, + /// for example MGLCameraChangeReasonResetNorth. + MGLCameraChangeReasonProgrammatic = 1 << 0, + + /// :nodoc: The user tapped the compass to reset the map orientation so North is up. + MGLCameraChangeReasonResetNorth = 1 << 1, + + /// :nodoc: The user panned the map. + MGLCameraChangeReasonGesturePan = 1 << 2, + + /// :nodoc: The user pinched to zoom in/out. + MGLCameraChangeReasonGesturePinch = 1 << 3, + + // :nodoc: The user rotated the map. + MGLCameraChangeReasonGestureRotate = 1 << 4, + + /// :nodoc: The user zoomed the map in (one finger double tap). + MGLCameraChangeReasonGestureZoomIn = 1 << 5, + + /// :nodoc: The user zoomed the map out (two finger single tap). + MGLCameraChangeReasonGestureZoomOut = 1 << 6, + + /// :nodoc: The user long pressed on the map for a quick zoom (single tap, then long press and drag up/down). + MGLCameraChangeReasonGestureOneFingerZoom = 1 << 7, + + // :nodoc: The user panned with two fingers to tilt the map (two finger drag). + MGLCameraChangeReasonGestureTilt = 1 << 8 +}; diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 0849cbd12f..f2141c3840 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -209,6 +209,8 @@ public: @property (nonatomic) UILongPressGestureRecognizer *quickZoom; @property (nonatomic) UIPanGestureRecognizer *twoFingerDrag; +@property (nonatomic) MGLCameraChangeReason cameraChangeReasonBitmask; + /// Mapping from reusable identifiers to annotation images. @property (nonatomic) NS_MUTABLE_DICTIONARY_OF(NSString *, MGLAnnotationImage *) *annotationImagesByIdentifier; @@ -502,10 +504,6 @@ public: _doubleTap.numberOfTapsRequired = 2; [self addGestureRecognizer:_doubleTap]; - _singleTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTapGesture:)]; - [_singleTapGestureRecognizer requireGestureRecognizerToFail:_doubleTap]; - _singleTapGestureRecognizer.delegate = self; - [self addGestureRecognizer:_singleTapGestureRecognizer]; _twoFingerDrag = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleTwoFingerDragGesture:)]; _twoFingerDrag.minimumNumberOfTouches = 2; @@ -530,16 +528,25 @@ public: [_quickZoom requireGestureRecognizerToFail:_doubleTap]; [self addGestureRecognizer:_quickZoom]; + _singleTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTapGesture:)]; + [_singleTapGestureRecognizer requireGestureRecognizerToFail:_doubleTap]; + _singleTapGestureRecognizer.delegate = self; + [_singleTapGestureRecognizer requireGestureRecognizerToFail:_quickZoom]; + [self addGestureRecognizer:_singleTapGestureRecognizer]; + // observe app activity // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willTerminate) name:UIApplicationWillTerminateNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sleepGL:) name:UIApplicationDidEnterBackgroundNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(wakeGL:) name:UIApplicationWillEnterForegroundNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sleepGL:) name:UIApplicationWillResignActiveNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(wakeGL:) name:UIApplicationDidBecomeActiveNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMemoryWarning) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceOrientationDidChange:) name:UIDeviceOrientationDidChangeNotification object:nil]; [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; + // set initial position // mbgl::CameraOptions options; @@ -547,6 +554,9 @@ public: mbgl::EdgeInsets padding = MGLEdgeInsetsFromNSEdgeInsets(self.contentInset); options.padding = padding; options.zoom = 0; + + _cameraChangeReasonBitmask = MGLCameraChangeReasonNone; + _mbglMap->jumpTo(options); _pendingLatitude = NAN; _pendingLongitude = NAN; @@ -1233,6 +1243,8 @@ public: - (void)handleCompassTapGesture:(__unused id)sender { + self.cameraChangeReasonBitmask |= MGLCameraChangeReasonResetNorth; + [self resetNorthAnimated:YES]; if (self.userTrackingMode == MGLUserTrackingModeFollowWithHeading || @@ -1255,6 +1267,7 @@ public: - (void)notifyGestureDidBegin { BOOL animated = NO; + [self cameraWillChangeAnimated:animated]; _mbglMap->setGestureInProgress(true); _changeDelimiterSuppressionDepth++; @@ -1278,6 +1291,23 @@ public: return _changeDelimiterSuppressionDepth > 0; } +- (BOOL)_shouldChangeFromCamera:(nonnull MGLMapCamera *)oldCamera toCamera:(nonnull MGLMapCamera *)newCamera +{ + // Check delegates first + if ([self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:reason:)]) + { + return [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:newCamera reason:self.cameraChangeReasonBitmask]; + } + else if ([self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)]) + { + return [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:newCamera]; + } + else + { + return YES; + } +} + - (void)handlePanGesture:(UIPanGestureRecognizer *)pan { if ( ! self.isScrollEnabled) return; @@ -1285,7 +1315,9 @@ public: _mbglMap->cancelTransitions(); MGLMapCamera *oldCamera = self.camera; - + + self.cameraChangeReasonBitmask |= MGLCameraChangeReasonGesturePan; + if (pan.state == UIGestureRecognizerStateBegan) { [self trackGestureEvent:MGLEventGesturePanStart forRecognizer:pan]; @@ -1299,9 +1331,8 @@ public: CGPoint delta = [pan translationInView:pan.view]; MGLMapCamera *toCamera = [self cameraByPanningWithTranslation:delta panGesture:pan]; - - if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] || - [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera]) + + if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera]) { _mbglMap->moveBy({ delta.x, delta.y }); [pan setTranslation:CGPointZero inView:pan.view]; @@ -1323,9 +1354,8 @@ public: { CGPoint offset = CGPointMake(velocity.x * self.decelerationRate / 4, velocity.y * self.decelerationRate / 4); MGLMapCamera *toCamera = [self cameraByPanningWithTranslation:offset panGesture:pan]; - - if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] || - [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera]) + + if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera]) { _mbglMap->moveBy({ offset.x, offset.y }, MGLDurationFromTimeInterval(self.decelerationRate)); } @@ -1356,6 +1386,8 @@ public: CGPoint centerPoint = [self anchorPointForGesture:pinch]; MGLMapCamera *oldCamera = self.camera; + self.cameraChangeReasonBitmask |= MGLCameraChangeReasonGesturePinch; + if (pinch.state == UIGestureRecognizerStateBegan) { [self trackGestureEvent:MGLEventGesturePinchStart forRecognizer:pinch]; @@ -1372,9 +1404,8 @@ public: // Calculates the final camera zoom, has no effect within current map camera. MGLMapCamera *toCamera = [self cameraByZoomingToZoomLevel:newZoom aroundAnchorPoint:centerPoint]; - - if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] || - [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera]) + + if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera]) { _mbglMap->setZoom(newZoom, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }); // The gesture recognizer only reports the gesture’s current center @@ -1426,9 +1457,8 @@ public: // Calculates the final camera zoom, this has no effect within current map camera. double zoom = log2(newScale); MGLMapCamera *toCamera = [self cameraByZoomingToZoomLevel:zoom aroundAnchorPoint:centerPoint]; - - if ([self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] - && ![self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera]) + + if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera]) { drift = NO; } else { @@ -1454,7 +1484,9 @@ public: CGPoint centerPoint = [self anchorPointForGesture:rotate]; MGLMapCamera *oldCamera = self.camera; - + + self.cameraChangeReasonBitmask |= MGLCameraChangeReasonGestureRotate; + if (rotate.state == UIGestureRecognizerStateBegan) { [self trackGestureEvent:MGLEventGestureRotateStart forRecognizer:rotate]; @@ -1481,9 +1513,8 @@ public: } MGLMapCamera *toCamera = [self cameraByRotatingToDirection:newDegrees aroundAnchorPoint:centerPoint]; - - if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] || - [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera]) + + if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera]) { _mbglMap->setBearing(newDegrees, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }); } @@ -1501,9 +1532,8 @@ public: CGFloat newDegrees = MGLDegreesFromRadians(newRadians) * -1; MGLMapCamera *toCamera = [self cameraByRotatingToDirection:newDegrees aroundAnchorPoint:centerPoint]; - - if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] || - [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera]) + + if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera]) { _mbglMap->setBearing(newDegrees, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }, MGLDurationFromTimeInterval(decelerationRate)); @@ -1548,6 +1578,7 @@ public: } [self deselectAnnotation:self.selectedAnnotation animated:YES]; UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nextElement); + return; } @@ -1558,7 +1589,7 @@ public: CGRect positionRect = [self positioningRectForAnnotation:annotation defaultCalloutPoint:calloutPoint]; [self selectAnnotation:annotation animated:YES calloutPositioningRect:positionRect]; } - else + else if (self.selectedAnnotation) { [self deselectAnnotation:self.selectedAnnotation animated:YES]; } @@ -1640,6 +1671,8 @@ public: if (doubleTap.state == UIGestureRecognizerStateEnded) { + self.cameraChangeReasonBitmask |= MGLCameraChangeReasonGestureZoomIn; + MGLMapCamera *oldCamera = self.camera; double newZoom = round(self.zoomLevel) + 1.0; @@ -1647,9 +1680,8 @@ public: CGPoint gesturePoint = [self anchorPointForGesture:doubleTap]; MGLMapCamera *toCamera = [self cameraByZoomingToZoomLevel:newZoom aroundAnchorPoint:gesturePoint]; - - if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] || - [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera]) + + if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera]) { [self trackGestureEvent:MGLEventGestureDoubleTap forRecognizer:doubleTap]; @@ -1676,9 +1708,13 @@ public: _mbglMap->cancelTransitions(); + self.cameraChangeReasonBitmask |= MGLCameraChangeReasonGestureZoomOut; + if (twoFingerTap.state == UIGestureRecognizerStateBegan) { [self trackGestureEvent:MGLEventGestureTwoFingerSingleTap forRecognizer:twoFingerTap]; + + [self notifyGestureDidBegin]; } else if (twoFingerTap.state == UIGestureRecognizerStateEnded) { @@ -1689,9 +1725,8 @@ public: CGPoint gesturePoint = [self anchorPointForGesture:twoFingerTap]; MGLMapCamera *toCamera = [self cameraByZoomingToZoomLevel:newZoom aroundAnchorPoint:gesturePoint]; - - if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] || - [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera]) + + if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera]) { mbgl::ScreenCoordinate center(gesturePoint.x, gesturePoint.y); _mbglMap->setZoom(newZoom, center, MGLDurationFromTimeInterval(MGLAnimationDuration)); @@ -1711,7 +1746,9 @@ public: if ( ! self.isZoomEnabled) return; _mbglMap->cancelTransitions(); - + + self.cameraChangeReasonBitmask |= MGLCameraChangeReasonGestureOneFingerZoom; + if (quickZoom.state == UIGestureRecognizerStateBegan) { [self trackGestureEvent:MGLEventGestureQuickZoom forRecognizer:quickZoom]; @@ -1734,9 +1771,8 @@ public: MGLMapCamera *oldCamera = self.camera; MGLMapCamera *toCamera = [self cameraByZoomingToZoomLevel:newZoom aroundAnchorPoint:centerPoint]; - - if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] || - [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera]) + + if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera]) { _mbglMap->setZoom(newZoom, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }); } @@ -1756,6 +1792,8 @@ public: _mbglMap->cancelTransitions(); + self.cameraChangeReasonBitmask |= MGLCameraChangeReasonGestureTilt; + if (twoFingerDrag.state == UIGestureRecognizerStateBegan) { [self trackGestureEvent:MGLEventGesturePitchStart forRecognizer:twoFingerDrag]; @@ -1775,8 +1813,7 @@ public: MGLMapCamera *oldCamera = self.camera; MGLMapCamera *toCamera = [self cameraByTiltingToPitch:pitchNew]; - if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] || - [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera]) + if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera]) { _mbglMap->setPitch(pitchNew, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }); } @@ -2852,6 +2889,8 @@ public: { self.userTrackingMode = MGLUserTrackingModeNone; + self.cameraChangeReasonBitmask |= MGLCameraChangeReasonProgrammatic; + [self _setCenterCoordinate:centerCoordinate edgePadding:self.contentInset zoomLevel:zoomLevel direction:direction duration:animated ? MGLAnimationDuration : 0 animationTimingFunction:nil completionHandler:completion]; } @@ -2901,6 +2940,9 @@ public: } _mbglMap->cancelTransitions(); + + self.cameraChangeReasonBitmask |= MGLCameraChangeReasonProgrammatic; + _mbglMap->easeTo(cameraOptions, animationOptions); } @@ -2924,6 +2966,8 @@ public: if (zoomLevel == self.zoomLevel) return; _mbglMap->cancelTransitions(); + self.cameraChangeReasonBitmask |= MGLCameraChangeReasonProgrammatic; + CGFloat duration = animated ? MGLAnimationDuration : 0; _mbglMap->setZoom(zoomLevel, @@ -3012,6 +3056,9 @@ public: - (void)setVisibleCoordinates:(const CLLocationCoordinate2D *)coordinates count:(NSUInteger)count edgePadding:(UIEdgeInsets)insets direction:(CLLocationDirection)direction duration:(NSTimeInterval)duration animationTimingFunction:(nullable CAMediaTimingFunction *)function completionHandler:(nullable void (^)(void))completion { self.userTrackingMode = MGLUserTrackingModeNone; + + self.cameraChangeReasonBitmask |= MGLCameraChangeReasonProgrammatic; + [self _setVisibleCoordinates:coordinates count:count edgePadding:insets direction:direction duration:duration animationTimingFunction:function completionHandler:completion]; } @@ -3061,6 +3108,9 @@ public: [self willChangeValueForKey:@"visibleCoordinateBounds"]; _mbglMap->cancelTransitions(); + + self.cameraChangeReasonBitmask |= MGLCameraChangeReasonProgrammatic; + _mbglMap->easeTo(cameraOptions, animationOptions); [self didChangeValueForKey:@"visibleCoordinateBounds"]; } @@ -3094,6 +3144,8 @@ public: CGFloat duration = animated ? MGLAnimationDuration : 0; + self.cameraChangeReasonBitmask |= MGLCameraChangeReasonProgrammatic; + if (self.userTrackingMode == MGLUserTrackingModeNone) { _mbglMap->setBearing(direction, @@ -3178,6 +3230,9 @@ public: [self willChangeValueForKey:@"camera"]; _mbglMap->cancelTransitions(); + + self.cameraChangeReasonBitmask |= MGLCameraChangeReasonProgrammatic; + mbgl::CameraOptions cameraOptions = [self cameraOptionsObjectForAnimatingToCamera:camera edgePadding:edgePadding]; _mbglMap->easeTo(cameraOptions, animationOptions); [self didChangeValueForKey:@"camera"]; @@ -3234,6 +3289,9 @@ public: [self willChangeValueForKey:@"camera"]; _mbglMap->cancelTransitions(); + + self.cameraChangeReasonBitmask |= MGLCameraChangeReasonProgrammatic; + mbgl::CameraOptions cameraOptions = [self cameraOptionsObjectForAnimatingToCamera:camera edgePadding:insets]; _mbglMap->flyTo(cameraOptions, animationOptions); [self didChangeValueForKey:@"camera"]; @@ -3394,6 +3452,13 @@ public: return [self metersPerPointAtLatitude:latitude]; } +#pragma mark - Camera Change Reason - + +- (void)resetCameraChangeReason +{ + self.cameraChangeReasonBitmask = MGLCameraChangeReasonNone; +} + #pragma mark - Styling - - (NS_ARRAY_OF(NSURL *) *)bundledStyleURLs @@ -5340,9 +5405,16 @@ public: } } - if ( ! [self isSuppressingChangeDelimiters] && [self.delegate respondsToSelector:@selector(mapView:regionWillChangeAnimated:)]) + if ( ! [self isSuppressingChangeDelimiters] ) { - [self.delegate mapView:self regionWillChangeAnimated:animated]; + if ([self.delegate respondsToSelector:@selector(mapView:regionWillChangeWithReason:animated:)]) + { + [self.delegate mapView:self regionWillChangeWithReason:self.cameraChangeReasonBitmask animated:animated]; + } + else if ([self.delegate respondsToSelector:@selector(mapView:regionWillChangeAnimated:)]) + { + [self.delegate mapView:self regionWillChangeAnimated:animated]; + } } } @@ -5356,8 +5428,12 @@ public: if (!self.scaleBar.hidden) { [(MGLScaleBar *)self.scaleBar setMetersPerPoint:[self metersPerPointAtLatitude:self.centerCoordinate.latitude]]; } - - if ([self.delegate respondsToSelector:@selector(mapViewRegionIsChanging:)]) + + if ([self.delegate respondsToSelector:@selector(mapView:regionIsChangingWithReason:)]) + { + [self.delegate mapView:self regionIsChangingWithReason:self.cameraChangeReasonBitmask]; + } + else if ([self.delegate respondsToSelector:@selector(mapViewRegionIsChanging:)]) { [self.delegate mapViewRegionIsChanging:self]; } @@ -5370,9 +5446,13 @@ public: [self updateCompass]; - if ( ! [self isSuppressingChangeDelimiters] && [self.delegate respondsToSelector:@selector(mapView:regionDidChangeAnimated:)]) + if ( ! [self isSuppressingChangeDelimiters]) { - if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive) + BOOL respondsToSelector = [self.delegate respondsToSelector:@selector(mapView:regionDidChangeAnimated:)]; + BOOL respondsToSelectorWithReason = [self.delegate respondsToSelector:@selector(mapView:regionDidChangeWithReason:animated:)]; + + if ((respondsToSelector || respondsToSelectorWithReason) && + ([UIApplication sharedApplication].applicationState == UIApplicationStateActive)) { _featureAccessibilityElements = nil; _visiblePlaceFeatures = nil; @@ -5384,7 +5464,17 @@ public: UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil); } } - [self.delegate mapView:self regionDidChangeAnimated:animated]; + + if (respondsToSelectorWithReason) + { + [self.delegate mapView:self regionDidChangeWithReason:self.cameraChangeReasonBitmask animated:animated]; + } + else if (respondsToSelector) + { + [self.delegate mapView:self regionDidChangeAnimated:animated]; + } + + [self resetCameraChangeReason]; } } diff --git a/platform/ios/src/MGLMapViewDelegate.h b/platform/ios/src/MGLMapViewDelegate.h index 096711fcbb..0368d8413c 100644 --- a/platform/ios/src/MGLMapViewDelegate.h +++ b/platform/ios/src/MGLMapViewDelegate.h @@ -1,6 +1,7 @@ #import #import "MGLTypes.h" +#import "MGLCameraChangeReason.h" NS_ASSUME_NONNULL_BEGIN @@ -21,17 +22,80 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark Responding to Map Position Changes +/** + Asks the delegate whether the map view should be allowed to change from the + existing camera to the new camera in response to a user gesture. + + This method is called as soon as the user gesture is recognized. It is not + called in response to a programmatic camera change, such as by setting the + `centerCoordinate` property or calling `-flyToCamera:completionHandler:`. + + This method is called many times during gesturing, so you should avoid performing + complex or performance-intensive tasks in your implementation. + + @param mapView The map view that the user is manipulating. + @param oldCamera The camera representing the viewpoint at the moment the + gesture is recognized. If this method returns `NO`, the map view’s camera + continues to be this camera. + @param newCamera The expected camera after the gesture completes. If this + method returns `YES`, this camera becomes the map view’s camera. + @return A Boolean value indicating whether the map view should stay at + `oldCamera` or change to `newCamera`. + */ +- (BOOL)mapView:(MGLMapView *)mapView shouldChangeFromCamera:(MGLMapCamera *)oldCamera toCamera:(MGLMapCamera *)newCamera; + +/** + :nodoc: + Asks the delegate whether the map view should be allowed to change from the + existing camera to the new camera in response to a user gesture. + + This method is called as soon as the user gesture is recognized. It is not + called in response to a programmatic camera change, such as by setting the + `centerCoordinate` property or calling `-flyToCamera:completionHandler:`. + + This method is called many times during gesturing, so you should avoid performing + complex or performance-intensive tasks in your implementation. + + @param mapView The map view that the user is manipulating. + @param oldCamera The camera representing the viewpoint at the moment the + gesture is recognized. If this method returns `NO`, the map view’s camera + continues to be this camera. + @param newCamera The expected camera after the gesture completes. If this + method returns `YES`, this camera becomes the map view’s camera. + @param reason The reason for the camera change. + @return A Boolean value indicating whether the map view should stay at + `oldCamera` or change to `newCamera`. + + @note If this method is implemented `-mapView:shouldChangeFromCamera:toCamera:` will not be called. + */ +- (BOOL)mapView:(MGLMapView *)mapView shouldChangeFromCamera:(MGLMapCamera *)oldCamera toCamera:(MGLMapCamera *)newCamera reason:(MGLCameraChangeReason)reason; + /** Tells the delegate that the viewpoint depicted by the map view is about to change. This method is called whenever the currently displayed map camera will start changing for any reason. - + @param mapView The map view whose viewpoint will change. @param animated Whether the change will cause an animated effect on the map. */ - (void)mapView:(MGLMapView *)mapView regionWillChangeAnimated:(BOOL)animated; +/** + :nodoc: + Tells the delegate that the viewpoint depicted by the map view is about to change. + + This method is called whenever the currently displayed map camera will start + changing for any reason. + + @param mapView The map view whose viewpoint will change. + @param animated Whether the change will cause an animated effect on the map. + @param reason The reason for the camera change. + + @note If this method is implemented `-mapView:regionWillChangeAnimated:` will not be called. + */ +- (void)mapView:(MGLMapView *)mapView regionWillChangeWithReason:(MGLCameraChangeReason)reason animated:(BOOL)animated; + /** Tells the delegate that the viewpoint depicted by the map view is changing. @@ -48,6 +112,26 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)mapViewRegionIsChanging:(MGLMapView *)mapView; +/** + :nodoc: + Tells the delegate that the viewpoint depicted by the map view is changing. + + This method is called as the currently displayed map camera changes as part of + an animation, whether due to a user gesture or due to a call to a method such + as `-[MGLMapView setCamera:animated:]`. This method can be called before + `-mapViewDidFinishLoadingMap:` is called. + + During the animation, this method may be called many times to report updates to + the viewpoint. Therefore, your implementation of this method should be as lightweight + as possible to avoid affecting performance. + + @param mapView The map view whose viewpoint is changing. + @param reason The reason for the camera change. + + @note If this method is implemented `-mapViewRegionIsChanging:` will not be called. + */ +- (void)mapView:(MGLMapView *)mapView regionIsChangingWithReason:(MGLCameraChangeReason)reason; + /** Tells the delegate that the viewpoint depicted by the map view has finished changing. @@ -62,26 +146,21 @@ NS_ASSUME_NONNULL_BEGIN - (void)mapView:(MGLMapView *)mapView regionDidChangeAnimated:(BOOL)animated; /** - Asks the delegate whether the map view should be allowed to change from the - existing camera to the new camera in response to a user gesture. - - This method is called as soon as the user gesture is recognized. It is not - called in response to a programmatic camera change, such as by setting the - `centerCoordinate` property or calling `-flyToCamera:completionHandler:`. - - This method is called many times during gesturing, so you should avoid performing - complex or performance-intensive tasks in your implementation. - - @param mapView The map view that the user is manipulating. - @param oldCamera The camera representing the viewpoint at the moment the - gesture is recognized. If this method returns `NO`, the map view’s camera - continues to be this camera. - @param newCamera The expected camera after the gesture completes. If this - method returns `YES`, this camera becomes the map view’s camera. - @return A Boolean value indicating whether the map view should stay at - `oldCamera` or change to `newCamera`. + :nodoc: + Tells the delegate that the viewpoint depicted by the map view has finished + changing. + + This method is called whenever the currently displayed map camera has finished + changing, after any calls to `-mapViewRegionIsChanging:` due to animation. Therefore, + this method can be called before `-mapViewDidFinishLoadingMap:` is called. + + @param mapView The map view whose viewpoint has changed. + @param animated Whether the change caused an animated effect on the map. + @param reason The reason for the camera change. + + @note If this method is implemented `-mapView:regionDidChangeAnimated:` will not be called. */ -- (BOOL)mapView:(MGLMapView *)mapView shouldChangeFromCamera:(MGLMapCamera *)oldCamera toCamera:(MGLMapCamera *)newCamera; +- (void)mapView:(MGLMapView *)mapView regionDidChangeWithReason:(MGLCameraChangeReason)reason animated:(BOOL)animated; #pragma mark Loading the Map diff --git a/platform/ios/test/MGLMapViewDelegateIntegrationTests.swift b/platform/ios/test/MGLMapViewDelegateIntegrationTests.swift index 50f101e86b..4d11b000b9 100644 --- a/platform/ios/test/MGLMapViewDelegateIntegrationTests.swift +++ b/platform/ios/test/MGLMapViewDelegateIntegrationTests.swift @@ -13,6 +13,10 @@ extension MGLMapViewDelegateIntegrationTests: MGLMapViewDelegate { func mapViewRegionIsChanging(_ mapView: MGLMapView) {} + func mapViewRegionIsChanging(_ mapView: MGLMapView, reason: MGLCameraChangeReason) {} + + func mapView(_ mapView: MGLMapView, regionIsChangingWith reason: MGLCameraChangeReason) {} + func mapView(_ mapView: MGLMapView, didChange mode: MGLUserTrackingMode, animated: Bool) {} func mapViewDidFinishLoadingMap(_ mapView: MGLMapView) {} @@ -33,10 +37,16 @@ extension MGLMapViewDelegateIntegrationTests: MGLMapViewDelegate { func mapView(_ mapView: MGLMapView, didDeselect annotation: MGLAnnotation) {} + func mapView(_ mapView: MGLMapView, didSingleTapAt coordinate: CLLocationCoordinate2D) {} + func mapView(_ mapView: MGLMapView, regionDidChangeAnimated animated: Bool) {} + func mapView(_ mapView: MGLMapView, regionDidChangeWith reason: MGLCameraChangeReason, animated: Bool) {} + func mapView(_ mapView: MGLMapView, regionWillChangeAnimated animated: Bool) {} + func mapView(_ mapView: MGLMapView, regionWillChangeWith reason: MGLCameraChangeReason, animated: Bool) {} + func mapViewDidFailLoadingMap(_ mapView: MGLMapView, withError error: Error) {} func mapView(_ mapView: MGLMapView, didUpdate userLocation: MGLUserLocation?) {} @@ -79,4 +89,5 @@ extension MGLMapViewDelegateIntegrationTests: MGLMapViewDelegate { func mapView(_ mapView: MGLMapView, shouldChangeFrom oldCamera: MGLMapCamera, to newCamera: MGLMapCamera) -> Bool { return false } + func mapView(_ mapView: MGLMapView, shouldChangeFrom oldCamera: MGLMapCamera, to newCamera: MGLMapCamera, reason: MGLCameraChangeReason) -> Bool { return false } } diff --git a/platform/ios/uitest/MapViewTests.m b/platform/ios/uitest/MapViewTests.m index 4ed3d89399..ba15af918a 100644 --- a/platform/ios/uitest/MapViewTests.m +++ b/platform/ios/uitest/MapViewTests.m @@ -538,10 +538,13 @@ userInfo:@{ @"animated" : @(animated) }]; } -- (void)mapView:(MGLMapView *)mapView regionDidChangeAnimated:(BOOL)animated { +- (void)mapView:(MGLMapView *)mapView regionDidChangeWithReason:(MGLCameraChangeReason)reason animated:(BOOL)animated { + [[NSNotificationCenter defaultCenter] postNotificationName:@"regionDidChangeAnimated" object:mapView - userInfo:@{ @"animated" : @(animated) }]; + userInfo:@{ @"animated" : @(animated), + @"reason" : @(reason) + }]; } - (void)testDelegatesStartStopLocatingUser { -- cgit v1.2.1 From 024e75bd560cdf2be58322e127f92c81f06cfe0c Mon Sep 17 00:00:00 2001 From: Fabian Guerra Soto Date: Mon, 12 Feb 2018 16:47:16 -0500 Subject: [ios, macos] Fix memory leaks in MGLMapSnapshotter. (#11133) --- platform/darwin/src/MGLMapSnapshotter.mm | 339 +++++++++++++------------- platform/ios/app/MBXSnapshotsViewController.m | 3 +- 2 files changed, 177 insertions(+), 165 deletions(-) diff --git a/platform/darwin/src/MGLMapSnapshotter.mm b/platform/darwin/src/MGLMapSnapshotter.mm index 8b4ae7977b..db236a8aeb 100644 --- a/platform/darwin/src/MGLMapSnapshotter.mm +++ b/platform/darwin/src/MGLMapSnapshotter.mm @@ -81,7 +81,7 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64; @end @interface MGLMapSnapshotter() - +@property (nonatomic) BOOL loading; @end @implementation MGLMapSnapshotter { @@ -90,6 +90,7 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64; std::unique_ptr _mbglMapSnapshotter; std::unique_ptr> _snapshotCallback; NS_ARRAY_OF(MGLAttributionInfo *) *_attributionInfo; + } - (instancetype)initWithOptions:(MGLMapSnapshotOptions *)options @@ -115,182 +116,192 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64; format:@"Already started this snapshotter."]; } - _loading = true; + self.loading = true; - dispatch_async(queue, ^{ - _snapshotCallback = std::make_unique>(*mbgl::Scheduler::GetCurrent(), [=](std::exception_ptr mbglError, mbgl::PremultipliedImage image, mbgl::MapSnapshotter::Attributions attributions, mbgl::MapSnapshotter::PointForFn pointForFn) { - _loading = false; + __weak __typeof__(self) weakSelf = self; + _snapshotCallback = std::make_unique>(*mbgl::Scheduler::GetCurrent(), [=](std::exception_ptr mbglError, mbgl::PremultipliedImage image, mbgl::MapSnapshotter::Attributions attributions, mbgl::MapSnapshotter::PointForFn pointForFn) { + __typeof__(self) strongSelf = weakSelf; + strongSelf.loading = false; + + + if (mbglError) { + NSString *description = @(mbgl::util::toString(mbglError).c_str()); + NSDictionary *userInfo = @{NSLocalizedDescriptionKey: description}; + NSError *error = [NSError errorWithDomain:MGLErrorDomain code:MGLErrorCodeSnapshotFailed userInfo:userInfo]; - NSMutableArray *infos = [NSMutableArray array]; - + // Dispatch result to origin queue + dispatch_async(queue, ^{ + completion(nil, error); + }); + } else { #if TARGET_OS_IPHONE - CGFloat fontSize = [UIFont smallSystemFontSize]; - UIColor *attributeFontColor = [UIColor blackColor]; + MGLImage *mglImage = [[MGLImage alloc] initWithMGLPremultipliedImage:std::move(image) scale:strongSelf.options.scale]; #else - CGFloat fontSize = [NSFont systemFontSizeForControlSize:NSMiniControlSize]; - NSColor *attributeFontColor = [NSColor blackColor]; + MGLImage *mglImage = [[MGLImage alloc] initWithMGLPremultipliedImage:std::move(image)]; + mglImage.size = NSMakeSize(mglImage.size.width / strongSelf.options.scale, + mglImage.size.height / strongSelf.options.scale); #endif - for (auto attribution = attributions.begin(); attribution != attributions.end(); ++attribution) { - NSString *attributionHTMLString = @(attribution->c_str()); - NSArray *tileSetInfos = [MGLAttributionInfo attributionInfosFromHTMLString:attributionHTMLString - fontSize:fontSize - linkColor:attributeFontColor]; - [infos growArrayByAddingAttributionInfosFromArray:tileSetInfos]; - } - - _attributionInfo = infos; - - if (mbglError) { - NSString *description = @(mbgl::util::toString(mbglError).c_str()); - NSDictionary *userInfo = @{NSLocalizedDescriptionKey: description}; - NSError *error = [NSError errorWithDomain:MGLErrorDomain code:MGLErrorCodeSnapshotFailed userInfo:userInfo]; - - // Dispatch result to origin queue - dispatch_async(queue, ^{ - completion(nil, error); - }); - } else { + [strongSelf drawAttributedSnapshot:attributions snapshotImage:mglImage pointForFn:pointForFn queue:queue completionHandler:completion]; + } + _snapshotCallback = NULL; + }); + dispatch_async(queue, ^{ + _mbglMapSnapshotter->snapshot(_snapshotCallback->self()); + + }); +} + +- (MGLImage *)drawAttributedSnapshot:(mbgl::MapSnapshotter::Attributions)attributions snapshotImage:(MGLImage *)mglImage pointForFn:(mbgl::MapSnapshotter::PointForFn)pointForFn queue:(dispatch_queue_t)queue completionHandler:(MGLMapSnapshotCompletionHandler)completion { + + NSMutableArray *infos = [NSMutableArray array]; + #if TARGET_OS_IPHONE - MGLImage *mglImage = [[MGLImage alloc] initWithMGLPremultipliedImage:std::move(image) scale:self.options.scale]; + CGFloat fontSize = [UIFont smallSystemFontSize]; + UIColor *attributeFontColor = [UIColor blackColor]; #else - MGLImage *mglImage = [[MGLImage alloc] initWithMGLPremultipliedImage:std::move(image)]; - mglImage.size = NSMakeSize(mglImage.size.width / self.options.scale, - mglImage.size.height / self.options.scale); + CGFloat fontSize = [NSFont systemFontSizeForControlSize:NSMiniControlSize]; + NSColor *attributeFontColor = [NSColor blackColor]; #endif - - // Process image watermark in a work queue - dispatch_queue_t workQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); - dispatch_async(workQueue, ^{ + for (auto attribution = attributions.begin(); attribution != attributions.end(); ++attribution) { + NSString *attributionHTMLString = @(attribution->c_str()); + NSArray *tileSetInfos = [MGLAttributionInfo attributionInfosFromHTMLString:attributionHTMLString + fontSize:fontSize + linkColor:attributeFontColor]; + [infos growArrayByAddingAttributionInfosFromArray:tileSetInfos]; + } + + _attributionInfo = infos; + + // Process image watermark in a work queue + dispatch_queue_t workQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + dispatch_async(workQueue, ^{ #if TARGET_OS_IPHONE - MGLAttributionInfoStyle attributionInfoStyle = MGLAttributionInfoStyleLong; - for (NSUInteger styleValue = MGLAttributionInfoStyleLong; styleValue >= MGLAttributionInfoStyleShort; styleValue--) { - attributionInfoStyle = (MGLAttributionInfoStyle)styleValue; - CGSize attributionSize = [self attributionSizeWithLogoStyle:attributionInfoStyle sourceAttributionStyle:attributionInfoStyle]; - if (attributionSize.width <= mglImage.size.width) { - break; - } - } - - UIImage *logoImage = [self logoImageWithStyle:attributionInfoStyle]; - CGSize attributionBackgroundSize = [self attributionTextSizeWithStyle:attributionInfoStyle]; - - CGRect logoImageRect = CGRectMake(MGLLogoImagePosition.x, mglImage.size.height - (MGLLogoImagePosition.y + logoImage.size.height), logoImage.size.width, logoImage.size.height); - CGPoint attributionOrigin = CGPointMake(mglImage.size.width - 10 - attributionBackgroundSize.width, - logoImageRect.origin.y + (logoImageRect.size.height / 2) - (attributionBackgroundSize.height / 2) + 1); - if (!logoImage) { - CGSize defaultLogoSize = [self mapboxLongStyleLogo].size; - logoImageRect = CGRectMake(0, mglImage.size.height - (MGLLogoImagePosition.y + defaultLogoSize.height), 0, defaultLogoSize.height); - attributionOrigin = CGPointMake(10, logoImageRect.origin.y + (logoImageRect.size.height / 2) - (attributionBackgroundSize.height / 2) + 1); - } - - CGRect attributionBackgroundFrame = CGRectMake(attributionOrigin.x, - attributionOrigin.y, - attributionBackgroundSize.width, - attributionBackgroundSize.height); - CGPoint attributionTextPosition = CGPointMake(attributionBackgroundFrame.origin.x + 10, - attributionBackgroundFrame.origin.y - 1); - - CGRect cropRect = CGRectMake(attributionBackgroundFrame.origin.x * mglImage.scale, - attributionBackgroundFrame.origin.y * mglImage.scale, - attributionBackgroundSize.width * mglImage.scale, - attributionBackgroundSize.height * mglImage.scale); - - - UIGraphicsBeginImageContextWithOptions(mglImage.size, NO, self.options.scale); - - [mglImage drawInRect:CGRectMake(0, 0, mglImage.size.width, mglImage.size.height)]; - - [logoImage drawInRect:logoImageRect]; - - UIImage *currentImage = UIGraphicsGetImageFromCurrentImageContext(); - CGImageRef attributionImageRef = CGImageCreateWithImageInRect([currentImage CGImage], cropRect); - UIImage *attributionImage = [UIImage imageWithCGImage:attributionImageRef]; - CGImageRelease(attributionImageRef); - - CIImage *ciAttributionImage = [[CIImage alloc] initWithCGImage:attributionImage.CGImage]; - - UIImage *blurredAttributionBackground = [self blurredAttributionBackground:ciAttributionImage]; - - [blurredAttributionBackground drawInRect:attributionBackgroundFrame]; - - [self drawAttributionTextWithStyle:attributionInfoStyle origin:attributionTextPosition]; - - UIImage *compositedImage = UIGraphicsGetImageFromCurrentImageContext(); - - UIGraphicsEndImageContext(); + MGLAttributionInfoStyle attributionInfoStyle = MGLAttributionInfoStyleLong; + for (NSUInteger styleValue = MGLAttributionInfoStyleLong; styleValue >= MGLAttributionInfoStyleShort; styleValue--) { + attributionInfoStyle = (MGLAttributionInfoStyle)styleValue; + CGSize attributionSize = [self attributionSizeWithLogoStyle:attributionInfoStyle sourceAttributionStyle:attributionInfoStyle]; + if (attributionSize.width <= mglImage.size.width) { + break; + } + } + + UIImage *logoImage = [self logoImageWithStyle:attributionInfoStyle]; + CGSize attributionBackgroundSize = [self attributionTextSizeWithStyle:attributionInfoStyle]; + + CGRect logoImageRect = CGRectMake(MGLLogoImagePosition.x, mglImage.size.height - (MGLLogoImagePosition.y + logoImage.size.height), logoImage.size.width, logoImage.size.height); + CGPoint attributionOrigin = CGPointMake(mglImage.size.width - 10 - attributionBackgroundSize.width, + logoImageRect.origin.y + (logoImageRect.size.height / 2) - (attributionBackgroundSize.height / 2) + 1); + if (!logoImage) { + CGSize defaultLogoSize = [self mapboxLongStyleLogo].size; + logoImageRect = CGRectMake(0, mglImage.size.height - (MGLLogoImagePosition.y + defaultLogoSize.height), 0, defaultLogoSize.height); + attributionOrigin = CGPointMake(10, logoImageRect.origin.y + (logoImageRect.size.height / 2) - (attributionBackgroundSize.height / 2) + 1); + } + + CGRect attributionBackgroundFrame = CGRectMake(attributionOrigin.x, + attributionOrigin.y, + attributionBackgroundSize.width, + attributionBackgroundSize.height); + CGPoint attributionTextPosition = CGPointMake(attributionBackgroundFrame.origin.x + 10, + attributionBackgroundFrame.origin.y - 1); + + CGRect cropRect = CGRectMake(attributionBackgroundFrame.origin.x * mglImage.scale, + attributionBackgroundFrame.origin.y * mglImage.scale, + attributionBackgroundSize.width * mglImage.scale, + attributionBackgroundSize.height * mglImage.scale); + + + UIGraphicsBeginImageContextWithOptions(mglImage.size, NO, self.options.scale); + + [mglImage drawInRect:CGRectMake(0, 0, mglImage.size.width, mglImage.size.height)]; + + [logoImage drawInRect:logoImageRect]; + + UIImage *currentImage = UIGraphicsGetImageFromCurrentImageContext(); + CGImageRef attributionImageRef = CGImageCreateWithImageInRect([currentImage CGImage], cropRect); + UIImage *attributionImage = [UIImage imageWithCGImage:attributionImageRef]; + CGImageRelease(attributionImageRef); + + CIImage *ciAttributionImage = [[CIImage alloc] initWithCGImage:attributionImage.CGImage]; + + UIImage *blurredAttributionBackground = [self blurredAttributionBackground:ciAttributionImage]; + + [blurredAttributionBackground drawInRect:attributionBackgroundFrame]; + + [self drawAttributionTextWithStyle:attributionInfoStyle origin:attributionTextPosition]; + + UIImage *compositedImage = UIGraphicsGetImageFromCurrentImageContext(); + + UIGraphicsEndImageContext(); #else - NSSize targetSize = NSMakeSize(self.options.size.width, self.options.size.height); - NSRect targetFrame = NSMakeRect(0, 0, targetSize.width, targetSize.height); - - MGLAttributionInfoStyle attributionInfoStyle = MGLAttributionInfoStyleLong; - for (NSUInteger styleValue = MGLAttributionInfoStyleLong; styleValue >= MGLAttributionInfoStyleShort; styleValue--) { - attributionInfoStyle = (MGLAttributionInfoStyle)styleValue; - CGSize attributionSize = [self attributionSizeWithLogoStyle:attributionInfoStyle sourceAttributionStyle:attributionInfoStyle]; - if (attributionSize.width <= mglImage.size.width) { - break; - } - } - - NSImage *logoImage = [self logoImageWithStyle:attributionInfoStyle]; - CGSize attributionBackgroundSize = [self attributionTextSizeWithStyle:attributionInfoStyle]; - NSImage *sourceImage = mglImage; - - CGRect logoImageRect = CGRectMake(MGLLogoImagePosition.x, MGLLogoImagePosition.y, logoImage.size.width, logoImage.size.height); - CGPoint attributionOrigin = CGPointMake(targetFrame.size.width - 10 - attributionBackgroundSize.width, - MGLLogoImagePosition.y + 1); - if (!logoImage) { - CGSize defaultLogoSize = [self mapboxLongStyleLogo].size; - logoImageRect = CGRectMake(0, MGLLogoImagePosition.y, 0, defaultLogoSize.height); - attributionOrigin = CGPointMake(10, attributionOrigin.y); - } - - CGRect attributionBackgroundFrame = CGRectMake(attributionOrigin.x, - attributionOrigin.y, - attributionBackgroundSize.width, - attributionBackgroundSize.height); - CGPoint attributionTextPosition = CGPointMake(attributionBackgroundFrame.origin.x + 10, - logoImageRect.origin.y + (logoImageRect.size.height / 2) - (attributionBackgroundSize.height / 2)); - - - NSImage *compositedImage = nil; - NSImageRep *sourceImageRep = [sourceImage bestRepresentationForRect:targetFrame - context:nil - hints:nil]; - compositedImage = [[NSImage alloc] initWithSize:targetSize]; - - [compositedImage lockFocus]; - - [sourceImageRep drawInRect: targetFrame]; - - if (logoImage) { - [logoImage drawInRect:logoImageRect]; - } - - NSBitmapImageRep *attributionBackground = [[NSBitmapImageRep alloc] initWithFocusedViewRect:attributionBackgroundFrame]; - - CIImage *attributionBackgroundImage = [[CIImage alloc] initWithCGImage:[attributionBackground CGImage]]; - - NSImage *blurredAttributionBackground = [self blurredAttributionBackground:attributionBackgroundImage]; - - [blurredAttributionBackground drawInRect:attributionBackgroundFrame]; - - [self drawAttributionTextWithStyle:attributionInfoStyle origin:attributionTextPosition]; - - [compositedImage unlockFocus]; - - -#endif - - // Dispatch result to origin queue - dispatch_async(queue, ^{ - MGLMapSnapshot* snapshot = [[MGLMapSnapshot alloc] initWithImage:compositedImage scale:self.options.scale pointForFn:pointForFn]; - completion(snapshot, nil); - }); - }); + NSSize targetSize = NSMakeSize(self.options.size.width, self.options.size.height); + NSRect targetFrame = NSMakeRect(0, 0, targetSize.width, targetSize.height); + + MGLAttributionInfoStyle attributionInfoStyle = MGLAttributionInfoStyleLong; + for (NSUInteger styleValue = MGLAttributionInfoStyleLong; styleValue >= MGLAttributionInfoStyleShort; styleValue--) { + attributionInfoStyle = (MGLAttributionInfoStyle)styleValue; + CGSize attributionSize = [self attributionSizeWithLogoStyle:attributionInfoStyle sourceAttributionStyle:attributionInfoStyle]; + if (attributionSize.width <= mglImage.size.width) { + break; } + } + + NSImage *logoImage = [self logoImageWithStyle:attributionInfoStyle]; + CGSize attributionBackgroundSize = [self attributionTextSizeWithStyle:attributionInfoStyle]; + NSImage *sourceImage = mglImage; + + CGRect logoImageRect = CGRectMake(MGLLogoImagePosition.x, MGLLogoImagePosition.y, logoImage.size.width, logoImage.size.height); + CGPoint attributionOrigin = CGPointMake(targetFrame.size.width - 10 - attributionBackgroundSize.width, + MGLLogoImagePosition.y + 1); + if (!logoImage) { + CGSize defaultLogoSize = [self mapboxLongStyleLogo].size; + logoImageRect = CGRectMake(0, MGLLogoImagePosition.y, 0, defaultLogoSize.height); + attributionOrigin = CGPointMake(10, attributionOrigin.y); + } + + CGRect attributionBackgroundFrame = CGRectMake(attributionOrigin.x, + attributionOrigin.y, + attributionBackgroundSize.width, + attributionBackgroundSize.height); + CGPoint attributionTextPosition = CGPointMake(attributionBackgroundFrame.origin.x + 10, + logoImageRect.origin.y + (logoImageRect.size.height / 2) - (attributionBackgroundSize.height / 2)); + + + NSImage *compositedImage = nil; + NSImageRep *sourceImageRep = [sourceImage bestRepresentationForRect:targetFrame + context:nil + hints:nil]; + compositedImage = [[NSImage alloc] initWithSize:targetSize]; + + [compositedImage lockFocus]; + + [sourceImageRep drawInRect: targetFrame]; + + if (logoImage) { + [logoImage drawInRect:logoImageRect]; + } + + NSBitmapImageRep *attributionBackground = [[NSBitmapImageRep alloc] initWithFocusedViewRect:attributionBackgroundFrame]; + + CIImage *attributionBackgroundImage = [[CIImage alloc] initWithCGImage:[attributionBackground CGImage]]; + + NSImage *blurredAttributionBackground = [self blurredAttributionBackground:attributionBackgroundImage]; + + [blurredAttributionBackground drawInRect:attributionBackgroundFrame]; + + [self drawAttributionTextWithStyle:attributionInfoStyle origin:attributionTextPosition]; + + [compositedImage unlockFocus]; + + +#endif + + // Dispatch result to origin queue + dispatch_async(queue, ^{ + MGLMapSnapshot* snapshot = [[MGLMapSnapshot alloc] initWithImage:compositedImage scale:self.options.scale pointForFn:pointForFn]; + completion(snapshot, nil); }); - _mbglMapSnapshotter->snapshot(_snapshotCallback->self()); }); + return nil; } - (void)drawAttributionTextWithStyle:(MGLAttributionInfoStyle)attributionInfoStyle origin:(CGPoint)origin diff --git a/platform/ios/app/MBXSnapshotsViewController.m b/platform/ios/app/MBXSnapshotsViewController.m index 3bf93d8721..95d3251e2e 100644 --- a/platform/ios/app/MBXSnapshotsViewController.m +++ b/platform/ios/app/MBXSnapshotsViewController.m @@ -50,12 +50,13 @@ options.zoomLevel = 10; // Create and start the snapshotter + __weak UIImageView *weakImageView = imageView; MGLMapSnapshotter* snapshotter = [[MGLMapSnapshotter alloc] initWithOptions:options]; [snapshotter startWithCompletionHandler: ^(MGLMapSnapshot* snapshot, NSError *error) { if (error) { NSLog(@"Could not load snapshot: %@", [error localizedDescription]); } else { - imageView.image = snapshot.image; + weakImageView.image = snapshot.image; } }]; -- cgit v1.2.1 From 3a0c87b1b4d4f2789a2ca5e995a79a7bad4f761f Mon Sep 17 00:00:00 2001 From: Fabian Guerra Soto Date: Mon, 12 Feb 2018 17:37:00 -0500 Subject: [ios] Bump podspec to 3.7.4 (#11179) * [ios] Bump podspec to 3.7.4 * [ios, macos] Update changelogs. --- platform/ios/CHANGELOG.md | 1 + platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec | 2 +- platform/ios/Mapbox-iOS-SDK-symbols.podspec | 2 +- platform/ios/Mapbox-iOS-SDK.podspec | 2 +- platform/macos/CHANGELOG.md | 1 + 5 files changed, 5 insertions(+), 3 deletions(-) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 7eb3fd8bf9..19d6d36ace 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -5,6 +5,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT ## 3.7.4 * Added the `MGLTileSourceOptionTileCoordinateBounds` option to create an `MGLTileSource` that only supplies tiles within a specific geographic bounding box. ([#11141](https://github.com/mapbox/mapbox-gl-native/pull/11141)) +* Fixed an issue that caused `-[MGLMapSnapshotter pointForCoordinate:]` to return the wrong point. ([#11035](https://github.com/mapbox/mapbox-gl-native/pull/11035)) ## 3.7.3 - January 10, 2018 diff --git a/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec b/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec index fb9511e707..65adcf8d3c 100644 --- a/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec +++ b/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '3.7.3' + version = '3.7.4' m.name = 'Mapbox-iOS-SDK-nightly-dynamic' m.version = "#{version}-nightly" diff --git a/platform/ios/Mapbox-iOS-SDK-symbols.podspec b/platform/ios/Mapbox-iOS-SDK-symbols.podspec index ea8ae667f6..fff485ebb4 100644 --- a/platform/ios/Mapbox-iOS-SDK-symbols.podspec +++ b/platform/ios/Mapbox-iOS-SDK-symbols.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '3.7.3' + version = '3.7.4' m.name = 'Mapbox-iOS-SDK-symbols' m.version = "#{version}-symbols" diff --git a/platform/ios/Mapbox-iOS-SDK.podspec b/platform/ios/Mapbox-iOS-SDK.podspec index 1e81272d6d..3181b66918 100644 --- a/platform/ios/Mapbox-iOS-SDK.podspec +++ b/platform/ios/Mapbox-iOS-SDK.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '3.7.3' + version = '3.7.4' m.name = 'Mapbox-iOS-SDK' m.version = version diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index 24abafdaaf..620c6f81ed 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -3,6 +3,7 @@ ## v0.6.2 * Added the `MGLTileSourceOptionTileCoordinateBounds` option to create an `MGLTileSource` that only supplies tiles within a specific geographic bounding box. ([#11141](https://github.com/mapbox/mapbox-gl-native/pull/11141)) +* Fixed an issue that caused `-[MGLMapSnapshotter pointForCoordinate:]` to return the wrong point. ([#11035](https://github.com/mapbox/mapbox-gl-native/pull/11035)) ## v0.6.1 - January 16, 2018 -- cgit v1.2.1 From aefc3daeeddaa597be3b10f5384cd3503099e2ee Mon Sep 17 00:00:00 2001 From: Asheem Mamoowala Date: Mon, 12 Feb 2018 12:17:57 -0800 Subject: TileJSON Bounds allows values inclusive of world extents --- src/mbgl/style/conversion/tileset.cpp | 2 +- test/style/conversion/tileset.test.cpp | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/mbgl/style/conversion/tileset.cpp b/src/mbgl/style/conversion/tileset.cpp index 6e559c0cac..88e78b1a83 100644 --- a/src/mbgl/style/conversion/tileset.cpp +++ b/src/mbgl/style/conversion/tileset.cpp @@ -6,7 +6,7 @@ namespace style { namespace conversion { bool validateLatitude(const double lat) { - return lat < 90 && lat > -90; + return lat <= 90 && lat >= -90; } optional Converter::operator()(const Convertible& value, Error& error) const { diff --git a/test/style/conversion/tileset.test.cpp b/test/style/conversion/tileset.test.cpp index 8002cd038f..9487277cca 100644 --- a/test/style/conversion/tileset.test.cpp +++ b/test/style/conversion/tileset.test.cpp @@ -52,6 +52,16 @@ TEST(Tileset, InvalidBounds) { } } +TEST(Tileset, ValidWorldBounds) { + Error error; + mbgl::optional converted = convertJSON(R"JSON({ + "tiles": ["http://mytiles"], + "bounds": [-180, -90, 180, 90] + })JSON", error); + EXPECT_TRUE((bool) converted); + EXPECT_EQ(converted->bounds, LatLngBounds::hull({90, -180}, {-90, 180})); +} + TEST(Tileset, FullConversion) { Error error; Tileset converted = *convertJSON(R"JSON({ -- cgit v1.2.1 From 44ca0a5ec4979138514e8dda10dd1b1173dd0db1 Mon Sep 17 00:00:00 2001 From: Pablo Guardiola Date: Tue, 13 Feb 2018 11:31:43 +0100 Subject: [android] Integration of the new events library (#10999) * [android] integration of the new events library * JNI Bug - current build with JNI bug * fix #10999 comments * Clean-up - clean-up timbers and test code * [android] fix sdk identifier and sdk version * [android] merge from master (MAS 3.0) * [android] bump events lib version to 3.0.0-beta.1 and remove never used methods from math utils class --- platform/android/MapboxGLAndroidSDK/build.gradle | 2 + .../com/mapbox/mapboxsdk/EmptyLocationSource.java | 107 ----------- .../src/main/java/com/mapbox/mapboxsdk/Mapbox.java | 31 +--- .../mapbox/mapboxsdk/camera/CameraPosition.java | 2 +- .../mapbox/mapboxsdk/location/LocationSource.java | 199 --------------------- .../mapbox/mapboxsdk/location/package-info.java | 4 - .../mapboxsdk/maps/AttributionDialogManager.java | 5 +- .../java/com/mapbox/mapboxsdk/maps/Events.java | 36 ++++ .../mapbox/mapboxsdk/maps/MapGestureDetector.java | 111 +++++++----- .../java/com/mapbox/mapboxsdk/maps/MapView.java | 13 +- .../mapbox/mapboxsdk/maps/MapboxEventWrapper.java | 57 ------ .../java/com/mapbox/mapboxsdk/maps/MapboxMap.java | 2 +- .../mapbox/mapboxsdk/maps/TrackingSettings.java | 6 +- .../mapboxsdk/maps/widgets/MyLocationView.java | 6 +- .../mapboxsdk/style/sources/GeoJsonOptions.java | 2 +- .../java/com/mapbox/mapboxsdk/utils/MathUtils.java | 49 +++++ .../test/java/com/mapbox/mapboxsdk/MapboxTest.java | 2 +- .../android/MapboxGLAndroidSDKTestApp/build.gradle | 11 +- .../src/main/AndroidManifest.xml | 2 +- .../testapp/activity/FeatureOverviewActivity.java | 4 +- .../userlocation/BaseLocationActivity.java | 4 +- .../activity/userlocation/MockLocationEngine.java | 4 +- .../userlocation/MyLocationDrawableActivity.java | 2 +- .../userlocation/MyLocationTintActivity.java | 2 +- .../MyLocationTrackingModeActivity.java | 2 +- platform/android/build.gradle | 1 + platform/android/gradle/dependencies.gradle | 3 +- 27 files changed, 203 insertions(+), 466 deletions(-) delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/EmptyLocationSource.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationSource.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/package-info.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Events.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxEventWrapper.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MathUtils.java diff --git a/platform/android/MapboxGLAndroidSDK/build.gradle b/platform/android/MapboxGLAndroidSDK/build.gradle index 12c7cc1e58..e2e0881857 100644 --- a/platform/android/MapboxGLAndroidSDK/build.gradle +++ b/platform/android/MapboxGLAndroidSDK/build.gradle @@ -25,6 +25,8 @@ android { minSdkVersion androidVersions.minSdkVersion targetSdkVersion androidVersions.targetSdkVersion buildConfigField "String", "GIT_REVISION_SHORT", String.format("\"%s\"", getGitRevision()) + buildConfigField "String", "MAPBOX_SDK_IDENTIFIER", String.format("\"%s\"", "mapbox-maps-android") + buildConfigField "String", "MAPBOX_SDK_VERSION", String.format("\"%s\"", project.VERSION_NAME) buildConfigField "String", "MAPBOX_VERSION_STRING", String.format("\"Mapbox/%s\"", project.VERSION_NAME) buildConfigField "String", "MAPBOX_EVENTS_USER_AGENT", String.format("\"MapboxEventsAndroid/%s\"", project.VERSION_NAME) } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/EmptyLocationSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/EmptyLocationSource.java deleted file mode 100644 index 8ea7e61eee..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/EmptyLocationSource.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.mapbox.mapboxsdk; - - -import android.location.Location; -import android.support.annotation.Nullable; - -import com.mapbox.mapboxsdk.location.LocationSource; -import com.mapbox.services.android.telemetry.location.LocationEngine; -import com.mapbox.services.android.telemetry.location.LocationEngineListener; - -class EmptyLocationSource extends LocationSource { - - /** - * Activate the location engine which will connect whichever location provider you are using. You'll need to call - * this before requesting user location updates using {@link LocationEngine#requestLocationUpdates()}. - */ - @Override - public void activate() { - // Intentionally left empty - } - - /** - * Disconnect the location engine which is useful when you no longer need location updates or requesting the users - * {@link LocationEngine#getLastLocation()}. Before deactivating, you'll need to stop request user location updates - * using {@link LocationEngine#removeLocationUpdates()}. - */ - @Override - public void deactivate() { - // Intentionally left empty - } - - /** - * Check if your location provider has been activated/connected. This is mainly used internally but is also useful in - * the rare case when you'd like to know if your location engine is connected or not. - * - * @return boolean true if the location engine has been activated/connected, else false. - */ - @Override - public boolean isConnected() { - return false; - } - - /** - * Returns the Last known location is the location provider is connected and location permissions are granted. - * - * @return the last known location - */ - @Override - @Nullable - public Location getLastLocation() { - return null; - } - - /** - * Request location updates to the location provider. - */ - @Override - public void requestLocationUpdates() { - // Intentionally left empty - } - - /** - * Dismiss ongoing location update to the location provider. - */ - @Override - public void removeLocationUpdates() { - // Intentionally left empty - } - - /** - * Invoked when the Location has changed. - * - * @param location the new location - */ - @Override - public void onLocationChanged(Location location) { - // Intentionally left empty - } - - /** - * Useful when you'd like to add a location listener to handle location connections and update events. It is important - * to note, that the callback will continue getting called even when your application isn't in the foreground. - * Therefore, it is a good idea to use {@link LocationEngine#removeLocationEngineListener(LocationEngineListener)} - * inside your activities {@code onStop()} method. - * - * @param listener A {@link LocationEngineListener} which you'd like to add to your location engine. - * @since 2.0.0 - */ - @Override - public void addLocationEngineListener(LocationEngineListener listener) { - // Intentionally left empty - } - - /** - * If you no longer need your {@link LocationEngineListener} to be invoked with every location update, use this - * method to remove it. It's also important to remove your listeners before the activity is destroyed to prevent any - * potential memory leaks. - * - * @param listener the {@link LocationEngineListener} you'd like to remove from this {@link LocationEngine}. - * @return true. - * @since 2.0.0 - */ - @Override - public boolean removeLocationEngineListener(LocationEngineListener listener) { - return true; - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java index b67b6e96f2..853ea1c11b 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java @@ -8,16 +8,12 @@ import android.support.annotation.NonNull; import android.support.annotation.UiThread; import android.text.TextUtils; +import com.mapbox.android.core.location.LocationEngine; +import com.mapbox.android.core.location.LocationEnginePriority; +import com.mapbox.android.core.location.LocationEngineProvider; import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.exceptions.MapboxConfigurationException; -import com.mapbox.mapboxsdk.location.LocationSource; import com.mapbox.mapboxsdk.net.ConnectivityReceiver; -import com.mapbox.services.android.telemetry.MapboxTelemetry; -import com.mapbox.services.android.telemetry.location.LocationEngine; -import com.mapbox.services.android.telemetry.location.LocationEnginePriority; -import com.mapbox.services.android.telemetry.location.LocationEngineProvider; - -import timber.log.Timber; /** * The entry point to initialize the Mapbox Android SDK. @@ -56,15 +52,9 @@ public final class Mapbox { INSTANCE = new Mapbox(appContext, accessToken, locationEngine); locationEngine.setPriority(LocationEnginePriority.NO_POWER); - try { - MapboxTelemetry.getInstance().initialize( - appContext, accessToken, BuildConfig.MAPBOX_EVENTS_USER_AGENT, locationEngine); - } catch (Exception exception) { - Timber.e(exception, "Unable to instantiate Mapbox telemetry"); - } - ConnectivityReceiver.instance(appContext); } + return INSTANCE; } @@ -145,23 +135,12 @@ public final class Mapbox { return (activeNetwork != null && activeNetwork.isConnected()); } - /** - * Returns a location source instance with empty methods. - * - * @return an empty location source implementation - * @deprecated Replaced by {@link Mapbox#getLocationEngine()} - */ - @Deprecated - public static LocationSource getLocationSource() { - return new EmptyLocationSource(); - } - - /** * Returns the location engine used by the SDK. * * @return the location engine configured */ + // TODO Do we need to expose this? public static LocationEngine getLocationEngine() { return INSTANCE.locationEngine; } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java index c2f19072db..e732b2525f 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java @@ -8,7 +8,7 @@ import android.support.annotation.FloatRange; import com.mapbox.mapboxsdk.R; import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.services.android.telemetry.utils.MathUtils; +import com.mapbox.mapboxsdk.utils.MathUtils; /** * Resembles the position, angle, zoom and tilt of the user's viewpoint. diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationSource.java deleted file mode 100644 index 1313587158..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationSource.java +++ /dev/null @@ -1,199 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -import android.content.Context; -import android.location.Location; -import android.support.annotation.Nullable; - -import com.mapbox.mapboxsdk.Mapbox; -import com.mapbox.services.android.telemetry.location.LocationEngine; -import com.mapbox.services.android.telemetry.location.LocationEngineListener; -import com.mapbox.services.android.telemetry.location.LocationEnginePriority; -import com.mapzen.android.lost.api.LocationListener; -import com.mapzen.android.lost.api.LocationRequest; -import com.mapzen.android.lost.api.LocationServices; -import com.mapzen.android.lost.api.LostApiClient; - -/** - * LocationEngine using the Open Source Lost library - * Manages locational updates. Contains methods to register and unregister location listeners. - *
    - *
  • You can register a LocationEngineListener with LocationSource#addLocationEngineListener(LocationEngineListener) - * to receive location updates.
  • - *
  • You can unregister a LocationEngineListener with - * LocationEngine#removeLocationEngineListener(LocationEngineListener)} to stop receiving location updates.
  • - *
- *

- * Note: If registering a listener in your Activity.onStart() implementation, you should unregister it in - * Activity.onStop(). (You won't receive location updates when paused, and this will cut down on unnecessary system - * overhead). Do not unregister in Activity.onSaveInstanceState(), because this won't be called if the user moves back - * in the history stack. - *

- * - * @deprecated Use a {@link Mapbox#getLocationEngine()} instead. - */ -@Deprecated -public class LocationSource extends LocationEngine implements LostApiClient.ConnectionCallbacks, LocationListener { - - private Context context; - private LostApiClient lostApiClient; - - /** - * Constructs a location source instance. - * - * @param context the context from which the Application context will be derived. - */ - public LocationSource(Context context) { - super(); - this.context = context.getApplicationContext(); - lostApiClient = new LostApiClient.Builder(this.context) - .addConnectionCallbacks(this) - .build(); - } - - /** - * Constructs a location source instance. - * Needed to create empty location source implementations. - */ - public LocationSource() { - } - - /** - * Activate the location engine which will connect whichever location provider you are using. You'll need to call - * this before requesting user location updates using {@link LocationEngine#requestLocationUpdates()}. - */ - @Override - public void activate() { - connect(); - } - - /** - * Disconnect the location engine which is useful when you no longer need location updates or requesting the users - * {@link LocationEngine#getLastLocation()}. Before deactivating, you'll need to stop request user location updates - * using {@link LocationEngine#removeLocationUpdates()}. - */ - @Override - public void deactivate() { - if (lostApiClient != null && lostApiClient.isConnected()) { - lostApiClient.disconnect(); - } - } - - /** - * Check if your location provider has been activated/connected. This is mainly used internally but is also useful in - * the rare case when you'd like to know if your location engine is connected or not. - * - * @return boolean true if the location engine has been activated/connected, else false. - */ - @Override - public boolean isConnected() { - return lostApiClient.isConnected(); - } - - /** - * Invoked when the location provider has connected. - */ - @Override - public void onConnected() { - for (LocationEngineListener listener : locationListeners) { - listener.onConnected(); - } - } - - /** - * Invoked when the location provider connection has been suspended. - */ - @Override - public void onConnectionSuspended() { - // Empty - } - - /** - * Returns the Last known location is the location provider is connected and location permissions are granted. - * - * @return the last known location - */ - @Override - @Nullable - public Location getLastLocation() { - if (lostApiClient.isConnected()) { - //noinspection MissingPermission - return LocationServices.FusedLocationApi.getLastLocation(lostApiClient); - } - return null; - } - - /** - * Request location updates to the location provider. - */ - @Override - public void requestLocationUpdates() { - LocationRequest request = LocationRequest.create(); - - if (interval != null) { - request.setInterval(interval); - } - if (fastestInterval != null) { - request.setFastestInterval(fastestInterval); - } - if (smallestDisplacement != null) { - request.setSmallestDisplacement(smallestDisplacement); - } - - if (priority == LocationEnginePriority.NO_POWER) { - request.setPriority(LocationRequest.PRIORITY_NO_POWER); - } else if (priority == LocationEnginePriority.LOW_POWER) { - request.setPriority(LocationRequest.PRIORITY_LOW_POWER); - } else if (priority == LocationEnginePriority.BALANCED_POWER_ACCURACY) { - request.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY); - } else if (priority == LocationEnginePriority.HIGH_ACCURACY) { - request.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); - } - - if (lostApiClient.isConnected()) { - //noinspection MissingPermission - LocationServices.FusedLocationApi.requestLocationUpdates(lostApiClient, request, this); - } - } - - /** - * Dismiss ongoing location update to the location provider. - */ - @Override - public void removeLocationUpdates() { - if (lostApiClient.isConnected()) { - LocationServices.FusedLocationApi.removeLocationUpdates(lostApiClient, this); - } - } - - /** - * Returns the location engine type. - * - * @return Lost type - */ - @Override - public Type obtainType() { - return Type.LOST; - } - - /** - * Invoked when the Location has changed. - * - * @param location the new location - */ - @Override - public void onLocationChanged(Location location) { - for (LocationEngineListener listener : locationListeners) { - listener.onLocationChanged(location); - } - } - - private void connect() { - if (lostApiClient != null) { - if (lostApiClient.isConnected()) { - onConnected(); - } else { - lostApiClient.connect(); - } - } - } -} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/package-info.java deleted file mode 100644 index b27559e95e..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Contains the Mapbox Maps Android Location API classes. - */ -package com.mapbox.mapboxsdk.location; \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java index b12757c81e..2bcbd5ce40 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java @@ -16,7 +16,6 @@ import com.mapbox.mapboxsdk.attribution.Attribution; import com.mapbox.mapboxsdk.attribution.AttributionParser; import com.mapbox.mapboxsdk.camera.CameraPosition; import com.mapbox.mapboxsdk.style.sources.Source; -import com.mapbox.services.android.telemetry.MapboxTelemetry; import java.util.ArrayList; import java.util.List; @@ -88,7 +87,7 @@ public class AttributionDialogManager implements View.OnClickListener, DialogInt builder.setPositiveButton(R.string.mapbox_attributionTelemetryPositive, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - MapboxTelemetry.getInstance().setTelemetryEnabled(true); + Events.obtainTelemetry().enable(); dialog.cancel(); } }); @@ -102,7 +101,7 @@ public class AttributionDialogManager implements View.OnClickListener, DialogInt builder.setNegativeButton(R.string.mapbox_attributionTelemetryNegative, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - MapboxTelemetry.getInstance().setTelemetryEnabled(false); + Events.obtainTelemetry().disable(); dialog.cancel(); } }); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Events.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Events.java new file mode 100644 index 0000000000..a68d4763ac --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Events.java @@ -0,0 +1,36 @@ +package com.mapbox.mapboxsdk.maps; + + +import com.mapbox.android.telemetry.MapboxTelemetry; +import com.mapbox.android.telemetry.TelemetryEnabler; +import com.mapbox.mapboxsdk.BuildConfig; +import com.mapbox.mapboxsdk.Mapbox; + +class Events { + static final String TWO_FINGER_TAP = "TwoFingerTap"; + static final String DOUBLE_TAP = "DoubleTap"; + static final String SINGLE_TAP = "SingleTap"; + static final String PAN = "Pan"; + static final String PINCH = "Pinch"; + static final String ROTATION = "Rotation"; + static final String PITCH = "Pitch"; + private MapboxTelemetry telemetry; + + private Events() { + telemetry = new MapboxTelemetry(Mapbox.getApplicationContext(), Mapbox.getAccessToken(), + BuildConfig.MAPBOX_EVENTS_USER_AGENT); + TelemetryEnabler.State telemetryState = TelemetryEnabler.retrieveTelemetryStateFromPreferences(); + if (TelemetryEnabler.State.NOT_INITIALIZED.equals(telemetryState) + || TelemetryEnabler.State.ENABLED.equals(telemetryState)) { + telemetry.enable(); + } + } + + private static class EventsHolder { + private static final Events INSTANCE = new Events(); + } + + static MapboxTelemetry obtainTelemetry() { + return EventsHolder.INSTANCE.telemetry; + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java index 1788cb4428..8047e19809 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java @@ -5,7 +5,6 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.PointF; -import android.location.Location; import android.support.annotation.Nullable; import android.support.v4.view.GestureDetectorCompat; import android.support.v4.view.ScaleGestureDetectorCompat; @@ -19,12 +18,12 @@ import android.view.ViewConfiguration; import com.almeros.android.multitouch.gesturedetectors.RotateGestureDetector; import com.almeros.android.multitouch.gesturedetectors.ShoveGestureDetector; import com.almeros.android.multitouch.gesturedetectors.TwoFingerGestureDetector; +import com.mapbox.android.telemetry.Event; +import com.mapbox.android.telemetry.MapEventFactory; +import com.mapbox.android.telemetry.MapState; import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.services.android.telemetry.MapboxEvent; -import com.mapbox.services.android.telemetry.MapboxTelemetry; -import com.mapbox.services.android.telemetry.utils.MathUtils; -import com.mapbox.services.android.telemetry.utils.TelemetryUtils; +import com.mapbox.mapboxsdk.utils.MathUtils; import java.util.concurrent.CopyOnWriteArrayList; @@ -144,19 +143,6 @@ final class MapGestureDetector { return focalPoint; } - /** - * Given coordinates from a gesture, use the current projection to translate it into - * a Location object. - * - * @param x coordinate - * @param y coordinate - * @return location - */ - private Location getLocationFromGesture(float x, float y) { - LatLng latLng = projection.fromScreenLocation(new PointF(x, y)); - return TelemetryUtils.buildLocation(latLng.getLongitude(), latLng.getLatitude()); - } - /** * Called when user touches the screen, all positions are absolute. *

@@ -202,9 +188,13 @@ final class MapGestureDetector { && uiSettings.isZoomGesturesEnabled(); if (twoTap) { // Confirmed 2nd Finger Down - MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent( - getLocationFromGesture(event.getX(), event.getY()), - MapboxEvent.GESTURE_TWO_FINGER_SINGLETAP, transform)); + if (isZoomValid(transform)) { + MapEventFactory mapEventFactory = new MapEventFactory(); + LatLng latLng = projection.fromScreenLocation(new PointF(event.getX(), event.getY())); + MapState twoFingerTap = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom()); + twoFingerTap.setGesture(Events.TWO_FINGER_TAP); + Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, twoFingerTap)); + } } break; @@ -233,8 +223,12 @@ final class MapGestureDetector { // Scroll / Pan Has Stopped if (scrollGestureOccurred) { - MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapDragEndEvent( - getLocationFromGesture(event.getX(), event.getY()), transform)); + if (isZoomValid(transform)) { + MapEventFactory mapEventFactory = new MapEventFactory(); + LatLng latLng = projection.fromScreenLocation(new PointF(event.getX(), event.getY())); + MapState dragend = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom()); + Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_DRAGEND, dragend)); + } scrollGestureOccurred = false; if (!scaleAnimating && !rotateAnimating) { @@ -350,9 +344,13 @@ final class MapGestureDetector { // Zoom in on gesture transform.zoom(true, new PointF(e.getX(), e.getY())); } - MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent( - getLocationFromGesture(e.getX(), e.getY()), - MapboxEvent.GESTURE_DOUBLETAP, transform)); + if (isZoomValid(transform)) { + MapEventFactory mapEventFactory = new MapEventFactory(); + LatLng latLng = projection.fromScreenLocation(new PointF(e.getX(), e.getY())); + MapState doubleTap = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom()); + doubleTap.setGesture(Events.DOUBLE_TAP); + Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, doubleTap)); + } break; } @@ -380,9 +378,13 @@ final class MapGestureDetector { notifyOnMapClickListeners(tapPoint); } - MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent( - getLocationFromGesture(motionEvent.getX(), motionEvent.getY()), - MapboxEvent.GESTURE_SINGLETAP, transform)); + if (isZoomValid(transform)) { + MapEventFactory mapEventFactory = new MapEventFactory(); + LatLng latLng = projection.fromScreenLocation(new PointF(motionEvent.getX(), motionEvent.getY())); + MapState singleTap = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom()); + singleTap.setGesture(Events.SINGLE_TAP); + Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, singleTap)); + } return true; } @@ -456,9 +458,13 @@ final class MapGestureDetector { cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); } - MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent( - getLocationFromGesture(e1.getX(), e1.getY()), - MapboxEvent.GESTURE_PAN_START, transform)); + if (isZoomValid(transform)) { + MapEventFactory mapEventFactory = new MapEventFactory(); + LatLng latLng = projection.fromScreenLocation(new PointF(e1.getX(), e1.getY())); + MapState pan = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom()); + pan.setGesture(Events.PAN); + Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, pan)); + } } // reset tracking if needed @@ -541,9 +547,13 @@ final class MapGestureDetector { recentScaleGestureOccurred = true; scalePointBegin = new PointF(detector.getFocusX(), detector.getFocusY()); scaleBeginTime = detector.getEventTime(); - MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent( - getLocationFromGesture(detector.getFocusX(), detector.getFocusY()), - MapboxEvent.GESTURE_PINCH_START, transform)); + if (isZoomValid(transform)) { + MapEventFactory mapEventFactory = new MapEventFactory(); + LatLng latLng = projection.fromScreenLocation(new PointF(detector.getFocusX(), detector.getFocusY())); + MapState pinch = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom()); + pinch.setGesture(Events.PINCH); + Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, pinch)); + } return true; } @@ -724,9 +734,13 @@ final class MapGestureDetector { // Also is zoom already started, don't rotate float angle = detector.getRotationDegreesDelta(); if (Math.abs(angle) >= ROTATE_INVOKE_ANGLE) { - MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent( - getLocationFromGesture(detector.getFocusX(), detector.getFocusY()), - MapboxEvent.GESTURE_ROTATION_START, transform)); + if (isZoomValid(transform)) { + MapEventFactory mapEventFactory = new MapEventFactory(); + LatLng latLng = projection.fromScreenLocation(new PointF(detector.getFocusX(), detector.getFocusY())); + MapState rotation = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom()); + rotation.setGesture(Events.ROTATION); + Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, rotation)); + } started = true; } @@ -895,9 +909,13 @@ final class MapGestureDetector { if (!tiltGestureOccurred && ((totalDelta > 10.0f) || (totalDelta < -10.0f))) { tiltGestureOccurred = true; beginTime = detector.getEventTime(); - MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent( - getLocationFromGesture(detector.getFocusX(), detector.getFocusY()), - MapboxEvent.GESTURE_PITCH_START, transform)); + if (isZoomValid(transform)) { + MapEventFactory mapEventFactory = new MapEventFactory(); + LatLng latLng = projection.fromScreenLocation(new PointF(detector.getFocusX(), detector.getFocusY())); + MapState pitch = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom()); + pitch.setGesture(Events.PITCH); + Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, pitch)); + } } if (!tiltGestureOccurred) { @@ -962,4 +980,15 @@ final class MapGestureDetector { void removeOnScrollListener(MapboxMap.OnScrollListener onScrollListener) { onScrollListenerList.remove(onScrollListener); } + + private boolean isZoomValid(Transform transform) { + if (transform == null) { + return false; + } + double mapZoom = transform.getZoom(); + if (mapZoom < MapboxConstants.MINIMUM_ZOOM || mapZoom > MapboxConstants.MAXIMUM_ZOOM) { + return false; + } + return true; + } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java index a54a84f83b..03a1d949f4 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java @@ -23,6 +23,11 @@ import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.ZoomButtonsController; +import com.mapbox.android.telemetry.AppUserTurnstile; +import com.mapbox.android.telemetry.Event; +import com.mapbox.android.telemetry.MapEventFactory; +import com.mapbox.android.telemetry.MapboxTelemetry; +import com.mapbox.mapboxsdk.BuildConfig; import com.mapbox.mapboxsdk.R; import com.mapbox.mapboxsdk.annotations.Annotation; import com.mapbox.mapboxsdk.annotations.MarkerViewManager; @@ -36,7 +41,6 @@ import com.mapbox.mapboxsdk.maps.widgets.MyLocationView; import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings; import com.mapbox.mapboxsdk.net.ConnectivityReceiver; import com.mapbox.mapboxsdk.storage.FileSource; -import com.mapbox.services.android.telemetry.MapboxTelemetry; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -285,7 +289,12 @@ public class MapView extends FrameLayout { @UiThread public void onCreate(@Nullable Bundle savedInstanceState) { if (savedInstanceState == null) { - MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapLoadEvent()); + MapboxTelemetry telemetry = Events.obtainTelemetry(); + AppUserTurnstile turnstileEvent = new AppUserTurnstile(BuildConfig.MAPBOX_SDK_IDENTIFIER, + BuildConfig.MAPBOX_SDK_VERSION); + telemetry.push(turnstileEvent); + MapEventFactory mapEventFactory = new MapEventFactory(); + telemetry.push(mapEventFactory.createMapLoadEvent(Event.Type.MAP_LOAD)); } else if (savedInstanceState.getBoolean(MapboxConstants.STATE_HAS_SAVED_STATE)) { this.savedInstanceState = savedInstanceState; } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxEventWrapper.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxEventWrapper.java deleted file mode 100644 index 6730278d79..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxEventWrapper.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - -import android.location.Location; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.mapbox.mapboxsdk.constants.MapboxConstants; -import com.mapbox.services.android.telemetry.MapboxEvent; - -import java.util.Hashtable; - -/** - * Wrapper class for MapboxEvent - *

- * Provides facility methods to use Transform and handle the case that the zoom, required for a telemetry event, - * isn't available yet. - *

- */ -class MapboxEventWrapper { - - @Nullable - static Hashtable buildMapClickEvent( - @NonNull Location location, @NonNull String gestureId, Transform transform) { - try { - double mapZoom = transform.getZoom(); - if (mapZoom >= MapboxConstants.MINIMUM_ZOOM && mapZoom <= MapboxConstants.MAXIMUM_ZOOM) { - // validate zoom #8057 - return MapboxEvent.buildMapClickEvent(location, gestureId, transform.getZoom()); - } - } catch (NullPointerException exception) { - // Map/Transform is not ready yet #8650 - // returning null is valid, event is ignored. - } - return null; - } - - @Nullable - static Hashtable buildMapDragEndEvent( - @NonNull Location location, Transform transform) { - try { - double mapZoom = transform.getZoom(); - if (mapZoom >= MapboxConstants.MINIMUM_ZOOM && mapZoom <= MapboxConstants.MAXIMUM_ZOOM) { - // validate zoom #8057 - return MapboxEvent.buildMapDragEndEvent(location, transform.getZoom()); - } - } catch (NullPointerException exception) { - // Map/Transform is not ready yet #8650 - // returning null is valid, event is ignored. - } - return null; - } - - @Nullable - static Hashtable buildMapLoadEvent() { - return MapboxEvent.buildMapLoadEvent(); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java index 834317dd25..2fd9a9010c 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java @@ -43,7 +43,7 @@ import com.mapbox.mapboxsdk.style.layers.Filter; import com.mapbox.mapboxsdk.style.layers.Layer; import com.mapbox.mapboxsdk.style.light.Light; import com.mapbox.mapboxsdk.style.sources.Source; -import com.mapbox.services.android.telemetry.location.LocationEngine; +import com.mapbox.android.core.location.LocationEngine; import java.lang.reflect.ParameterizedType; import java.util.HashMap; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java index 81fd091c90..3743096824 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java @@ -12,9 +12,9 @@ import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.constants.MyBearingTracking; import com.mapbox.mapboxsdk.constants.MyLocationTracking; import com.mapbox.mapboxsdk.maps.widgets.MyLocationView; -import com.mapbox.services.android.telemetry.location.LocationEngine; -import com.mapbox.services.android.telemetry.location.LocationEngineListener; -import com.mapbox.services.android.telemetry.permissions.PermissionsManager; +import com.mapbox.android.core.location.LocationEngine; +import com.mapbox.android.core.location.LocationEngineListener; +import com.mapbox.android.core.permissions.PermissionsManager; import timber.log.Timber; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java index 1cdd91028d..3f37da99d5 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java @@ -35,9 +35,9 @@ import com.mapbox.mapboxsdk.constants.MyLocationTracking; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.maps.Projection; -import com.mapbox.services.android.telemetry.location.LocationEngine; -import com.mapbox.services.android.telemetry.location.LocationEngineListener; -import com.mapbox.services.android.telemetry.location.LocationEnginePriority; +import com.mapbox.android.core.location.LocationEngine; +import com.mapbox.android.core.location.LocationEngineListener; +import com.mapbox.android.core.location.LocationEnginePriority; import java.lang.ref.WeakReference; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonOptions.java index 81f7255b86..79cde7429c 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonOptions.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonOptions.java @@ -13,7 +13,7 @@ public class GeoJsonOptions extends HashMap { /** * Maximum zoom level at which to create vector tiles (higher means greater detail at high zoom levels). * - * @param maxZoom the maximum zoom - Defaults to 18. + * @param minZoom the maximum zoom - Defaults to 18. * @return the current instance for chaining */ public GeoJsonOptions withMinZoom(int minZoom) { diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MathUtils.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MathUtils.java new file mode 100644 index 0000000000..0c90e4b244 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MathUtils.java @@ -0,0 +1,49 @@ +package com.mapbox.mapboxsdk.utils; + +// TODO Remove this class if we finally include it within MAS 3.x (GeoJSON) +public class MathUtils { + + /** + * Test a value in specified range, returning minimum if it's below, and maximum if it's above + * + * @param value Value to test + * @param min Minimum value of range + * @param max Maximum value of range + * @return value if it's between min and max, min if it's below, max if it's above + */ + public static double clamp(double value, double min, double max) { + return Math.max(min, Math.min(max, value)); + } + + /** + * Test a value in specified range, returning minimum if it's below, and maximum if it's above + * + * @param value Value to test + * @param min Minimum value of range + * @param max Maximum value of range + * @return value if it's between min and max, min if it's below, max if it's above + */ + public static float clamp(float value, float min, float max) { + return Math.max(min, Math.min(max, value)); + } + + /** + * Constrains value to the given range (including min, excluding max) via modular arithmetic. + *

+ * Same formula as used in Core GL (wrap.hpp) + * std::fmod((std::fmod((value - min), d) + d), d) + min; + * + * @param value Value to wrap + * @param min Minimum value + * @param max Maximum value + * @return Wrapped value + */ + public static double wrap(double value, double min, double max) { + double delta = max - min; + + double firstMod = (value - min) % delta; + double secondMod = (firstMod + delta) % delta; + + return secondMod + min; + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/MapboxTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/MapboxTest.java index 7a28d846ea..d9e3ae427d 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/MapboxTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/MapboxTest.java @@ -5,7 +5,7 @@ import android.net.ConnectivityManager; import android.net.NetworkInfo; import com.mapbox.mapboxsdk.exceptions.MapboxConfigurationException; -import com.mapbox.services.android.telemetry.location.LocationEngine; +import com.mapbox.android.core.location.LocationEngine; import org.junit.Before; import org.junit.Test; diff --git a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle index 1f889a6218..caff70e543 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle +++ b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle @@ -52,18 +52,17 @@ android { } dependencies { - implementation(project(':MapboxGLAndroidSDK')) - implementation(dependenciesList.mapboxJavaServices) { - transitive = true - } + api(project(':MapboxGLAndroidSDK')) + implementation dependenciesList.mapboxJavaServices - api dependenciesList.mapboxJavaTurf + implementation dependenciesList.mapboxJavaTurf implementation dependenciesList.supportAppcompatV7 implementation dependenciesList.supportRecyclerView implementation dependenciesList.supportDesign - implementation dependenciesList.lost + // implementation dependenciesList.lost + implementation dependenciesList.gmsLocation implementation dependenciesList.timber debugImplementation dependenciesList.leakCanaryDebug releaseImplementation dependenciesList.leakCanaryRelease diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml index 89f922fb9e..2d6efc0d84 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml @@ -806,7 +806,7 @@ + android:value="api-events-staging.tilestream.net"/> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java index 95cc9687f2..0ee1f78e0e 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java @@ -22,8 +22,8 @@ import com.mapbox.mapboxsdk.testapp.adapter.FeatureAdapter; import com.mapbox.mapboxsdk.testapp.adapter.FeatureSectionAdapter; import com.mapbox.mapboxsdk.testapp.model.activity.Feature; import com.mapbox.mapboxsdk.testapp.utils.ItemClickSupport; -import com.mapbox.services.android.telemetry.permissions.PermissionsListener; -import com.mapbox.services.android.telemetry.permissions.PermissionsManager; +import com.mapbox.android.core.permissions.PermissionsListener; +import com.mapbox.android.core.permissions.PermissionsManager; import java.util.ArrayList; import java.util.Collections; diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/BaseLocationActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/BaseLocationActivity.java index 71b8115d2e..eec26cc9a7 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/BaseLocationActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/BaseLocationActivity.java @@ -9,8 +9,8 @@ import android.support.design.widget.Snackbar; import android.support.v7.app.AppCompatActivity; import android.text.TextUtils; -import com.mapbox.services.android.telemetry.permissions.PermissionsListener; -import com.mapbox.services.android.telemetry.permissions.PermissionsManager; +import com.mapbox.android.core.permissions.PermissionsListener; +import com.mapbox.android.core.permissions.PermissionsManager; import java.util.List; diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MockLocationEngine.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MockLocationEngine.java index f4b54551bf..f4fe710de1 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MockLocationEngine.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MockLocationEngine.java @@ -5,8 +5,8 @@ import android.animation.TypeEvaluator; import android.animation.ValueAnimator; import android.location.Location; -import com.mapbox.services.android.telemetry.location.LocationEngine; -import com.mapbox.services.android.telemetry.location.LocationEngineListener; +import com.mapbox.android.core.location.LocationEngine; +import com.mapbox.android.core.location.LocationEngineListener; import timber.log.Timber; diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationDrawableActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationDrawableActivity.java index 000042306f..f603050030 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationDrawableActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationDrawableActivity.java @@ -15,7 +15,7 @@ import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.maps.MapboxMapOptions; import com.mapbox.mapboxsdk.testapp.R; -import com.mapbox.services.android.telemetry.location.LocationEngineListener; +import com.mapbox.android.core.location.LocationEngineListener; /** * Test activity showcasing how to change the MyLocationView drawable. diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTintActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTintActivity.java index 89774dc507..ff3c4dfbc0 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTintActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTintActivity.java @@ -18,7 +18,7 @@ import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.maps.TrackingSettings; import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings; import com.mapbox.mapboxsdk.testapp.R; -import com.mapbox.services.android.telemetry.location.LocationEngineListener; +import com.mapbox.android.core.location.LocationEngineListener; /** * Test activity showcasing how to tint the MyLocationView. diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTrackingModeActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTrackingModeActivity.java index 5ebe43e68c..ffbb2c1a90 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTrackingModeActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTrackingModeActivity.java @@ -25,7 +25,7 @@ import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; import com.mapbox.mapboxsdk.maps.TrackingSettings; import com.mapbox.mapboxsdk.maps.UiSettings; import com.mapbox.mapboxsdk.testapp.R; -import com.mapbox.services.android.telemetry.location.LocationEngineListener; +import com.mapbox.android.core.location.LocationEngineListener; import timber.log.Timber; diff --git a/platform/android/build.gradle b/platform/android/build.gradle index 0368558e92..6cd9505447 100644 --- a/platform/android/build.gradle +++ b/platform/android/build.gradle @@ -11,6 +11,7 @@ buildscript { allprojects { repositories { + mavenCentral() jcenter() google() maven { url "http://oss.sonatype.org/content/repositories/snapshots/" } diff --git a/platform/android/gradle/dependencies.gradle b/platform/android/gradle/dependencies.gradle index 99fa3f3508..4ef4ae2f7d 100644 --- a/platform/android/gradle/dependencies.gradle +++ b/platform/android/gradle/dependencies.gradle @@ -9,7 +9,7 @@ ext { versions = [ mapboxServices: '3.0.0-beta.2', - mapboxTelemetry: '2.2.9', + mapboxTelemetry: '3.0.0-beta.1', supportLib : '25.4.0', espresso : '3.0.1', testRunner : '1.0.1', @@ -46,6 +46,7 @@ ext { supportRecyclerView : "com.android.support:recyclerview-v7:${versions.supportLib}", lost : "com.mapzen.android:lost:${versions.lost}", + gmsLocation : 'com.google.android.gms:play-services-location:11.0.4', timber : "com.jakewharton.timber:timber:${versions.timber}", okhttp3 : "com.squareup.okhttp3:okhttp:${versions.okhttp}", leakCanaryDebug : "com.squareup.leakcanary:leakcanary-android:${versions.leakCanary}", -- cgit v1.2.1 From 7ec840bbabe61b291e1e52f15595a1b51069f97e Mon Sep 17 00:00:00 2001 From: Fabian Guerra Date: Tue, 13 Feb 2018 11:47:37 -0500 Subject: [android] Add Timber library import. --- .../src/main/java/com/mapbox/mapboxsdk/maps/MapView.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java index d2567a114b..990c56cb51 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java @@ -52,6 +52,8 @@ import java.util.Iterator; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; +import timber.log.Timber; + import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_MAP_NORTH_ANIMATION; import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_WAIT_IDLE; -- cgit v1.2.1 From b31eae7514a520ff2caddfae74f0d0cb63ba5287 Mon Sep 17 00:00:00 2001 From: Jesse Bounds Date: Tue, 16 Jan 2018 16:36:26 -0800 Subject: [ios] Remove unused reference to reachability This removes a reference to reachability that has been used and incorrect since the file does not exist at the referenced location. --- platform/ios/ios.xcodeproj/project.pbxproj | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index 9614bd009a..e177a9bc87 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -1645,16 +1645,6 @@ path = ../vendor/SMCalloutView; sourceTree = ""; }; - DA8848911CBB049300AB86E3 /* reachability */ = { - isa = PBXGroup; - children = ( - DA88488D1CBB047F00AB86E3 /* reachability.h */, - DA88488F1CBB048E00AB86E3 /* reachability.m */, - ); - name = reachability; - path = ..; - sourceTree = ""; - }; DA8933B91CCD2C6700E68420 /* Foundation Resources */ = { isa = PBXGroup; children = ( -- cgit v1.2.1 From eddcefb3c8e6e2a9d9c191b42f7ea19fc615c107 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Tue, 13 Feb 2018 14:10:48 +0100 Subject: [android] - update changelog for 6.0.0-beta.2 --- platform/android/CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index 6f81db7e3b..afac6d4902 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -2,6 +2,16 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started. +## 6.0.0-beta.2 - February 13, 2018 + - Deprecate LocationEngine [#11185](https://github.com/mapbox/mapbox-gl-native/pull/11185) + - Remove LOST from SDK [11186](https://github.com/mapbox/mapbox-gl-native/pull/11186) + - Transparent surface configuration on TextureView [#11065](https://github.com/mapbox/mapbox-gl-native/pull/11065) + - Constrained setLatLng documentation, expose setLatLngZoom method [#11184](https://github.com/mapbox/mapbox-gl-native/pull/11184) + - Integration of new events library [#10999](https://github.com/mapbox/mapbox-gl-native/pull/10999) + - AddImage performance improvement [#11111](https://github.com/mapbox/mapbox-gl-native/pull/11111) + - Migrate MAS to 3.0.0, refactor GeoJson integration [#11149](https://github.com/mapbox/mapbox-gl-native/pull/11149) + - Remove @jar and @aar dependency suffixes [#11161](https://github.com/mapbox/mapbox-gl-native/pull/11161) + ## 5.4.1 - February 9, 2018 - Don't recreate TextureView surface as part of view resizing, solves OOM crashes [#11148](https://github.com/mapbox/mapbox-gl-native/pull/11148) - Don't invoke OnLowMemory before map is ready, solves startup crash on low memory devices [#11109](https://github.com/mapbox/mapbox-gl-native/pull/11109) -- cgit v1.2.1 From f7217799631dc3e49c59836540f7de5f951f605a Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Tue, 13 Feb 2018 14:29:50 -0800 Subject: [core] fix onLowMemory to release GL buffers Context cleanup must be called _after_ render sources release tiles. --- src/mbgl/renderer/renderer_impl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp index 61e7d17242..9aca5ab978 100644 --- a/src/mbgl/renderer/renderer_impl.cpp +++ b/src/mbgl/renderer/renderer_impl.cpp @@ -724,10 +724,10 @@ std::vector Renderer::Impl::querySourceFeatures(const std::string& sour void Renderer::Impl::onLowMemory() { assert(BackendScope::exists()); - backend.getContext().performCleanup(); for (const auto& entry : renderSources) { entry.second->onLowMemory(); } + backend.getContext().performCleanup(); observer->onInvalidate(); } -- cgit v1.2.1 From a11b277bc19bdf142dcc85927f4e7d7acaeb3ff1 Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Wed, 14 Feb 2018 10:11:45 -0800 Subject: [core] Rename "onLowMemory" to "reduceMemoryUse". Android still calls "reduceMemoryUse" only while handling a low memory event. iOS, on the other hand, calls "reduceMemoryUse" every time it enters the background. --- include/mbgl/renderer/renderer.hpp | 2 +- platform/android/src/android_renderer_frontend.cpp | 4 ++-- platform/android/src/android_renderer_frontend.hpp | 2 +- platform/android/src/native_map_view.cpp | 2 +- platform/darwin/src/MGLRendererFrontend.h | 4 ++-- platform/ios/src/MGLMapView.mm | 4 ++-- src/mbgl/annotation/render_annotation_source.cpp | 4 ++-- src/mbgl/annotation/render_annotation_source.hpp | 2 +- src/mbgl/renderer/render_source.hpp | 2 +- src/mbgl/renderer/renderer.cpp | 4 ++-- src/mbgl/renderer/renderer_impl.cpp | 4 ++-- src/mbgl/renderer/renderer_impl.hpp | 2 +- src/mbgl/renderer/sources/render_custom_geometry_source.cpp | 4 ++-- src/mbgl/renderer/sources/render_custom_geometry_source.hpp | 2 +- src/mbgl/renderer/sources/render_geojson_source.cpp | 4 ++-- src/mbgl/renderer/sources/render_geojson_source.hpp | 2 +- src/mbgl/renderer/sources/render_image_source.hpp | 2 +- src/mbgl/renderer/sources/render_raster_dem_source.cpp | 4 ++-- src/mbgl/renderer/sources/render_raster_dem_source.hpp | 2 +- src/mbgl/renderer/sources/render_raster_source.cpp | 4 ++-- src/mbgl/renderer/sources/render_raster_source.hpp | 2 +- src/mbgl/renderer/sources/render_vector_source.cpp | 4 ++-- src/mbgl/renderer/sources/render_vector_source.hpp | 2 +- src/mbgl/renderer/tile_pyramid.cpp | 2 +- src/mbgl/renderer/tile_pyramid.hpp | 2 +- 25 files changed, 36 insertions(+), 36 deletions(-) diff --git a/include/mbgl/renderer/renderer.hpp b/include/mbgl/renderer/renderer.hpp index db28ee92fc..798928087a 100644 --- a/include/mbgl/renderer/renderer.hpp +++ b/include/mbgl/renderer/renderer.hpp @@ -48,7 +48,7 @@ public: void dumpDebugLogs(); // Memory - void onLowMemory(); + void reduceMemoryUse(); private: class Impl; diff --git a/platform/android/src/android_renderer_frontend.cpp b/platform/android/src/android_renderer_frontend.cpp index afdb08a10e..2a03d9de9e 100644 --- a/platform/android/src/android_renderer_frontend.cpp +++ b/platform/android/src/android_renderer_frontend.cpp @@ -77,8 +77,8 @@ void AndroidRendererFrontend::update(std::shared_ptr params) { mapRenderer.requestRender(); } -void AndroidRendererFrontend::onLowMemory() { - mapRenderer.actor().invoke(&Renderer::onLowMemory); +void AndroidRendererFrontend::reduceMemoryUse() { + mapRenderer.actor().invoke(&Renderer::reduceMemoryUse); } std::vector AndroidRendererFrontend::querySourceFeatures(const std::string& sourceID, diff --git a/platform/android/src/android_renderer_frontend.hpp b/platform/android/src/android_renderer_frontend.hpp index 178870c452..b61904e388 100644 --- a/platform/android/src/android_renderer_frontend.hpp +++ b/platform/android/src/android_renderer_frontend.hpp @@ -39,7 +39,7 @@ public: AnnotationIDs queryShapeAnnotations(const ScreenBox& box) const; // Memory - void onLowMemory(); + void reduceMemoryUse(); private: MapRenderer& mapRenderer; diff --git a/platform/android/src/native_map_view.cpp b/platform/android/src/native_map_view.cpp index 36a73fee35..67fc132204 100755 --- a/platform/android/src/native_map_view.cpp +++ b/platform/android/src/native_map_view.cpp @@ -450,7 +450,7 @@ jni::Array NativeMapView::addMarkers(jni::JNIEnv& env, jni::ArrayonLowMemory(); + rendererFrontend->reduceMemoryUse(); } using DebugOptions = mbgl::MapDebugOptions; diff --git a/platform/darwin/src/MGLRendererFrontend.h b/platform/darwin/src/MGLRendererFrontend.h index 76904d008b..2df67ca4e4 100644 --- a/platform/darwin/src/MGLRendererFrontend.h +++ b/platform/darwin/src/MGLRendererFrontend.h @@ -56,9 +56,9 @@ public: return renderer.get(); } - void onLowMemory() { + void reduceMemoryUse() { if (!renderer) return; - renderer->onLowMemory(); + renderer->reduceMemoryUse(); } private: diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 1dd0c4c6d3..e6712ba5e6 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -716,7 +716,7 @@ public: { MGLAssertIsMainThread(); - _rendererFrontend->onLowMemory(); + _rendererFrontend->reduceMemoryUse(); } #pragma mark - Layout - @@ -2315,7 +2315,7 @@ public: - (void)emptyMemoryCache { - _rendererFrontend->onLowMemory(); + _rendererFrontend->reduceMemoryUse(); } - (void)setZoomEnabled:(BOOL)zoomEnabled diff --git a/src/mbgl/annotation/render_annotation_source.cpp b/src/mbgl/annotation/render_annotation_source.cpp index 38ca5ccd0b..a237100d13 100644 --- a/src/mbgl/annotation/render_annotation_source.cpp +++ b/src/mbgl/annotation/render_annotation_source.cpp @@ -73,8 +73,8 @@ std::vector RenderAnnotationSource::querySourceFeatures(const SourceQue return {}; } -void RenderAnnotationSource::onLowMemory() { - tilePyramid.onLowMemory(); +void RenderAnnotationSource::reduceMemoryUse() { + tilePyramid.reduceMemoryUse(); } void RenderAnnotationSource::dumpDebugLogs() const { diff --git a/src/mbgl/annotation/render_annotation_source.hpp b/src/mbgl/annotation/render_annotation_source.hpp index aa2578d4e3..e812ec2883 100644 --- a/src/mbgl/annotation/render_annotation_source.hpp +++ b/src/mbgl/annotation/render_annotation_source.hpp @@ -33,7 +33,7 @@ public: std::vector querySourceFeatures(const SourceQueryOptions&) const final; - void onLowMemory() final; + void reduceMemoryUse() final; void dumpDebugLogs() const final; private: diff --git a/src/mbgl/renderer/render_source.hpp b/src/mbgl/renderer/render_source.hpp index db88230e53..53519c763e 100644 --- a/src/mbgl/renderer/render_source.hpp +++ b/src/mbgl/renderer/render_source.hpp @@ -70,7 +70,7 @@ public: virtual std::vector querySourceFeatures(const SourceQueryOptions&) const = 0; - virtual void onLowMemory() = 0; + virtual void reduceMemoryUse() = 0; virtual void dumpDebugLogs() const = 0; diff --git a/src/mbgl/renderer/renderer.cpp b/src/mbgl/renderer/renderer.cpp index 6d086c70b1..1d2f2bb522 100644 --- a/src/mbgl/renderer/renderer.cpp +++ b/src/mbgl/renderer/renderer.cpp @@ -94,9 +94,9 @@ void Renderer::dumpDebugLogs() { impl->dumDebugLogs(); } -void Renderer::onLowMemory() { +void Renderer::reduceMemoryUse() { BackendScope guard { impl->backend }; - impl->onLowMemory(); + impl->reduceMemoryUse(); } } // namespace mbgl diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp index 9aca5ab978..26a07bb093 100644 --- a/src/mbgl/renderer/renderer_impl.cpp +++ b/src/mbgl/renderer/renderer_impl.cpp @@ -722,10 +722,10 @@ std::vector Renderer::Impl::querySourceFeatures(const std::string& sour return source->querySourceFeatures(options); } -void Renderer::Impl::onLowMemory() { +void Renderer::Impl::reduceMemoryUse() { assert(BackendScope::exists()); for (const auto& entry : renderSources) { - entry.second->onLowMemory(); + entry.second->reduceMemoryUse(); } backend.getContext().performCleanup(); observer->onInvalidate(); diff --git a/src/mbgl/renderer/renderer_impl.hpp b/src/mbgl/renderer/renderer_impl.hpp index 4f45d514a5..4675ac79ea 100644 --- a/src/mbgl/renderer/renderer_impl.hpp +++ b/src/mbgl/renderer/renderer_impl.hpp @@ -53,7 +53,7 @@ public: std::vector querySourceFeatures(const std::string& sourceID, const SourceQueryOptions&) const; std::vector queryShapeAnnotations(const ScreenLineString&) const; - void onLowMemory(); + void reduceMemoryUse(); void dumDebugLogs(); private: diff --git a/src/mbgl/renderer/sources/render_custom_geometry_source.cpp b/src/mbgl/renderer/sources/render_custom_geometry_source.cpp index df615a7e20..057ad5a4a7 100644 --- a/src/mbgl/renderer/sources/render_custom_geometry_source.cpp +++ b/src/mbgl/renderer/sources/render_custom_geometry_source.cpp @@ -76,8 +76,8 @@ std::vector RenderCustomGeometrySource::querySourceFeatures(const Sourc return tilePyramid.querySourceFeatures(options); } -void RenderCustomGeometrySource::onLowMemory() { - tilePyramid.onLowMemory(); +void RenderCustomGeometrySource::reduceMemoryUse() { + tilePyramid.reduceMemoryUse(); } void RenderCustomGeometrySource::dumpDebugLogs() const { diff --git a/src/mbgl/renderer/sources/render_custom_geometry_source.hpp b/src/mbgl/renderer/sources/render_custom_geometry_source.hpp index 82e691d5c9..033d731029 100644 --- a/src/mbgl/renderer/sources/render_custom_geometry_source.hpp +++ b/src/mbgl/renderer/sources/render_custom_geometry_source.hpp @@ -33,7 +33,7 @@ public: std::vector querySourceFeatures(const SourceQueryOptions&) const final; - void onLowMemory() final; + void reduceMemoryUse() final; void dumpDebugLogs() const final; private: diff --git a/src/mbgl/renderer/sources/render_geojson_source.cpp b/src/mbgl/renderer/sources/render_geojson_source.cpp index 8ea80cd813..cbf4db70b5 100644 --- a/src/mbgl/renderer/sources/render_geojson_source.cpp +++ b/src/mbgl/renderer/sources/render_geojson_source.cpp @@ -94,8 +94,8 @@ std::vector RenderGeoJSONSource::querySourceFeatures(const SourceQueryO return tilePyramid.querySourceFeatures(options); } -void RenderGeoJSONSource::onLowMemory() { - tilePyramid.onLowMemory(); +void RenderGeoJSONSource::reduceMemoryUse() { + tilePyramid.reduceMemoryUse(); } void RenderGeoJSONSource::dumpDebugLogs() const { diff --git a/src/mbgl/renderer/sources/render_geojson_source.hpp b/src/mbgl/renderer/sources/render_geojson_source.hpp index 55166ea901..72fccbd043 100644 --- a/src/mbgl/renderer/sources/render_geojson_source.hpp +++ b/src/mbgl/renderer/sources/render_geojson_source.hpp @@ -37,7 +37,7 @@ public: std::vector querySourceFeatures(const SourceQueryOptions&) const final; - void onLowMemory() final; + void reduceMemoryUse() final; void dumpDebugLogs() const final; private: diff --git a/src/mbgl/renderer/sources/render_image_source.hpp b/src/mbgl/renderer/sources/render_image_source.hpp index 72cf4cea61..85ee0ace11 100644 --- a/src/mbgl/renderer/sources/render_image_source.hpp +++ b/src/mbgl/renderer/sources/render_image_source.hpp @@ -37,7 +37,7 @@ public: std::vector querySourceFeatures(const SourceQueryOptions&) const final; - void onLowMemory() final { + void reduceMemoryUse() final { } void dumpDebugLogs() const final; diff --git a/src/mbgl/renderer/sources/render_raster_dem_source.cpp b/src/mbgl/renderer/sources/render_raster_dem_source.cpp index 4de949c9f2..fb8f2c69d4 100644 --- a/src/mbgl/renderer/sources/render_raster_dem_source.cpp +++ b/src/mbgl/renderer/sources/render_raster_dem_source.cpp @@ -155,8 +155,8 @@ std::vector RenderRasterDEMSource::querySourceFeatures(const SourceQuer return {}; } -void RenderRasterDEMSource::onLowMemory() { - tilePyramid.onLowMemory(); +void RenderRasterDEMSource::reduceMemoryUse() { + tilePyramid.reduceMemoryUse(); } void RenderRasterDEMSource::dumpDebugLogs() const { diff --git a/src/mbgl/renderer/sources/render_raster_dem_source.hpp b/src/mbgl/renderer/sources/render_raster_dem_source.hpp index 87f9bcb563..3c2a90c37f 100644 --- a/src/mbgl/renderer/sources/render_raster_dem_source.hpp +++ b/src/mbgl/renderer/sources/render_raster_dem_source.hpp @@ -33,7 +33,7 @@ public: std::vector querySourceFeatures(const SourceQueryOptions&) const final; - void onLowMemory() final; + void reduceMemoryUse() final; void dumpDebugLogs() const final; private: diff --git a/src/mbgl/renderer/sources/render_raster_source.cpp b/src/mbgl/renderer/sources/render_raster_source.cpp index 5d32818663..60b3fa9a3b 100644 --- a/src/mbgl/renderer/sources/render_raster_source.cpp +++ b/src/mbgl/renderer/sources/render_raster_source.cpp @@ -85,8 +85,8 @@ std::vector RenderRasterSource::querySourceFeatures(const SourceQueryOp return {}; } -void RenderRasterSource::onLowMemory() { - tilePyramid.onLowMemory(); +void RenderRasterSource::reduceMemoryUse() { + tilePyramid.reduceMemoryUse(); } void RenderRasterSource::dumpDebugLogs() const { diff --git a/src/mbgl/renderer/sources/render_raster_source.hpp b/src/mbgl/renderer/sources/render_raster_source.hpp index bc6ac1bd9f..78eda199ac 100644 --- a/src/mbgl/renderer/sources/render_raster_source.hpp +++ b/src/mbgl/renderer/sources/render_raster_source.hpp @@ -33,7 +33,7 @@ public: std::vector querySourceFeatures(const SourceQueryOptions&) const final; - void onLowMemory() final; + void reduceMemoryUse() final; void dumpDebugLogs() const final; private: diff --git a/src/mbgl/renderer/sources/render_vector_source.cpp b/src/mbgl/renderer/sources/render_vector_source.cpp index c01257aea9..e87bea5dcd 100644 --- a/src/mbgl/renderer/sources/render_vector_source.cpp +++ b/src/mbgl/renderer/sources/render_vector_source.cpp @@ -88,8 +88,8 @@ std::vector RenderVectorSource::querySourceFeatures(const SourceQueryOp return tilePyramid.querySourceFeatures(options); } -void RenderVectorSource::onLowMemory() { - tilePyramid.onLowMemory(); +void RenderVectorSource::reduceMemoryUse() { + tilePyramid.reduceMemoryUse(); } void RenderVectorSource::dumpDebugLogs() const { diff --git a/src/mbgl/renderer/sources/render_vector_source.hpp b/src/mbgl/renderer/sources/render_vector_source.hpp index 57fe6dbb6c..592160dc16 100644 --- a/src/mbgl/renderer/sources/render_vector_source.hpp +++ b/src/mbgl/renderer/sources/render_vector_source.hpp @@ -33,7 +33,7 @@ public: std::vector querySourceFeatures(const SourceQueryOptions&) const final; - void onLowMemory() final; + void reduceMemoryUse() final; void dumpDebugLogs() const final; private: diff --git a/src/mbgl/renderer/tile_pyramid.cpp b/src/mbgl/renderer/tile_pyramid.cpp index 07239b7a1c..c4372e7112 100644 --- a/src/mbgl/renderer/tile_pyramid.cpp +++ b/src/mbgl/renderer/tile_pyramid.cpp @@ -303,7 +303,7 @@ void TilePyramid::setCacheSize(size_t size) { cache.setSize(size); } -void TilePyramid::onLowMemory() { +void TilePyramid::reduceMemoryUse() { cache.clear(); } diff --git a/src/mbgl/renderer/tile_pyramid.hpp b/src/mbgl/renderer/tile_pyramid.hpp index ad3f91bf88..2638599c38 100644 --- a/src/mbgl/renderer/tile_pyramid.hpp +++ b/src/mbgl/renderer/tile_pyramid.hpp @@ -59,7 +59,7 @@ public: std::vector querySourceFeatures(const SourceQueryOptions&) const; void setCacheSize(size_t); - void onLowMemory(); + void reduceMemoryUse(); void setObserver(TileObserver*); void dumpDebugLogs() const; -- cgit v1.2.1 From d4df044ffe2234e3d14d1d86727372cd8e4953d8 Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Tue, 13 Feb 2018 14:33:07 -0800 Subject: [ios] Release cached tiles on entering background. Retain current render tiles for fast restart. Waiting for a memory warning doesn't work because we can't make GL release calls once we're in the background. --- platform/ios/src/MGLMapView.mm | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index e6712ba5e6..6f2d5039be 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -1158,6 +1158,13 @@ public: - (void)sleepGL:(__unused NSNotification *)notification { MGLAssertIsMainThread(); + + // Ideally we would wait until we actually received a memory warning but the bulk of the memory + // we have to release is tied up in GL buffers that we can't touch once we're in the background. + // Compromise position: release everything but currently rendering tiles + // A possible improvement would be to store a copy of the GL buffers that we could use to rapidly + // restart, but that we could also discard in response to a memory warning. + _rendererFrontend->reduceMemoryUse(); if ( ! self.dormant) { -- cgit v1.2.1 From a386cac452b333d3e9656efd04e98035cd6b1c57 Mon Sep 17 00:00:00 2001 From: Molly Lloyd Date: Wed, 14 Feb 2018 11:28:51 -0800 Subject: [core] add maxzoom uniform for raster-dem tilesets (#11134) * add maxzoom uniform to support external tilesets * update git sha for gl-js * try and fix android crash * name default maxzoom constant --- mapbox-gl-js | 2 +- src/mbgl/programs/hillshade_prepare_program.hpp | 2 ++ src/mbgl/renderer/layers/render_hillshade_layer.cpp | 8 +++++++- src/mbgl/renderer/layers/render_hillshade_layer.hpp | 2 +- src/mbgl/renderer/sources/render_raster_dem_source.cpp | 2 +- src/mbgl/renderer/sources/render_raster_dem_source.hpp | 5 +++++ src/mbgl/shaders/hillshade_prepare.cpp | 9 +++++---- 7 files changed, 22 insertions(+), 8 deletions(-) diff --git a/mapbox-gl-js b/mapbox-gl-js index 063fdebeaf..920de235a7 160000 --- a/mapbox-gl-js +++ b/mapbox-gl-js @@ -1 +1 @@ -Subproject commit 063fdebeaffbf6bc3ffff32233ed6248f42f3c59 +Subproject commit 920de235a7257f1042ff9a0eb0f39404d4724bcb diff --git a/src/mbgl/programs/hillshade_prepare_program.hpp b/src/mbgl/programs/hillshade_prepare_program.hpp index 0f31a22df5..76638afddd 100644 --- a/src/mbgl/programs/hillshade_prepare_program.hpp +++ b/src/mbgl/programs/hillshade_prepare_program.hpp @@ -10,6 +10,7 @@ namespace mbgl { namespace uniforms { MBGL_DEFINE_UNIFORM_VECTOR(uint16_t, 2, u_dimension); +MBGL_DEFINE_UNIFORM_SCALAR(float, u_maxzoom); } // namespace uniforms class HillshadePrepareProgram : public Program< @@ -22,6 +23,7 @@ class HillshadePrepareProgram : public Program< uniforms::u_matrix, uniforms::u_dimension, uniforms::u_zoom, + uniforms::u_maxzoom, uniforms::u_image>, style::Properties<>> { public: diff --git a/src/mbgl/renderer/layers/render_hillshade_layer.cpp b/src/mbgl/renderer/layers/render_hillshade_layer.cpp index 55702849df..bcfd4ffe99 100644 --- a/src/mbgl/renderer/layers/render_hillshade_layer.cpp +++ b/src/mbgl/renderer/layers/render_hillshade_layer.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -55,10 +56,14 @@ bool RenderHillshadeLayer::hasTransition() const { return unevaluated.hasTransition(); } -void RenderHillshadeLayer::render(PaintParameters& parameters, RenderSource*) { +void RenderHillshadeLayer::render(PaintParameters& parameters, RenderSource* src) { if (parameters.pass != RenderPass::Translucent && parameters.pass != RenderPass::Pass3D) return; + RenderRasterDEMSource* demsrc = dynamic_cast(src); + const uint8_t TERRAIN_RGB_MAXZOOM = 15; + const uint8_t maxzoom = demsrc != nullptr ? demsrc->getMaxZoom() : TERRAIN_RGB_MAXZOOM; + auto draw = [&] (const mat4& matrix, const auto& vertexBuffer, const auto& indexBuffer, @@ -118,6 +123,7 @@ void RenderHillshadeLayer::render(PaintParameters& parameters, RenderSource*) { uniforms::u_matrix::Value { mat }, uniforms::u_dimension::Value { {{uint16_t(tilesize * 2), uint16_t(tilesize * 2) }} }, uniforms::u_zoom::Value{ float(tile.id.canonical.z) }, + uniforms::u_maxzoom::Value{ float(maxzoom) }, uniforms::u_image::Value{ 0 } }, parameters.staticData.rasterVertexBuffer, diff --git a/src/mbgl/renderer/layers/render_hillshade_layer.hpp b/src/mbgl/renderer/layers/render_hillshade_layer.hpp index e9b9db1ec3..13093ee7ef 100644 --- a/src/mbgl/renderer/layers/render_hillshade_layer.hpp +++ b/src/mbgl/renderer/layers/render_hillshade_layer.hpp @@ -16,7 +16,7 @@ public: void evaluate(const PropertyEvaluationParameters&) override; bool hasTransition() const override; - void render(PaintParameters&, RenderSource*) override; + void render(PaintParameters&, RenderSource* src) override; std::unique_ptr createBucket(const BucketParameters&, const std::vector&) const override; diff --git a/src/mbgl/renderer/sources/render_raster_dem_source.cpp b/src/mbgl/renderer/sources/render_raster_dem_source.cpp index fb8f2c69d4..b3153622c3 100644 --- a/src/mbgl/renderer/sources/render_raster_dem_source.cpp +++ b/src/mbgl/renderer/sources/render_raster_dem_source.cpp @@ -36,7 +36,7 @@ void RenderRasterDEMSource::update(Immutable baseImpl_, if (tileset != _tileset) { tileset = _tileset; - + maxzoom = tileset->zoomRange.max; // TODO: this removes existing buckets, and will cause flickering. // Should instead refresh tile data in place. tilePyramid.tiles.clear(); diff --git a/src/mbgl/renderer/sources/render_raster_dem_source.hpp b/src/mbgl/renderer/sources/render_raster_dem_source.hpp index 3c2a90c37f..741214a14d 100644 --- a/src/mbgl/renderer/sources/render_raster_dem_source.hpp +++ b/src/mbgl/renderer/sources/render_raster_dem_source.hpp @@ -36,11 +36,16 @@ public: void reduceMemoryUse() final; void dumpDebugLogs() const final; + uint8_t getMaxZoom() const { + return maxzoom; + }; + private: const style::RasterSource::Impl& impl() const; TilePyramid tilePyramid; optional tileset; + uint8_t maxzoom = 15; protected: void onTileChanged(Tile&) final; diff --git a/src/mbgl/shaders/hillshade_prepare.cpp b/src/mbgl/shaders/hillshade_prepare.cpp index 733658435e..8d0571f6a4 100644 --- a/src/mbgl/shaders/hillshade_prepare.cpp +++ b/src/mbgl/shaders/hillshade_prepare.cpp @@ -29,6 +29,7 @@ uniform sampler2D u_image; varying vec2 v_pos; uniform vec2 u_dimension; uniform float u_zoom; +uniform float u_maxzoom; float getElevation(vec2 coord, float bias) { // Convert encoded elevation value to meters @@ -71,16 +72,16 @@ void main() { // which can be reduced to: pow(2, 19.25619978527 - u_zoom) // we want to vertically exaggerate the hillshading though, because otherwise // it is barely noticeable at low zooms. to do this, we multiply this by some - // scale factor pow(2, (u_zoom - 14) * a) where a is an arbitrary value and 14 is the - // maxzoom of the tile source. here we use a=0.3 which works out to the - // expression below. see nickidlugash's awesome breakdown for more info + // scale factor pow(2, (u_zoom - u_maxzoom) * a) where a is an arbitrary value + // Here we use a=0.3 which works out to the expression below. see + // nickidlugash's awesome breakdown for more info // https://github.com/mapbox/mapbox-gl-js/pull/5286#discussion_r148419556 float exaggeration = u_zoom < 2.0 ? 0.4 : u_zoom < 4.5 ? 0.35 : 0.3; vec2 deriv = vec2( (c + f + f + i) - (a + d + d + g), (g + h + h + i) - (a + b + b + c) - ) / pow(2.0, (u_zoom - 14.0) * exaggeration + 19.2562 - u_zoom); + ) / pow(2.0, (u_zoom - u_maxzoom) * exaggeration + 19.2562 - u_zoom); gl_FragColor = clamp(vec4( deriv.x / 2.0 + 0.5, -- cgit v1.2.1 From 8a283b030629abb00367929d047731c3974bc3d6 Mon Sep 17 00:00:00 2001 From: Molly Lloyd Date: Wed, 14 Feb 2018 13:17:55 -0800 Subject: [core] add support for mapzen terrarium (#11154) * add support for mapzen terrarium * Encoding --> DEMEncoding, avoid if statement when unpacking elevation values * add Terrarium test * update submodule * remove redundant checks --- include/mbgl/util/tileset.hpp | 10 ++++-- mapbox-gl-js | 2 +- .../mapboxsdk/style/layers/HillshadeLayer.java | 2 +- src/mbgl/geometry/dem_data.cpp | 16 +++++++-- src/mbgl/geometry/dem_data.hpp | 3 +- src/mbgl/renderer/buckets/hillshade_bucket.cpp | 2 +- src/mbgl/renderer/buckets/hillshade_bucket.hpp | 5 +-- src/mbgl/style/conversion/tileset.cpp | 10 ++++++ src/mbgl/tile/raster_dem_tile.cpp | 3 +- src/mbgl/tile/raster_dem_tile.hpp | 1 + src/mbgl/tile/raster_dem_tile_worker.cpp | 4 +-- src/mbgl/tile/raster_dem_tile_worker.hpp | 3 +- test/geometry/dem_data.test.cpp | 39 +++++++++++++--------- test/tile/raster_dem_tile.test.cpp | 2 +- 14 files changed, 72 insertions(+), 30 deletions(-) diff --git a/include/mbgl/util/tileset.hpp b/include/mbgl/util/tileset.hpp index 1b7b8f0f75..ed2b907647 100644 --- a/include/mbgl/util/tileset.hpp +++ b/include/mbgl/util/tileset.hpp @@ -14,22 +14,28 @@ namespace mbgl { class Tileset { public: enum class Scheme : bool { XYZ, TMS }; + enum class DEMEncoding : bool { Mapbox, Terrarium }; std::vector tiles; Range zoomRange; std::string attribution; Scheme scheme; + // DEMEncoding is not supported by the TileJSON spec + DEMEncoding encoding; optional bounds; + Tileset(std::vector tiles_ = std::vector(), Range zoomRange_ = { 0, util::DEFAULT_MAX_ZOOM }, std::string attribution_ = {}, - Scheme scheme_ = Scheme::XYZ) + Scheme scheme_ = Scheme::XYZ, + DEMEncoding encoding_ = DEMEncoding::Mapbox) : tiles(std::move(tiles_)), zoomRange(std::move(zoomRange_)), attribution(std::move(attribution_)), scheme(scheme_), - bounds() {} + encoding(encoding_), + bounds() {}; // TileJSON also includes center and zoom but they are not used by mbgl. diff --git a/mapbox-gl-js b/mapbox-gl-js index 920de235a7..f13c86ea35 160000 --- a/mapbox-gl-js +++ b/mapbox-gl-js @@ -1 +1 @@ -Subproject commit 920de235a7257f1042ff9a0eb0f39404d4724bcb +Subproject commit f13c86ea356c384fdab31855b9152f5bf5ef97b8 diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HillshadeLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HillshadeLayer.java index dc65cb5081..7e3d3a7779 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HillshadeLayer.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HillshadeLayer.java @@ -11,7 +11,7 @@ import static com.mapbox.mapboxsdk.utils.ColorUtils.rgbaToColor; import com.mapbox.mapboxsdk.style.layers.TransitionOptions; /** - * Client-side hillshading visualization based on DEM data. Currently, the implementation only supports Mapbox Terrain RGB tiles + * Client-side hillshading visualization based on DEM data. Currently, the implementation only supports Mapbox Terrain RGB and Mapzen Terrarium tiles. * * @see The online documentation */ diff --git a/src/mbgl/geometry/dem_data.cpp b/src/mbgl/geometry/dem_data.cpp index 78134dadc1..7fa98950ea 100644 --- a/src/mbgl/geometry/dem_data.cpp +++ b/src/mbgl/geometry/dem_data.cpp @@ -3,7 +3,7 @@ namespace mbgl { -DEMData::DEMData(const PremultipliedImage& _image): +DEMData::DEMData(const PremultipliedImage& _image, Tileset::DEMEncoding encoding): dim(_image.size.height), border(std::max(std::ceil(_image.size.height / 2), 1)), stride(dim + 2 * border), @@ -13,13 +13,25 @@ DEMData::DEMData(const PremultipliedImage& _image): throw std::runtime_error("raster-dem tiles must be square."); } + auto decodeMapbox = [] (const uint8_t r, const uint8_t g, const uint8_t b){ + // https://www.mapbox.com/help/access-elevation-data/#mapbox-terrain-rgb + return (r * 256 * 256 + g * 256 + b)/10 - 10000; + }; + + auto decodeTerrarium = [] (const uint8_t r, const uint8_t g, const uint8_t b){ + // https://aws.amazon.com/public-datasets/terrain/ + return ((r * 256 + g + b / 256) - 32768); + }; + + auto decodeRGB = encoding == Tileset::DEMEncoding::Terrarium ? decodeTerrarium : decodeMapbox; + std::memset(image.data.get(), 0, image.bytes()); for (int32_t y = 0; y < dim; y++) { for (int32_t x = 0; x < dim; x++) { const int32_t i = y * dim + x; const int32_t j = i * 4; - set(x, y, (_image.data[j] * 256 * 256 + _image.data[j+1] * 256 + _image.data[j+2])/10 - 10000); + set(x, y, decodeRGB(_image.data[j], _image.data[j+1], _image.data[j+2])); } } diff --git a/src/mbgl/geometry/dem_data.hpp b/src/mbgl/geometry/dem_data.hpp index 507a51661d..817d3cc9c9 100644 --- a/src/mbgl/geometry/dem_data.hpp +++ b/src/mbgl/geometry/dem_data.hpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -12,7 +13,7 @@ namespace mbgl { class DEMData { public: - DEMData(const PremultipliedImage& image); + DEMData(const PremultipliedImage& image, Tileset::DEMEncoding encoding); void backfillBorder(const DEMData& borderTileData, int8_t dx, int8_t dy); void set(const int32_t x, const int32_t y, const int32_t value) { diff --git a/src/mbgl/renderer/buckets/hillshade_bucket.cpp b/src/mbgl/renderer/buckets/hillshade_bucket.cpp index 8011681ee0..00b9536894 100644 --- a/src/mbgl/renderer/buckets/hillshade_bucket.cpp +++ b/src/mbgl/renderer/buckets/hillshade_bucket.cpp @@ -8,7 +8,7 @@ namespace mbgl { using namespace style; -HillshadeBucket::HillshadeBucket(PremultipliedImage&& image_): demdata(image_) { +HillshadeBucket::HillshadeBucket(PremultipliedImage&& image_, Tileset::DEMEncoding encoding): demdata(image_, encoding) { } HillshadeBucket::HillshadeBucket(DEMData&& demdata_) : demdata(std::move(demdata_)) { diff --git a/src/mbgl/renderer/buckets/hillshade_bucket.hpp b/src/mbgl/renderer/buckets/hillshade_bucket.hpp index 3d9f6c61af..5335f7ceda 100644 --- a/src/mbgl/renderer/buckets/hillshade_bucket.hpp +++ b/src/mbgl/renderer/buckets/hillshade_bucket.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -16,8 +17,8 @@ namespace mbgl { class HillshadeBucket : public Bucket { public: - HillshadeBucket(PremultipliedImage&&); - HillshadeBucket(std::shared_ptr); + HillshadeBucket(PremultipliedImage&&, Tileset::DEMEncoding encoding); + HillshadeBucket(std::shared_ptr, Tileset::DEMEncoding encoding); HillshadeBucket(DEMData&&); diff --git a/src/mbgl/style/conversion/tileset.cpp b/src/mbgl/style/conversion/tileset.cpp index 88e78b1a83..6d89cef944 100644 --- a/src/mbgl/style/conversion/tileset.cpp +++ b/src/mbgl/style/conversion/tileset.cpp @@ -40,6 +40,16 @@ optional Converter::operator()(const Convertible& value, Error } } + auto encodingValue = objectMember(value, "encoding"); + if (encodingValue) { + optional encoding = toString(*encodingValue); + if (encoding && *encoding == "terrarium") { + result.encoding = Tileset::DEMEncoding::Terrarium; + } else if (encoding && *encoding != "mapbox") { + error = { "invalid raster-dem encoding type - valid types are 'mapbox' and 'terrarium' " }; + } + } + auto minzoomValue = objectMember(value, "minzoom"); if (minzoomValue) { optional minzoom = toNumber(*minzoomValue); diff --git a/src/mbgl/tile/raster_dem_tile.cpp b/src/mbgl/tile/raster_dem_tile.cpp index b270378ece..5db298cf4c 100644 --- a/src/mbgl/tile/raster_dem_tile.cpp +++ b/src/mbgl/tile/raster_dem_tile.cpp @@ -21,6 +21,7 @@ RasterDEMTile::RasterDEMTile(const OverscaledTileID& id_, worker(parameters.workerScheduler, ActorRef(*this, mailbox)) { + encoding = tileset.encoding; if ( id.canonical.y == 0 ){ // this tile doesn't have upper neighboring tiles so marked those as backfilled neighboringTiles = neighboringTiles | DEMTileNeighbors::NoUpper; @@ -47,7 +48,7 @@ void RasterDEMTile::setMetadata(optional modified_, optional data) { pending = true; ++correlationID; - worker.invoke(&RasterDEMTileWorker::parse, data, correlationID); + worker.invoke(&RasterDEMTileWorker::parse, data, correlationID, encoding); } void RasterDEMTile::onParsed(std::unique_ptr result, const uint64_t resultCorrelationID) { diff --git a/src/mbgl/tile/raster_dem_tile.hpp b/src/mbgl/tile/raster_dem_tile.hpp index 68f8a91e00..0c8dd75961 100644 --- a/src/mbgl/tile/raster_dem_tile.hpp +++ b/src/mbgl/tile/raster_dem_tile.hpp @@ -94,6 +94,7 @@ private: Actor worker; uint64_t correlationID = 0; + Tileset::DEMEncoding encoding; // Contains the Bucket object for the tile. Buckets are render // objects and they get added by tile parsing operations. diff --git a/src/mbgl/tile/raster_dem_tile_worker.cpp b/src/mbgl/tile/raster_dem_tile_worker.cpp index ed8573788f..7338e578c7 100644 --- a/src/mbgl/tile/raster_dem_tile_worker.cpp +++ b/src/mbgl/tile/raster_dem_tile_worker.cpp @@ -10,14 +10,14 @@ RasterDEMTileWorker::RasterDEMTileWorker(ActorRef, ActorRef : parent(std::move(parent_)) { } -void RasterDEMTileWorker::parse(std::shared_ptr data, uint64_t correlationID) { +void RasterDEMTileWorker::parse(std::shared_ptr data, uint64_t correlationID, Tileset::DEMEncoding encoding) { if (!data) { parent.invoke(&RasterDEMTile::onParsed, nullptr, correlationID); // No data; empty tile. return; } try { - auto bucket = std::make_unique(decodeImage(*data)); + auto bucket = std::make_unique(decodeImage(*data), encoding); parent.invoke(&RasterDEMTile::onParsed, std::move(bucket), correlationID); } catch (...) { parent.invoke(&RasterDEMTile::onError, std::current_exception(), correlationID); diff --git a/src/mbgl/tile/raster_dem_tile_worker.hpp b/src/mbgl/tile/raster_dem_tile_worker.hpp index 14fd1f43b5..5a8222bc2d 100644 --- a/src/mbgl/tile/raster_dem_tile_worker.hpp +++ b/src/mbgl/tile/raster_dem_tile_worker.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -13,7 +14,7 @@ class RasterDEMTileWorker { public: RasterDEMTileWorker(ActorRef, ActorRef); - void parse(std::shared_ptr data, uint64_t correlationID); + void parse(std::shared_ptr data, uint64_t correlationID, Tileset::DEMEncoding encoding); private: ActorRef parent; diff --git a/test/geometry/dem_data.test.cpp b/test/geometry/dem_data.test.cpp index 30091973b2..3848f028f1 100644 --- a/test/geometry/dem_data.test.cpp +++ b/test/geometry/dem_data.test.cpp @@ -1,6 +1,7 @@ #include #include +#include #include using namespace mbgl; @@ -14,30 +15,38 @@ auto fakeImage = [](Size s) { return img; }; -TEST(DEMData, Constructor) { +TEST(DEMData, ConstructorMapbox) { PremultipliedImage image = fakeImage({16, 16}); - DEMData pyramid(image); - - EXPECT_EQ(pyramid.dim, 16); - EXPECT_EQ(pyramid.border, 8); - EXPECT_EQ(pyramid.stride, 32); - EXPECT_EQ(pyramid.getImage()->bytes(), size_t(32*32*4)); - EXPECT_EQ(pyramid.dim, 16); - EXPECT_EQ(pyramid.border, 8); + DEMData demdata(image, Tileset::DEMEncoding::Mapbox); + + EXPECT_EQ(demdata.dim, 16); + EXPECT_EQ(demdata.border, 8); + EXPECT_EQ(demdata.stride, 32); + EXPECT_EQ(demdata.getImage()->bytes(), size_t(32*32*4)); +}; + +TEST(DEMData, ConstructorTerrarium) { + PremultipliedImage image = fakeImage({16, 16}); + DEMData demdata(image, Tileset::DEMEncoding::Terrarium); + + EXPECT_EQ(demdata.dim, 16); + EXPECT_EQ(demdata.border, 8); + EXPECT_EQ(demdata.stride, 32); + EXPECT_EQ(demdata.getImage()->bytes(), size_t(32*32*4)); }; TEST(DEMData, RoundTrip) { PremultipliedImage image = fakeImage({16, 16}); - DEMData pyramid(image); + DEMData demdata(image, Tileset::DEMEncoding::Mapbox); - pyramid.set(4, 6, 255); - EXPECT_EQ(pyramid.get(4, 6), 255); + demdata.set(4, 6, 255); + EXPECT_EQ(demdata.get(4, 6), 255); } TEST(DEMData, InitialBackfill) { PremultipliedImage image1 = fakeImage({4, 4}); - DEMData dem1(image1); + DEMData dem1(image1, Tileset::DEMEncoding::Mapbox); bool nonempty = true; // checking that a 1 px border around the fake image has been populated @@ -91,10 +100,10 @@ TEST(DEMData, InitialBackfill) { TEST(DEMData, BackfillNeighbor) { PremultipliedImage image1 = fakeImage({4, 4}); - DEMData dem0(image1); + DEMData dem0(image1, Tileset::DEMEncoding::Mapbox); PremultipliedImage image2 = fakeImage({4, 4}); - DEMData dem1(image2); + DEMData dem1(image2, Tileset::DEMEncoding::Mapbox); dem0.backfillBorder(dem1, -1, 0); for (int y = 0; y < 4; y++) { diff --git a/test/tile/raster_dem_tile.test.cpp b/test/tile/raster_dem_tile.test.cpp index c6f9b80b42..5a6f5a8c9a 100644 --- a/test/tile/raster_dem_tile.test.cpp +++ b/test/tile/raster_dem_tile.test.cpp @@ -62,7 +62,7 @@ TEST(RasterDEMTile, onError) { TEST(RasterDEMTile, onParsed) { RasterDEMTileTest test; RasterDEMTile tile(OverscaledTileID(0, 0, 0), test.tileParameters, test.tileset); - tile.onParsed(std::make_unique(PremultipliedImage({16, 16})), 0); + tile.onParsed(std::make_unique(PremultipliedImage({16, 16}), Tileset::DEMEncoding::Mapbox), 0); EXPECT_TRUE(tile.isRenderable()); EXPECT_TRUE(tile.isLoaded()); EXPECT_TRUE(tile.isComplete()); -- cgit v1.2.1 From 1fdec7a5c1babc0cd36a110ce372175a5252d45e Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Fri, 9 Feb 2018 13:42:47 -0500 Subject: [core] fix opacity of duplicate labels with -allow-overlap: true --- src/mbgl/text/placement.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/mbgl/text/placement.cpp b/src/mbgl/text/placement.cpp index 400fe79ac5..fc24c78902 100644 --- a/src/mbgl/text/placement.cpp +++ b/src/mbgl/text/placement.cpp @@ -230,6 +230,8 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, std::set& if (bucket.hasCollisionBoxData()) bucket.collisionBox.dynamicVertices.clear(); if (bucket.hasCollisionCircleData()) bucket.collisionCircle.dynamicVertices.clear(); + JointOpacityState duplicateOpacityState(false, false, true); + JointOpacityState defaultOpacityState( bucket.layout.get(), bucket.layout.get(), @@ -239,9 +241,12 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, std::set& bool isDuplicate = seenCrossTileIDs.count(symbolInstance.crossTileID) > 0; auto it = opacities.find(symbolInstance.crossTileID); - auto opacityState = it != opacities.end() && !isDuplicate ? - it->second : - defaultOpacityState; + auto opacityState = defaultOpacityState; + if (isDuplicate) { + opacityState = duplicateOpacityState; + } else if (it != opacities.end()) { + opacityState = it->second; + } if (it == opacities.end()) { opacities.emplace(symbolInstance.crossTileID, defaultOpacityState); -- cgit v1.2.1 From 82de856c94bbc090ba30186011610da5e233e277 Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Thu, 15 Feb 2018 17:38:23 +0200 Subject: [core, ios, macos, android, node] Heatmap layer (#11046) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Konstantin Käfer Co-Authored-By: Anand Thakker Co-Authored-By: Minh Nguyễn <1ec5@users.noreply.github.com> --- cmake/core-files.cmake | 20 + .../conversion/heatmap_color_property_value.hpp | 46 ++ .../mbgl/style/heatmap_color_property_value.hpp | 49 ++ include/mbgl/style/layer.hpp | 3 + include/mbgl/style/layer_type.hpp | 1 + include/mbgl/style/layers/heatmap_layer.hpp | 86 +++ include/mbgl/style/layers/layer.hpp.ejs | 3 + mapbox-gl-js | 2 +- platform/android/CHANGELOG.md | 4 + .../mapboxsdk/style/layers/HeatmapLayer.java | 221 ++++++ .../mapboxsdk/style/layers/PropertyFactory.java | 132 ++++ .../mapboxsdk/testapp/style/HeatmapLayerTest.java | 626 ++++++++++++++++ .../src/main/AndroidManifest.xml | 11 + .../activity/style/HeatmapLayerActivity.java | 216 ++++++ .../src/main/res/layout/activity_heatmaplayer.xml | 14 + .../src/main/res/values/descriptions.xml | 1 + .../src/main/res/values/titles.xml | 1 + platform/android/config.cmake | 2 + platform/android/scripts/generate-style-code.js | 3 + .../android/src/style/layers/heatmap_layer.cpp | 134 ++++ .../android/src/style/layers/heatmap_layer.hpp | 50 ++ platform/android/src/style/layers/layer.cpp | 1 + platform/android/src/style/layers/layers.cpp | 4 + platform/darwin/scripts/generate-style-code.js | 11 +- .../darwin/scripts/style-spec-overrides-v8.json | 10 +- platform/darwin/src/MGLHeatmapStyleLayer.h | 189 +++++ platform/darwin/src/MGLHeatmapStyleLayer.mm | 210 ++++++ platform/darwin/src/MGLStyle.mm | 4 + platform/darwin/src/MGLStyleLayer.mm.ejs | 4 +- platform/darwin/src/MGLStyleValue_Private.h | 36 +- platform/darwin/src/MGLVectorStyleLayer.h | 8 +- .../darwin/test/MGLDocumentationExampleTests.swift | 18 + platform/darwin/test/MGLHeatmapColorTests.mm | 61 ++ platform/darwin/test/MGLHeatmapStyleLayerTests.mm | 296 ++++++++ platform/darwin/test/MGLStyleLayerTests.mm.ejs | 2 + platform/ios/CHANGELOG.md | 1 + platform/ios/docs/guides/For Style Authors.md | 1 + platform/ios/ios.xcodeproj/project.pbxproj | 20 + platform/ios/jazzy.yml | 1 + platform/ios/src/Mapbox.h | 1 + platform/macos/CHANGELOG.md | 1 + .../Layers/heatmap.imageset/Contents.json | 16 + .../Layers/heatmap.imageset/heatmap.pdf | Bin 0 -> 1262 bytes platform/macos/app/StyleLayerIconTransformer.m | 3 + platform/macos/app/heatmap.json | 809 +++++++++++++++++++++ platform/macos/app/resources/heatmap.svg | 72 ++ platform/macos/docs/guides/For Style Authors.md | 1 + platform/macos/jazzy.yml | 1 + platform/macos/macos.xcodeproj/project.pbxproj | 20 + platform/macos/src/Mapbox.h | 1 + platform/node/src/node_map.cpp | 1 + platform/node/test/ignores.json | 18 - scripts/generate-shaders.js | 3 - scripts/generate-style-code.js | 2 + scripts/style-spec.js | 2 - src/mbgl/gl/color_mode.hpp | 4 + src/mbgl/gl/context.cpp | 31 +- src/mbgl/gl/context.hpp | 24 +- src/mbgl/gl/types.hpp | 9 + src/mbgl/programs/attributes.hpp | 7 +- src/mbgl/programs/heatmap_program.cpp | 7 + src/mbgl/programs/heatmap_program.hpp | 49 ++ src/mbgl/programs/heatmap_texture_program.cpp | 7 + src/mbgl/programs/heatmap_texture_program.hpp | 43 ++ src/mbgl/programs/programs.hpp | 6 + src/mbgl/programs/uniforms.hpp | 5 + src/mbgl/renderer/buckets/heatmap_bucket.cpp | 98 +++ src/mbgl/renderer/buckets/heatmap_bucket.hpp | 40 + src/mbgl/renderer/layers/render_heatmap_layer.cpp | 178 +++++ src/mbgl/renderer/layers/render_heatmap_layer.hpp | 48 ++ src/mbgl/renderer/render_layer.cpp | 3 + src/mbgl/renderer/renderer_impl.cpp | 11 +- src/mbgl/shaders/heatmap.cpp | 128 ++++ src/mbgl/shaders/heatmap.hpp | 16 + src/mbgl/shaders/heatmap_texture.cpp | 42 ++ src/mbgl/shaders/heatmap_texture.hpp | 16 + src/mbgl/style/conversion/layer.cpp | 3 + .../style/conversion/make_property_setters.hpp | 13 + src/mbgl/style/conversion/property_setter.hpp | 1 + src/mbgl/style/layers/heatmap_layer.cpp | 239 ++++++ src/mbgl/style/layers/heatmap_layer_impl.cpp | 15 + src/mbgl/style/layers/heatmap_layer_impl.hpp | 21 + src/mbgl/style/layers/heatmap_layer_properties.cpp | 9 + src/mbgl/style/layers/heatmap_layer_properties.hpp | 40 + src/mbgl/style/layers/layer.cpp.ejs | 12 + src/mbgl/style/layers/layer_properties.hpp.ejs | 1 + src/mbgl/style/paint_property.hpp | 23 + src/mbgl/style/style_impl.cpp | 1 + src/mbgl/util/offscreen_texture.cpp | 22 +- src/mbgl/util/offscreen_texture.hpp | 6 +- 90 files changed, 4570 insertions(+), 61 deletions(-) create mode 100644 include/mbgl/style/conversion/heatmap_color_property_value.hpp create mode 100644 include/mbgl/style/heatmap_color_property_value.hpp create mode 100644 include/mbgl/style/layers/heatmap_layer.hpp create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HeatmapLayer.java create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/HeatmapLayerTest.java create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/HeatmapLayerActivity.java create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_heatmaplayer.xml create mode 100644 platform/android/src/style/layers/heatmap_layer.cpp create mode 100644 platform/android/src/style/layers/heatmap_layer.hpp create mode 100644 platform/darwin/src/MGLHeatmapStyleLayer.h create mode 100644 platform/darwin/src/MGLHeatmapStyleLayer.mm create mode 100644 platform/darwin/test/MGLHeatmapColorTests.mm create mode 100644 platform/darwin/test/MGLHeatmapStyleLayerTests.mm create mode 100644 platform/macos/app/Assets.xcassets/Layers/heatmap.imageset/Contents.json create mode 100644 platform/macos/app/Assets.xcassets/Layers/heatmap.imageset/heatmap.pdf create mode 100644 platform/macos/app/heatmap.json create mode 100644 platform/macos/app/resources/heatmap.svg create mode 100644 src/mbgl/programs/heatmap_program.cpp create mode 100644 src/mbgl/programs/heatmap_program.hpp create mode 100644 src/mbgl/programs/heatmap_texture_program.cpp create mode 100644 src/mbgl/programs/heatmap_texture_program.hpp create mode 100644 src/mbgl/renderer/buckets/heatmap_bucket.cpp create mode 100644 src/mbgl/renderer/buckets/heatmap_bucket.hpp create mode 100644 src/mbgl/renderer/layers/render_heatmap_layer.cpp create mode 100644 src/mbgl/renderer/layers/render_heatmap_layer.hpp create mode 100644 src/mbgl/shaders/heatmap.cpp create mode 100644 src/mbgl/shaders/heatmap.hpp create mode 100644 src/mbgl/shaders/heatmap_texture.cpp create mode 100644 src/mbgl/shaders/heatmap_texture.hpp create mode 100644 src/mbgl/style/layers/heatmap_layer.cpp create mode 100644 src/mbgl/style/layers/heatmap_layer_impl.cpp create mode 100644 src/mbgl/style/layers/heatmap_layer_impl.hpp create mode 100644 src/mbgl/style/layers/heatmap_layer_properties.cpp create mode 100644 src/mbgl/style/layers/heatmap_layer_properties.hpp diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake index a0f75a6a59..f24482e301 100644 --- a/cmake/core-files.cmake +++ b/cmake/core-files.cmake @@ -146,6 +146,10 @@ set(MBGL_CORE_FILES src/mbgl/programs/fill_extrusion_program.hpp src/mbgl/programs/fill_program.cpp src/mbgl/programs/fill_program.hpp + src/mbgl/programs/heatmap_program.cpp + src/mbgl/programs/heatmap_program.hpp + src/mbgl/programs/heatmap_texture_program.cpp + src/mbgl/programs/heatmap_texture_program.hpp src/mbgl/programs/hillshade_prepare_program.cpp src/mbgl/programs/hillshade_prepare_program.hpp src/mbgl/programs/hillshade_program.cpp @@ -225,6 +229,8 @@ set(MBGL_CORE_FILES src/mbgl/renderer/buckets/fill_bucket.hpp src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp + src/mbgl/renderer/buckets/heatmap_bucket.cpp + src/mbgl/renderer/buckets/heatmap_bucket.hpp src/mbgl/renderer/buckets/hillshade_bucket.cpp src/mbgl/renderer/buckets/hillshade_bucket.hpp src/mbgl/renderer/buckets/line_bucket.cpp @@ -245,6 +251,8 @@ set(MBGL_CORE_FILES src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp src/mbgl/renderer/layers/render_fill_layer.cpp src/mbgl/renderer/layers/render_fill_layer.hpp + src/mbgl/renderer/layers/render_heatmap_layer.cpp + src/mbgl/renderer/layers/render_heatmap_layer.hpp src/mbgl/renderer/layers/render_hillshade_layer.cpp src/mbgl/renderer/layers/render_hillshade_layer.hpp src/mbgl/renderer/layers/render_line_layer.cpp @@ -297,6 +305,10 @@ set(MBGL_CORE_FILES src/mbgl/shaders/fill_outline_pattern.hpp src/mbgl/shaders/fill_pattern.cpp src/mbgl/shaders/fill_pattern.hpp + src/mbgl/shaders/heatmap.cpp + src/mbgl/shaders/heatmap.hpp + src/mbgl/shaders/heatmap_texture.cpp + src/mbgl/shaders/heatmap_texture.hpp src/mbgl/shaders/hillshade.cpp src/mbgl/shaders/hillshade.hpp src/mbgl/shaders/hillshade_prepare.cpp @@ -349,6 +361,7 @@ set(MBGL_CORE_FILES include/mbgl/style/data_driven_property_value.hpp include/mbgl/style/filter.hpp include/mbgl/style/filter_evaluator.hpp + include/mbgl/style/heatmap_color_property_value.hpp include/mbgl/style/image.hpp include/mbgl/style/layer.hpp include/mbgl/style/layer_type.hpp @@ -401,6 +414,7 @@ set(MBGL_CORE_FILES include/mbgl/style/conversion/geojson.hpp include/mbgl/style/conversion/geojson_options.hpp include/mbgl/style/conversion/get_json_type.hpp + include/mbgl/style/conversion/heatmap_color_property_value.hpp include/mbgl/style/conversion/layer.hpp include/mbgl/style/conversion/light.hpp include/mbgl/style/conversion/position.hpp @@ -495,6 +509,7 @@ set(MBGL_CORE_FILES include/mbgl/style/layers/custom_layer.hpp include/mbgl/style/layers/fill_extrusion_layer.hpp include/mbgl/style/layers/fill_layer.hpp + include/mbgl/style/layers/heatmap_layer.hpp include/mbgl/style/layers/hillshade_layer.hpp include/mbgl/style/layers/line_layer.hpp include/mbgl/style/layers/raster_layer.hpp @@ -522,6 +537,11 @@ set(MBGL_CORE_FILES src/mbgl/style/layers/fill_layer_impl.hpp src/mbgl/style/layers/fill_layer_properties.cpp src/mbgl/style/layers/fill_layer_properties.hpp + src/mbgl/style/layers/heatmap_layer.cpp + src/mbgl/style/layers/heatmap_layer_impl.cpp + src/mbgl/style/layers/heatmap_layer_impl.hpp + src/mbgl/style/layers/heatmap_layer_properties.cpp + src/mbgl/style/layers/heatmap_layer_properties.hpp src/mbgl/style/layers/hillshade_layer.cpp src/mbgl/style/layers/hillshade_layer_impl.cpp src/mbgl/style/layers/hillshade_layer_impl.hpp diff --git a/include/mbgl/style/conversion/heatmap_color_property_value.hpp b/include/mbgl/style/conversion/heatmap_color_property_value.hpp new file mode 100644 index 0000000000..e3689c524c --- /dev/null +++ b/include/mbgl/style/conversion/heatmap_color_property_value.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mbgl { +namespace style { +namespace conversion { + +template <> +struct Converter { + optional operator()(const Convertible& value, Error& error) const { + if (isUndefined(value)) { + return HeatmapColorPropertyValue(); + } else if (isExpression(value)) { + optional> expression = convert>(value, error, expression::type::Color); + if (!expression) { + return {}; + } + assert(*expression); + if (!isFeatureConstant(**expression)) { + error = { "property expressions not supported" }; + return {}; + } + if (!isZoomConstant(**expression)) { + error = { "zoom expressions not supported" }; + return {}; + } + return {HeatmapColorPropertyValue(std::move(*expression))}; + } else { + error = { "heatmap-color must be an expression" }; + return {}; + } + } +}; + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/heatmap_color_property_value.hpp b/include/mbgl/style/heatmap_color_property_value.hpp new file mode 100644 index 0000000000..130639c6e2 --- /dev/null +++ b/include/mbgl/style/heatmap_color_property_value.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include +#include +#include + +namespace mbgl { +namespace style { + +/* + * Special-case implementation of (a subset of) the PropertyValue interface + * used for building the HeatmapColor paint property traits class. + */ +class HeatmapColorPropertyValue { +private: + std::shared_ptr value; + + friend bool operator==(const HeatmapColorPropertyValue& lhs, const HeatmapColorPropertyValue& rhs) { + return (lhs.isUndefined() && rhs.isUndefined()) || (lhs.value && rhs.value && *(lhs.value) == *(rhs.value)); + } + + friend bool operator!=(const HeatmapColorPropertyValue& lhs, const HeatmapColorPropertyValue& rhs) { + return !(lhs == rhs); + } + +public: + HeatmapColorPropertyValue() : value(nullptr) {} + HeatmapColorPropertyValue(std::shared_ptr value_) : value(std::move(value_)) {} + + bool isUndefined() const { return value.get() == nullptr; } + + // noop, needed for batch evaluation of paint property values to compile + template + Color evaluate(const Evaluator&, TimePoint = {}) const { return {}; } + + Color evaluate(double heatmapDensity) const { + const auto result = value->evaluate(expression::EvaluationContext({}, nullptr, {heatmapDensity})); + return *expression::fromExpressionValue(*result); + } + + bool isDataDriven() const { return false; } + bool hasDataDrivenPropertyDifference(const HeatmapColorPropertyValue&) const { return false; } + + const expression::Expression& getExpression() const { return *value; } +}; + + +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/layer.hpp b/include/mbgl/style/layer.hpp index 8a5a4236b3..12494f5387 100644 --- a/include/mbgl/style/layer.hpp +++ b/include/mbgl/style/layer.hpp @@ -23,6 +23,7 @@ class HillshadeLayer; class BackgroundLayer; class CustomLayer; class FillExtrusionLayer; +class HeatmapLayer; class LayerObserver; /** @@ -93,6 +94,8 @@ public: return std::forward(visitor)(*as()); case LayerType::FillExtrusion: return std::forward(visitor)(*as()); + case LayerType::Heatmap: + return std::forward(visitor)(*as()); } diff --git a/include/mbgl/style/layer_type.hpp b/include/mbgl/style/layer_type.hpp index 951757134b..0987ea4d0a 100644 --- a/include/mbgl/style/layer_type.hpp +++ b/include/mbgl/style/layer_type.hpp @@ -13,6 +13,7 @@ enum class LayerType { Background, Custom, FillExtrusion, + Heatmap, }; } // namespace style diff --git a/include/mbgl/style/layers/heatmap_layer.hpp b/include/mbgl/style/layers/heatmap_layer.hpp new file mode 100644 index 0000000000..33d927ad38 --- /dev/null +++ b/include/mbgl/style/layers/heatmap_layer.hpp @@ -0,0 +1,86 @@ +// This file is generated. Do not edit. + +#pragma once + +#include +#include +#include +#include +#include + +#include + +namespace mbgl { +namespace style { + +class TransitionOptions; + +class HeatmapLayer : public Layer { +public: + HeatmapLayer(const std::string& layerID, const std::string& sourceID); + ~HeatmapLayer() final; + + // Source + const std::string& getSourceID() const; + const std::string& getSourceLayer() const; + void setSourceLayer(const std::string& sourceLayer); + + void setFilter(const Filter&); + const Filter& getFilter() const; + + // Visibility + void setVisibility(VisibilityType) final; + + // Zoom range + void setMinZoom(float) final; + void setMaxZoom(float) final; + + // Paint properties + + static DataDrivenPropertyValue getDefaultHeatmapRadius(); + DataDrivenPropertyValue getHeatmapRadius() const; + void setHeatmapRadius(DataDrivenPropertyValue); + void setHeatmapRadiusTransition(const TransitionOptions&); + TransitionOptions getHeatmapRadiusTransition() const; + + static DataDrivenPropertyValue getDefaultHeatmapWeight(); + DataDrivenPropertyValue getHeatmapWeight() const; + void setHeatmapWeight(DataDrivenPropertyValue); + void setHeatmapWeightTransition(const TransitionOptions&); + TransitionOptions getHeatmapWeightTransition() const; + + static PropertyValue getDefaultHeatmapIntensity(); + PropertyValue getHeatmapIntensity() const; + void setHeatmapIntensity(PropertyValue); + void setHeatmapIntensityTransition(const TransitionOptions&); + TransitionOptions getHeatmapIntensityTransition() const; + + static HeatmapColorPropertyValue getDefaultHeatmapColor(); + HeatmapColorPropertyValue getHeatmapColor() const; + void setHeatmapColor(HeatmapColorPropertyValue); + void setHeatmapColorTransition(const TransitionOptions&); + TransitionOptions getHeatmapColorTransition() const; + + static PropertyValue getDefaultHeatmapOpacity(); + PropertyValue getHeatmapOpacity() const; + void setHeatmapOpacity(PropertyValue); + void setHeatmapOpacityTransition(const TransitionOptions&); + TransitionOptions getHeatmapOpacityTransition() const; + + // Private implementation + + class Impl; + const Impl& impl() const; + + Mutable mutableImpl() const; + HeatmapLayer(Immutable); + std::unique_ptr cloneRef(const std::string& id) const final; +}; + +template <> +inline bool Layer::is() const { + return getType() == LayerType::Heatmap; +} + +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/layers/layer.hpp.ejs b/include/mbgl/style/layers/layer.hpp.ejs index 265dd57e1f..6d40405ccd 100644 --- a/include/mbgl/style/layers/layer.hpp.ejs +++ b/include/mbgl/style/layers/layer.hpp.ejs @@ -11,6 +11,9 @@ #include #include #include +<% if (type === 'heatmap') { -%> +#include +<% } -%> #include diff --git a/mapbox-gl-js b/mapbox-gl-js index f13c86ea35..d61850a6bd 160000 --- a/mapbox-gl-js +++ b/mapbox-gl-js @@ -1 +1 @@ -Subproject commit f13c86ea356c384fdab31855b9152f5bf5ef97b8 +Subproject commit d61850a6bd84ce6d697be392c7ef6d4b5555c20e diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index afac6d4902..fa9b1d29cc 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -2,6 +2,10 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started. +## master + + - HeatmapLayer [#11046](https://github.com/mapbox/mapbox-gl-native/pull/11046) + ## 6.0.0-beta.2 - February 13, 2018 - Deprecate LocationEngine [#11185](https://github.com/mapbox/mapbox-gl-native/pull/11185) - Remove LOST from SDK [11186](https://github.com/mapbox/mapbox-gl-native/pull/11186) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HeatmapLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HeatmapLayer.java new file mode 100644 index 0000000000..6b8fd65def --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HeatmapLayer.java @@ -0,0 +1,221 @@ +// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`. + +package com.mapbox.mapboxsdk.style.layers; + +import android.support.annotation.ColorInt; +import android.support.annotation.NonNull; +import android.support.annotation.UiThread; + +import static com.mapbox.mapboxsdk.utils.ColorUtils.rgbaToColor; + +import com.mapbox.mapboxsdk.style.layers.TransitionOptions; + +/** + * A heatmap. + * + * @see The online documentation + */ +@UiThread +public class HeatmapLayer extends Layer { + + /** + * Creates a HeatmapLayer. + * + * @param nativePtr pointer used by core + */ + public HeatmapLayer(long nativePtr) { + super(nativePtr); + } + + /** + * Creates a HeatmapLayer. + * + * @param layerId the id of the layer + * @param sourceId the id of the source + */ + public HeatmapLayer(String layerId, String sourceId) { + initialize(layerId, sourceId); + } + + protected native void initialize(String layerId, String sourceId); + + /** + * Set the source layer. + * + * @param sourceLayer the source layer to set + */ + public void setSourceLayer(String sourceLayer) { + nativeSetSourceLayer(sourceLayer); + } + + /** + * Set the source Layer. + * + * @param sourceLayer the source layer to set + * @return This + */ + public HeatmapLayer withSourceLayer(String sourceLayer) { + setSourceLayer(sourceLayer); + return this; + } + + /** + * Get the source layer. + * + * @return sourceLayer the source layer to get + */ + public String getSourceLayer() { + return nativeGetSourceLayer(); + } + + /** + * Set a single filter. + * + * @param filter the filter to set + */ + public void setFilter(Filter.Statement filter) { + nativeSetFilter(filter.toArray()); + } + + /** + * Set a single filter. + * + * @param filter the filter to set + * @return This + */ + public HeatmapLayer withFilter(Filter.Statement filter) { + setFilter(filter); + return this; + } + + /** + * Set a property or properties. + * + * @param properties the var-args properties + * @return This + */ + public HeatmapLayer withProperties(@NonNull PropertyValue... properties) { + setProperties(properties); + return this; + } + + // Property getters + + /** + * Get the HeatmapRadius property + * + * @return property wrapper value around Float + */ + @SuppressWarnings("unchecked") + public PropertyValue getHeatmapRadius() { + return (PropertyValue) new PropertyValue("heatmap-radius", nativeGetHeatmapRadius()); + } + + /** + * Get the HeatmapRadius property transition options + * + * @return transition options for Float + */ + public TransitionOptions getHeatmapRadiusTransition() { + return nativeGetHeatmapRadiusTransition(); + } + + /** + * Set the HeatmapRadius property transition options + * + * @param options transition options for Float + */ + public void setHeatmapRadiusTransition(TransitionOptions options) { + nativeSetHeatmapRadiusTransition(options.getDuration(), options.getDelay()); + } + + /** + * Get the HeatmapWeight property + * + * @return property wrapper value around Float + */ + @SuppressWarnings("unchecked") + public PropertyValue getHeatmapWeight() { + return (PropertyValue) new PropertyValue("heatmap-weight", nativeGetHeatmapWeight()); + } + + /** + * Get the HeatmapIntensity property + * + * @return property wrapper value around Float + */ + @SuppressWarnings("unchecked") + public PropertyValue getHeatmapIntensity() { + return (PropertyValue) new PropertyValue("heatmap-intensity", nativeGetHeatmapIntensity()); + } + + /** + * Get the HeatmapIntensity property transition options + * + * @return transition options for Float + */ + public TransitionOptions getHeatmapIntensityTransition() { + return nativeGetHeatmapIntensityTransition(); + } + + /** + * Set the HeatmapIntensity property transition options + * + * @param options transition options for Float + */ + public void setHeatmapIntensityTransition(TransitionOptions options) { + nativeSetHeatmapIntensityTransition(options.getDuration(), options.getDelay()); + } + + /** + * Get the HeatmapOpacity property + * + * @return property wrapper value around Float + */ + @SuppressWarnings("unchecked") + public PropertyValue getHeatmapOpacity() { + return (PropertyValue) new PropertyValue("heatmap-opacity", nativeGetHeatmapOpacity()); + } + + /** + * Get the HeatmapOpacity property transition options + * + * @return transition options for Float + */ + public TransitionOptions getHeatmapOpacityTransition() { + return nativeGetHeatmapOpacityTransition(); + } + + /** + * Set the HeatmapOpacity property transition options + * + * @param options transition options for Float + */ + public void setHeatmapOpacityTransition(TransitionOptions options) { + nativeSetHeatmapOpacityTransition(options.getDuration(), options.getDelay()); + } + + private native Object nativeGetHeatmapRadius(); + + private native TransitionOptions nativeGetHeatmapRadiusTransition(); + + private native void nativeSetHeatmapRadiusTransition(long duration, long delay); + + private native Object nativeGetHeatmapWeight(); + + private native Object nativeGetHeatmapIntensity(); + + private native TransitionOptions nativeGetHeatmapIntensityTransition(); + + private native void nativeSetHeatmapIntensityTransition(long duration, long delay); + + private native Object nativeGetHeatmapOpacity(); + + private native TransitionOptions nativeGetHeatmapOpacityTransition(); + + private native void nativeSetHeatmapOpacityTransition(long duration, long delay); + + @Override + protected native void finalize() throws Throwable; + +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java index 6e644c5591..18ee05e63b 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java @@ -1515,6 +1515,138 @@ public class PropertyFactory { return new PaintPropertyValue<>("circle-stroke-opacity", function); } + /** + * Radius of influence of one heatmap point in density-independent pixels. Increasing the value makes the heatmap smoother, but less detailed. + * + * @param value a Float value + * @return property wrapper around Float + */ + public static PropertyValue heatmapRadius(Float value) { + return new PaintPropertyValue<>("heatmap-radius", value); + } + + /** + * Radius of influence of one heatmap point in density-independent pixels. Increasing the value makes the heatmap smoother, but less detailed. + * + * @param expression an expression statement + * @return property wrapper around an expression statement + */ + public static PropertyValue heatmapRadius(Expression expression) { + return new PaintPropertyValue<>("heatmap-radius", expression); + } + + + /** + * Radius of influence of one heatmap point in density-independent pixels. Increasing the value makes the heatmap smoother, but less detailed. + * + * @param the function input type + * @param function a wrapper function for Float + * @return property wrapper around a Float function + */ + @Deprecated + public static PropertyValue> heatmapRadius(Function function) { + return new PaintPropertyValue<>("heatmap-radius", function); + } + + /** + * A measure of how much an individual point contributes to the heatmap. A value of 10 would be equivalent to having 10 points of weight 1 in the same spot. Especially useful when combined with clustering. + * + * @param value a Float value + * @return property wrapper around Float + */ + public static PropertyValue heatmapWeight(Float value) { + return new PaintPropertyValue<>("heatmap-weight", value); + } + + /** + * A measure of how much an individual point contributes to the heatmap. A value of 10 would be equivalent to having 10 points of weight 1 in the same spot. Especially useful when combined with clustering. + * + * @param expression an expression statement + * @return property wrapper around an expression statement + */ + public static PropertyValue heatmapWeight(Expression expression) { + return new PaintPropertyValue<>("heatmap-weight", expression); + } + + + /** + * A measure of how much an individual point contributes to the heatmap. A value of 10 would be equivalent to having 10 points of weight 1 in the same spot. Especially useful when combined with clustering. + * + * @param the function input type + * @param function a wrapper function for Float + * @return property wrapper around a Float function + */ + @Deprecated + public static PropertyValue> heatmapWeight(Function function) { + return new PaintPropertyValue<>("heatmap-weight", function); + } + + /** + * Similar to {@link PropertyFactory#heatmapWeight} but controls the intensity of the heatmap globally. Primarily used for adjusting the heatmap based on zoom level. + * + * @param value a Float value + * @return property wrapper around Float + */ + public static PropertyValue heatmapIntensity(Float value) { + return new PaintPropertyValue<>("heatmap-intensity", value); + } + + /** + * Similar to {@link PropertyFactory#heatmapWeight} but controls the intensity of the heatmap globally. Primarily used for adjusting the heatmap based on zoom level. + * + * @param expression an expression statement + * @return property wrapper around an expression statement + */ + public static PropertyValue heatmapIntensity(Expression expression) { + return new PaintPropertyValue<>("heatmap-intensity", expression); + } + + + /** + * Similar to {@link PropertyFactory#heatmapWeight} but controls the intensity of the heatmap globally. Primarily used for adjusting the heatmap based on zoom level. + * + * @param the zoom parameter type + * @param function a wrapper {@link CameraFunction} for Float + * @return property wrapper around a Float function + */ + @Deprecated + public static PropertyValue> heatmapIntensity(CameraFunction function) { + return new PaintPropertyValue<>("heatmap-intensity", function); + } + + /** + * The global opacity at which the heatmap layer will be drawn. + * + * @param value a Float value + * @return property wrapper around Float + */ + public static PropertyValue heatmapOpacity(Float value) { + return new PaintPropertyValue<>("heatmap-opacity", value); + } + + /** + * The global opacity at which the heatmap layer will be drawn. + * + * @param expression an expression statement + * @return property wrapper around an expression statement + */ + public static PropertyValue heatmapOpacity(Expression expression) { + return new PaintPropertyValue<>("heatmap-opacity", expression); + } + + + /** + * The global opacity at which the heatmap layer will be drawn. + * + * @param the zoom parameter type + * @param function a wrapper {@link CameraFunction} for Float + * @return property wrapper around a Float function + */ + @Deprecated + public static PropertyValue> heatmapOpacity(CameraFunction function) { + return new PaintPropertyValue<>("heatmap-opacity", function); + } + /** * The opacity of the entire fill extrusion layer. This is rendered on a per-layer, not per-feature, basis, and data-driven styling is not available. * diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/HeatmapLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/HeatmapLayerTest.java new file mode 100644 index 0000000000..364c8d2679 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/HeatmapLayerTest.java @@ -0,0 +1,626 @@ +// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`. + +package com.mapbox.mapboxsdk.testapp.style; + +import android.graphics.Color; +import android.support.test.espresso.UiController; +import android.support.test.runner.AndroidJUnit4; + +import timber.log.Timber; + +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.style.functions.CompositeFunction; +import com.mapbox.mapboxsdk.style.functions.CameraFunction; +import com.mapbox.mapboxsdk.style.functions.SourceFunction; +import com.mapbox.mapboxsdk.style.functions.stops.CategoricalStops; +import com.mapbox.mapboxsdk.style.functions.stops.ExponentialStops; +import com.mapbox.mapboxsdk.style.functions.stops.IdentityStops; +import com.mapbox.mapboxsdk.style.functions.stops.IntervalStops; +import com.mapbox.mapboxsdk.style.functions.stops.Stop; +import com.mapbox.mapboxsdk.style.functions.stops.Stops; +import com.mapbox.mapboxsdk.style.layers.HeatmapLayer; +import com.mapbox.mapboxsdk.testapp.action.MapboxMapAction; +import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static com.mapbox.mapboxsdk.style.functions.Function.*; +import static com.mapbox.mapboxsdk.style.functions.stops.Stop.stop; +import static com.mapbox.mapboxsdk.style.functions.stops.Stops.*; +import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke; +import static org.junit.Assert.*; +import static com.mapbox.mapboxsdk.style.layers.Property.*; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.*; + +import com.mapbox.mapboxsdk.style.layers.TransitionOptions; +import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity; + +/** + * Basic smoke tests for HeatmapLayer + */ +@RunWith(AndroidJUnit4.class) +public class HeatmapLayerTest extends BaseActivityTest { + + private HeatmapLayer layer; + + @Override + protected Class getActivityClass() { + return EspressoTestActivity.class; + } + + private void setupLayer() { + Timber.i("Retrieving layer"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + if ((layer = mapboxMap.getLayerAs("my-layer")) == null) { + Timber.i("Adding layer"); + layer = new HeatmapLayer("my-layer", "composite"); + layer.setSourceLayer("composite"); + mapboxMap.addLayer(layer); + // Layer reference is now stale, get new reference + layer = mapboxMap.getLayerAs("my-layer"); + } + } + }); + } + + @Test + public void testSetVisibility() { + validateTestSetup(); + setupLayer(); + Timber.i("Visibility"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Get initial + assertEquals(layer.getVisibility().getValue(), VISIBLE); + + // Set + layer.setProperties(visibility(NONE)); + assertEquals(layer.getVisibility().getValue(), NONE); + } + }); + } + + @Test + public void testSourceLayer() { + validateTestSetup(); + setupLayer(); + Timber.i("SourceLayer"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Get initial + assertEquals(layer.getSourceLayer(), "composite"); + + // Set + final String sourceLayer = "test"; + layer.setSourceLayer(sourceLayer); + assertEquals(layer.getSourceLayer(), sourceLayer); + } + }); + } + + @Test + public void testHeatmapRadiusTransition() { + validateTestSetup(); + setupLayer(); + Timber.i("heatmap-radiusTransitionOptions"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set and Get + TransitionOptions options = new TransitionOptions(300, 100); + layer.setHeatmapRadiusTransition(options); + assertEquals(layer.getHeatmapRadiusTransition(), options); + } + }); + } + + @Test + public void testHeatmapRadiusAsConstant() { + validateTestSetup(); + setupLayer(); + Timber.i("heatmap-radius"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set and Get + layer.setProperties(heatmapRadius(0.3f)); + assertEquals((Float) layer.getHeatmapRadius().getValue(), (Float) 0.3f); + } + }); + } + + @Test + public void testHeatmapRadiusAsCameraFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("heatmap-radius"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + heatmapRadius( + zoom( + exponential( + stop(2, heatmapRadius(0.3f)) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getHeatmapRadius()); + assertNotNull(layer.getHeatmapRadius().getFunction()); + assertEquals(CameraFunction.class, layer.getHeatmapRadius().getFunction().getClass()); + assertEquals(ExponentialStops.class, layer.getHeatmapRadius().getFunction().getStops().getClass()); + assertEquals(0.5f, ((ExponentialStops) layer.getHeatmapRadius().getFunction().getStops()).getBase(), 0.001); + assertEquals(1, ((ExponentialStops) layer.getHeatmapRadius().getFunction().getStops()).size()); + } + }); + } + + @Test + public void testHeatmapRadiusAsIdentitySourceFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("heatmap-radius"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + heatmapRadius(property("FeaturePropertyA", Stops.identity())) + ); + + // Verify + assertNotNull(layer.getHeatmapRadius()); + assertNotNull(layer.getHeatmapRadius().getFunction()); + assertEquals(SourceFunction.class, layer.getHeatmapRadius().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getHeatmapRadius().getFunction()).getProperty()); + assertEquals(IdentityStops.class, layer.getHeatmapRadius().getFunction().getStops().getClass()); + } + }); + } + + @Test + public void testHeatmapRadiusAsExponentialSourceFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("heatmap-radius"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + heatmapRadius( + property( + "FeaturePropertyA", + exponential( + stop(0.3f, heatmapRadius(0.3f)) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getHeatmapRadius()); + assertNotNull(layer.getHeatmapRadius().getFunction()); + assertEquals(SourceFunction.class, layer.getHeatmapRadius().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getHeatmapRadius().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getHeatmapRadius().getFunction().getStops().getClass()); + } + }); + } + + @Test + public void testHeatmapRadiusAsCategoricalSourceFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("heatmap-radius"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + heatmapRadius( + property( + "FeaturePropertyA", + categorical( + stop(1.0f, heatmapRadius(0.3f)) + ) + ).withDefaultValue(heatmapRadius(0.3f)) + ) + ); + + // Verify + assertNotNull(layer.getHeatmapRadius()); + assertNotNull(layer.getHeatmapRadius().getFunction()); + assertEquals(SourceFunction.class, layer.getHeatmapRadius().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getHeatmapRadius().getFunction()).getProperty()); + assertEquals(CategoricalStops.class, layer.getHeatmapRadius().getFunction().getStops().getClass()); + assertNotNull(((SourceFunction) layer.getHeatmapRadius().getFunction()).getDefaultValue()); + assertNotNull(((SourceFunction) layer.getHeatmapRadius().getFunction()).getDefaultValue().getValue()); + assertEquals(0.3f, ((SourceFunction) layer.getHeatmapRadius().getFunction()).getDefaultValue().getValue()); + } + }); + + } + + @Test + public void testHeatmapRadiusAsCompositeFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("heatmap-radius"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + heatmapRadius( + composite( + "FeaturePropertyA", + exponential( + stop(0, 0.3f, heatmapRadius(0.9f)) + ).withBase(0.5f) + ).withDefaultValue(heatmapRadius(0.3f)) + ) + ); + + // Verify + assertNotNull(layer.getHeatmapRadius()); + assertNotNull(layer.getHeatmapRadius().getFunction()); + assertEquals(CompositeFunction.class, layer.getHeatmapRadius().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getHeatmapRadius().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getHeatmapRadius().getFunction().getStops().getClass()); + assertEquals(1, ((ExponentialStops) layer.getHeatmapRadius().getFunction().getStops()).size()); + + ExponentialStops, Float> stops = + (ExponentialStops, Float>) layer.getHeatmapRadius().getFunction().getStops(); + Stop, Float> stop = stops.iterator().next(); + assertEquals(0f, stop.in.zoom, 0.001); + assertEquals(0.3f, stop.in.value, 0.001f); + assertEquals(0.9f, stop.out, 0.001f); + } + }); + } + + @Test + public void testHeatmapWeightAsConstant() { + validateTestSetup(); + setupLayer(); + Timber.i("heatmap-weight"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set and Get + layer.setProperties(heatmapWeight(0.3f)); + assertEquals((Float) layer.getHeatmapWeight().getValue(), (Float) 0.3f); + } + }); + } + + @Test + public void testHeatmapWeightAsCameraFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("heatmap-weight"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + heatmapWeight( + zoom( + exponential( + stop(2, heatmapWeight(0.3f)) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getHeatmapWeight()); + assertNotNull(layer.getHeatmapWeight().getFunction()); + assertEquals(CameraFunction.class, layer.getHeatmapWeight().getFunction().getClass()); + assertEquals(ExponentialStops.class, layer.getHeatmapWeight().getFunction().getStops().getClass()); + assertEquals(0.5f, ((ExponentialStops) layer.getHeatmapWeight().getFunction().getStops()).getBase(), 0.001); + assertEquals(1, ((ExponentialStops) layer.getHeatmapWeight().getFunction().getStops()).size()); + } + }); + } + + @Test + public void testHeatmapWeightAsIdentitySourceFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("heatmap-weight"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + heatmapWeight(property("FeaturePropertyA", Stops.identity())) + ); + + // Verify + assertNotNull(layer.getHeatmapWeight()); + assertNotNull(layer.getHeatmapWeight().getFunction()); + assertEquals(SourceFunction.class, layer.getHeatmapWeight().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getHeatmapWeight().getFunction()).getProperty()); + assertEquals(IdentityStops.class, layer.getHeatmapWeight().getFunction().getStops().getClass()); + } + }); + } + + @Test + public void testHeatmapWeightAsExponentialSourceFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("heatmap-weight"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + heatmapWeight( + property( + "FeaturePropertyA", + exponential( + stop(0.3f, heatmapWeight(0.3f)) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getHeatmapWeight()); + assertNotNull(layer.getHeatmapWeight().getFunction()); + assertEquals(SourceFunction.class, layer.getHeatmapWeight().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getHeatmapWeight().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getHeatmapWeight().getFunction().getStops().getClass()); + } + }); + } + + @Test + public void testHeatmapWeightAsCategoricalSourceFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("heatmap-weight"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + heatmapWeight( + property( + "FeaturePropertyA", + categorical( + stop(1.0f, heatmapWeight(0.3f)) + ) + ).withDefaultValue(heatmapWeight(0.3f)) + ) + ); + + // Verify + assertNotNull(layer.getHeatmapWeight()); + assertNotNull(layer.getHeatmapWeight().getFunction()); + assertEquals(SourceFunction.class, layer.getHeatmapWeight().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getHeatmapWeight().getFunction()).getProperty()); + assertEquals(CategoricalStops.class, layer.getHeatmapWeight().getFunction().getStops().getClass()); + assertNotNull(((SourceFunction) layer.getHeatmapWeight().getFunction()).getDefaultValue()); + assertNotNull(((SourceFunction) layer.getHeatmapWeight().getFunction()).getDefaultValue().getValue()); + assertEquals(0.3f, ((SourceFunction) layer.getHeatmapWeight().getFunction()).getDefaultValue().getValue()); + } + }); + + } + + @Test + public void testHeatmapWeightAsCompositeFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("heatmap-weight"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + heatmapWeight( + composite( + "FeaturePropertyA", + exponential( + stop(0, 0.3f, heatmapWeight(0.9f)) + ).withBase(0.5f) + ).withDefaultValue(heatmapWeight(0.3f)) + ) + ); + + // Verify + assertNotNull(layer.getHeatmapWeight()); + assertNotNull(layer.getHeatmapWeight().getFunction()); + assertEquals(CompositeFunction.class, layer.getHeatmapWeight().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getHeatmapWeight().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getHeatmapWeight().getFunction().getStops().getClass()); + assertEquals(1, ((ExponentialStops) layer.getHeatmapWeight().getFunction().getStops()).size()); + + ExponentialStops, Float> stops = + (ExponentialStops, Float>) layer.getHeatmapWeight().getFunction().getStops(); + Stop, Float> stop = stops.iterator().next(); + assertEquals(0f, stop.in.zoom, 0.001); + assertEquals(0.3f, stop.in.value, 0.001f); + assertEquals(0.9f, stop.out, 0.001f); + } + }); + } + + @Test + public void testHeatmapIntensityTransition() { + validateTestSetup(); + setupLayer(); + Timber.i("heatmap-intensityTransitionOptions"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set and Get + TransitionOptions options = new TransitionOptions(300, 100); + layer.setHeatmapIntensityTransition(options); + assertEquals(layer.getHeatmapIntensityTransition(), options); + } + }); + } + + @Test + public void testHeatmapIntensityAsConstant() { + validateTestSetup(); + setupLayer(); + Timber.i("heatmap-intensity"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set and Get + layer.setProperties(heatmapIntensity(0.3f)); + assertEquals((Float) layer.getHeatmapIntensity().getValue(), (Float) 0.3f); + } + }); + } + + @Test + public void testHeatmapIntensityAsCameraFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("heatmap-intensity"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + heatmapIntensity( + zoom( + exponential( + stop(2, heatmapIntensity(0.3f)) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getHeatmapIntensity()); + assertNotNull(layer.getHeatmapIntensity().getFunction()); + assertEquals(CameraFunction.class, layer.getHeatmapIntensity().getFunction().getClass()); + assertEquals(ExponentialStops.class, layer.getHeatmapIntensity().getFunction().getStops().getClass()); + assertEquals(0.5f, ((ExponentialStops) layer.getHeatmapIntensity().getFunction().getStops()).getBase(), 0.001); + assertEquals(1, ((ExponentialStops) layer.getHeatmapIntensity().getFunction().getStops()).size()); + } + }); + } + + @Test + public void testHeatmapOpacityTransition() { + validateTestSetup(); + setupLayer(); + Timber.i("heatmap-opacityTransitionOptions"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set and Get + TransitionOptions options = new TransitionOptions(300, 100); + layer.setHeatmapOpacityTransition(options); + assertEquals(layer.getHeatmapOpacityTransition(), options); + } + }); + } + + @Test + public void testHeatmapOpacityAsConstant() { + validateTestSetup(); + setupLayer(); + Timber.i("heatmap-opacity"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set and Get + layer.setProperties(heatmapOpacity(0.3f)); + assertEquals((Float) layer.getHeatmapOpacity().getValue(), (Float) 0.3f); + } + }); + } + + @Test + public void testHeatmapOpacityAsCameraFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("heatmap-opacity"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + heatmapOpacity( + zoom( + exponential( + stop(2, heatmapOpacity(0.3f)) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getHeatmapOpacity()); + assertNotNull(layer.getHeatmapOpacity().getFunction()); + assertEquals(CameraFunction.class, layer.getHeatmapOpacity().getFunction().getClass()); + assertEquals(ExponentialStops.class, layer.getHeatmapOpacity().getFunction().getStops().getClass()); + assertEquals(0.5f, ((ExponentialStops) layer.getHeatmapOpacity().getFunction().getStops()).getBase(), 0.001); + assertEquals(1, ((ExponentialStops) layer.getHeatmapOpacity().getFunction().getStops()).size()); + } + }); + } + +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml index 2d6efc0d84..5a0493e5bd 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml @@ -789,6 +789,17 @@ android:name="android.support.PARENT_ACTIVITY" android:value=".activity.FeatureOverviewActivity"/> + + + + { + mapboxMap = map; + addEarthquakeSource(); + addHeatmapLayer(); + addCircleLayer(); + }); + } + + private void addEarthquakeSource() { + try { + mapboxMap.addSource(new GeoJsonSource(EARTHQUAKE_SOURCE_ID, new URL(EARTHQUAKE_SOURCE_URL))); + } catch (MalformedURLException malformedUrlException) { + Timber.e(malformedUrlException, "That's not an url... "); + } + } + + private void addHeatmapLayer() { + HeatmapLayer layer = new HeatmapLayer(HEATMAP_LAYER_ID, EARTHQUAKE_SOURCE_ID); + layer.setMaxZoom(9); + layer.setSourceLayer(HEATMAP_LAYER_SOURCE); + layer.setProperties( + + // TODO add heatmap color https://github.com/mapbox/mapbox-gl-native/issues/11172 + // Color ramp for heatmap. Domain is 0 (low) to 1 (high). + // Begin color ramp at 0-stop with a 0-transparancy color + // to create a blur-like effect. + //heatmapColor(), + + // Increase the heatmap weight based on frequency and property magnitude + heatmapWeight( + interpolate( + linear(), get("mag"), + stop(0, 0), + stop(6, 1) + ) + ), + + // Increase the heatmap color weight weight by zoom level + // heatmap-intensity is a multiplier on top of heatmap-weight + heatmapIntensity( + interpolate( + linear(), zoom(), + stop(0, 1), + stop(9, 3) + ) + ), + + // Adjust the heatmap radius by zoom level + heatmapRadius( + interpolate( + linear(), zoom(), + stop(0, 2), + stop(9, 20) + ) + ), + + // Transition from heatmap to circle layer by zoom level + heatmapOpacity( + interpolate( + linear(), zoom(), + stop(7, 1), + stop(9, 0) + ) + ) + ); + + mapboxMap.addLayerAbove(layer, "waterway-label"); + } + + private void addCircleLayer() { + CircleLayer circleLayer = new CircleLayer(CIRCLE_LAYER_ID, EARTHQUAKE_SOURCE_ID); + circleLayer.setProperties( + + // Size circle radius by earthquake magnitude and zoom level + circleRadius( + interpolate( + linear(), zoom(), + literal(7), interpolate( + linear(), get("mag"), + stop(1, 1), + stop(6, 4) + ), + literal(16), interpolate( + linear(), get("mag"), + stop(1, 5), + stop(6, 50) + ) + ) + ), + + // Color circle by earthquake magnitude + circleColor( + interpolate( + linear(), get("mag"), + literal(1), rgba(33, 102, 172, 0), + literal(2), rgb(103, 169, 207), + literal(3), rgb(209, 229, 240), + literal(4), rgb(253, 219, 199), + literal(5), rgb(239, 138, 98), + literal(6), rgb(178, 24, 43) + ) + ), + + // Transition from heatmap to circle layer by zoom level + circleOpacity( + interpolate( + linear(), zoom(), + stop(7, 0), + stop(8, 1) + ) + ), + circleStrokeColor("white"), + circleStrokeWidth(1.0f) + ); + + mapboxMap.addLayerBelow(circleLayer, HEATMAP_LAYER_ID); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_heatmaplayer.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_heatmaplayer.xml new file mode 100644 index 0000000000..23ef9ea336 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_heatmaplayer.xml @@ -0,0 +1,14 @@ + + + + + + diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml index e867046c80..d17ec20546 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml @@ -70,4 +70,5 @@ Example Custom Geometry Source Suzhou using Droid Sans for Chinese glyphs Example raster-dem source and hillshade layer + Use HeatmapLayer to visualise earthquakes diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml index 47fee31c0a..0323d7c428 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml @@ -70,4 +70,5 @@ Grid Source Local CJK glyph generation Hillshade + Heatmap layer \ No newline at end of file diff --git a/platform/android/config.cmake b/platform/android/config.cmake index e1c36789f5..30182bbc06 100644 --- a/platform/android/config.cmake +++ b/platform/android/config.cmake @@ -158,6 +158,8 @@ add_library(mbgl-android STATIC platform/android/src/style/layers/fill_extrusion_layer.hpp platform/android/src/style/layers/fill_layer.cpp platform/android/src/style/layers/fill_layer.hpp + platform/android/src/style/layers/heatmap_layer.cpp + platform/android/src/style/layers/heatmap_layer.hpp platform/android/src/style/layers/hillshade_layer.cpp platform/android/src/style/layers/hillshade_layer.hpp platform/android/src/style/layers/layer.cpp diff --git a/platform/android/scripts/generate-style-code.js b/platform/android/scripts/generate-style-code.js index 65b0a399ac..6e6d3cfa67 100755 --- a/platform/android/scripts/generate-style-code.js +++ b/platform/android/scripts/generate-style-code.js @@ -28,6 +28,9 @@ var layers = Object.keys(spec.layer.type.values).map((type) => { }, []); const paintProperties = Object.keys(spec[`paint_${type}`]).reduce((memo, name) => { + // disabled for now, see https://github.com/mapbox/mapbox-gl-native/issues/11172 + if (name === 'heatmap-color') return memo; + spec[`paint_${type}`][name].name = name; memo.push(spec[`paint_${type}`][name]); return memo; diff --git a/platform/android/src/style/layers/heatmap_layer.cpp b/platform/android/src/style/layers/heatmap_layer.cpp new file mode 100644 index 0000000000..609499ec93 --- /dev/null +++ b/platform/android/src/style/layers/heatmap_layer.cpp @@ -0,0 +1,134 @@ +// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`. + +#include "heatmap_layer.hpp" + +#include + +#include "../conversion/property_value.hpp" +#include "../conversion/transition_options.hpp" + +namespace mbgl { +namespace android { + + /** + * Creates an owning peer object (for layers not attached to the map) from the JVM side + */ + HeatmapLayer::HeatmapLayer(jni::JNIEnv& env, jni::String layerId, jni::String sourceId) + : Layer(env, std::make_unique(jni::Make(env, layerId), jni::Make(env, sourceId))) { + } + + /** + * Creates a non-owning peer object (for layers currently attached to the map) + */ + HeatmapLayer::HeatmapLayer(mbgl::Map& map, mbgl::style::HeatmapLayer& coreLayer) + : Layer(map, coreLayer) { + } + + /** + * Creates an owning peer object (for layers not attached to the map) + */ + HeatmapLayer::HeatmapLayer(mbgl::Map& map, std::unique_ptr coreLayer) + : Layer(map, std::move(coreLayer)) { + } + + HeatmapLayer::~HeatmapLayer() = default; + + // Property getters + + jni::Object HeatmapLayer::getHeatmapRadius(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + Result converted = convert(env, layer.as()->HeatmapLayer::getHeatmapRadius()); + return jni::Object(*converted); + } + + jni::Object HeatmapLayer::getHeatmapRadiusTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = layer.as()->HeatmapLayer::getHeatmapRadiusTransition(); + return *convert>(env, options); + } + + void HeatmapLayer::setHeatmapRadiusTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + layer.as()->HeatmapLayer::setHeatmapRadiusTransition(options); + } + + jni::Object HeatmapLayer::getHeatmapWeight(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + Result converted = convert(env, layer.as()->HeatmapLayer::getHeatmapWeight()); + return jni::Object(*converted); + } + + jni::Object HeatmapLayer::getHeatmapIntensity(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + Result converted = convert(env, layer.as()->HeatmapLayer::getHeatmapIntensity()); + return jni::Object(*converted); + } + + jni::Object HeatmapLayer::getHeatmapIntensityTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = layer.as()->HeatmapLayer::getHeatmapIntensityTransition(); + return *convert>(env, options); + } + + void HeatmapLayer::setHeatmapIntensityTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + layer.as()->HeatmapLayer::setHeatmapIntensityTransition(options); + } + + jni::Object HeatmapLayer::getHeatmapOpacity(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + Result converted = convert(env, layer.as()->HeatmapLayer::getHeatmapOpacity()); + return jni::Object(*converted); + } + + jni::Object HeatmapLayer::getHeatmapOpacityTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = layer.as()->HeatmapLayer::getHeatmapOpacityTransition(); + return *convert>(env, options); + } + + void HeatmapLayer::setHeatmapOpacityTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + layer.as()->HeatmapLayer::setHeatmapOpacityTransition(options); + } + + + jni::Class HeatmapLayer::javaClass; + + jni::jobject* HeatmapLayer::createJavaPeer(jni::JNIEnv& env) { + static auto constructor = HeatmapLayer::javaClass.template GetConstructor(env); + return HeatmapLayer::javaClass.New(env, constructor, reinterpret_cast(this)); + } + + void HeatmapLayer::registerNative(jni::JNIEnv& env) { + // Lookup the class + HeatmapLayer::javaClass = *jni::Class::Find(env).NewGlobalRef(env).release(); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) + + // Register the peer + jni::RegisterNativePeer( + env, HeatmapLayer::javaClass, "nativePtr", + std::make_unique, + "initialize", + "finalize", + METHOD(&HeatmapLayer::getHeatmapRadiusTransition, "nativeGetHeatmapRadiusTransition"), + METHOD(&HeatmapLayer::setHeatmapRadiusTransition, "nativeSetHeatmapRadiusTransition"), + METHOD(&HeatmapLayer::getHeatmapRadius, "nativeGetHeatmapRadius"), + METHOD(&HeatmapLayer::getHeatmapWeight, "nativeGetHeatmapWeight"), + METHOD(&HeatmapLayer::getHeatmapIntensityTransition, "nativeGetHeatmapIntensityTransition"), + METHOD(&HeatmapLayer::setHeatmapIntensityTransition, "nativeSetHeatmapIntensityTransition"), + METHOD(&HeatmapLayer::getHeatmapIntensity, "nativeGetHeatmapIntensity"), + METHOD(&HeatmapLayer::getHeatmapOpacityTransition, "nativeGetHeatmapOpacityTransition"), + METHOD(&HeatmapLayer::setHeatmapOpacityTransition, "nativeSetHeatmapOpacityTransition"), + METHOD(&HeatmapLayer::getHeatmapOpacity, "nativeGetHeatmapOpacity")); + } + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/style/layers/heatmap_layer.hpp b/platform/android/src/style/layers/heatmap_layer.hpp new file mode 100644 index 0000000000..85f9f0292e --- /dev/null +++ b/platform/android/src/style/layers/heatmap_layer.hpp @@ -0,0 +1,50 @@ +// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`. + +#pragma once + +#include "layer.hpp" +#include "../transition_options.hpp" +#include +#include + +namespace mbgl { +namespace android { + +class HeatmapLayer : public Layer { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/layers/HeatmapLayer"; }; + + static jni::Class javaClass; + + static void registerNative(jni::JNIEnv&); + + HeatmapLayer(jni::JNIEnv&, jni::String, jni::String); + + HeatmapLayer(mbgl::Map&, mbgl::style::HeatmapLayer&); + + HeatmapLayer(mbgl::Map&, std::unique_ptr); + + ~HeatmapLayer(); + + // Properties + + jni::Object getHeatmapRadius(jni::JNIEnv&); + void setHeatmapRadiusTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Object getHeatmapRadiusTransition(jni::JNIEnv&); + + jni::Object getHeatmapWeight(jni::JNIEnv&); + + jni::Object getHeatmapIntensity(jni::JNIEnv&); + void setHeatmapIntensityTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Object getHeatmapIntensityTransition(jni::JNIEnv&); + + jni::Object getHeatmapOpacity(jni::JNIEnv&); + void setHeatmapOpacityTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Object getHeatmapOpacityTransition(jni::JNIEnv&); + jni::jobject* createJavaPeer(jni::JNIEnv&); + +}; // class HeatmapLayer + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/style/layers/layer.cpp b/platform/android/src/style/layers/layer.cpp index da1550bdb1..29530879a5 100644 --- a/platform/android/src/style/layers/layer.cpp +++ b/platform/android/src/style/layers/layer.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/platform/android/src/style/layers/layers.cpp b/platform/android/src/style/layers/layers.cpp index 5d1d1bbcbf..5df689b45d 100644 --- a/platform/android/src/style/layers/layers.cpp +++ b/platform/android/src/style/layers/layers.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -16,6 +17,7 @@ #include "custom_layer.hpp" #include "fill_extrusion_layer.hpp" #include "fill_layer.hpp" +#include "heatmap_layer.hpp" #include "hillshade_layer.hpp" #include "line_layer.hpp" #include "raster_layer.hpp" @@ -32,6 +34,7 @@ template <> struct PeerType { using Type = android::Back template <> struct PeerType { using Type = android::CircleLayer; }; template <> struct PeerType { using Type = android::FillExtrusionLayer; }; template <> struct PeerType { using Type = android::FillLayer; }; +template <> struct PeerType { using Type = android::HeatmapLayer; }; template <> struct PeerType { using Type = android::HillshadeLayer; }; template <> struct PeerType { using Type = android::LineLayer; }; template <> struct PeerType { using Type = android::RasterLayer; }; @@ -95,6 +98,7 @@ void registerNativeLayers(jni::JNIEnv& env) { CustomLayer::registerNative(env); FillExtrusionLayer::registerNative(env); FillLayer::registerNative(env); + HeatmapLayer::registerNative(env); HillshadeLayer::registerNative(env); LineLayer::registerNative(env); RasterLayer::registerNative(env); diff --git a/platform/darwin/scripts/generate-style-code.js b/platform/darwin/scripts/generate-style-code.js index a7804ac948..53a668d10b 100755 --- a/platform/darwin/scripts/generate-style-code.js +++ b/platform/darwin/scripts/generate-style-code.js @@ -308,14 +308,15 @@ global.propertyDoc = function (propertyName, property, layerType, kind) { doc += '* Predefined functions, including mathematical and string operators\n' + '* Conditional expressions\n' + '* Variable assignments and references to assigned variables\n'; + const inputVariable = property.name === 'heatmap-color' ? '$heatmapDensity' : '$zoomLevel'; if (property["property-function"]) { - doc += '* Interpolation and step functions applied to the `$zoomLevel` variable and/or feature attributes\n'; + doc += `* Interpolation and step functions applied to the \`${inputVariable}\` variable and/or feature attributes\n`; } else if (property.function === "interpolated") { - doc += '* Interpolation and step functions applied to the `$zoomLevel` variable\n\n' + + doc += `* Interpolation and step functions applied to the \`${inputVariable}\` variable\n\n` + 'This property does not support applying interpolation or step functions to feature attributes.'; } else { - doc += '* Step functions applied to the `$zoomLevel` variable\n\n' + - 'This property does not support applying interpolation functions to the `$zoomLevel` variable or applying interpolation or step functions to feature attributes.'; + doc += `* Step functions applied to the \`${inputVariable}\` variable\n\n` + + `This property does not support applying interpolation functions to the \`${inputVariable}\` variable or applying interpolation or step functions to feature attributes.`; } } return doc; @@ -387,7 +388,7 @@ global.describeValue = function (value, property, layerType) { throw new Error(`No description available for ${value[0]} expression in ${property.name} of ${layerType}.`); } } - + switch (property.type) { case 'boolean': return value ? '`YES`' : '`NO`'; diff --git a/platform/darwin/scripts/style-spec-overrides-v8.json b/platform/darwin/scripts/style-spec-overrides-v8.json index 12cfa31575..b0c50a06f8 100644 --- a/platform/darwin/scripts/style-spec-overrides-v8.json +++ b/platform/darwin/scripts/style-spec-overrides-v8.json @@ -23,6 +23,9 @@ "circle": { "doc": "An `MGLCircleStyleLayer` is a style layer that renders one or more filled circles on the map.\n\nUse a circle style layer to configure the visual appearance of point or point collection features in vector tiles loaded by an `MGLVectorSource` object or `MGLPointAnnotation`, `MGLPointFeature`, `MGLPointCollection`, or `MGLPointCollectionFeature` instances in an `MGLShapeSource` object.\n\nA circle style layer renders circles whose radii are measured in screen units. To display circles on the map whose radii correspond to real-world distances, use many-sided regular polygons and configure their appearance using an `MGLFillStyleLayer` object." }, + "heatmap": { + "doc": "An `MGLHeatmapStyleLayer` is a style layer that renders a heatmap.\n\nA heatmap visualizes the spatial distribution of a large, dense set of point data, using color to avoid cluttering the map with individual points at low zoom levels. The points are weighted by an attribute you specify. Use a heatmap style layer in conjunction with point or point collection features in vector tiles loaded by an `MGLVectorSource` object or `MGLPointAnnotation`, `MGLPointFeature`, `MGLPointCollection`, or `MGLPointCollectionFeature` instances in an `MGLShapeSource` object.\n\nConsider accompanying a heatmap style layer with an `MGLCircleStyleLayer` or `MGLSymbolStyleLayer` at high zoom levels. If you are unsure whether the point data in an `MGLShapeSource` is dense enough to warrant a heatmap, you can alternatively cluster the source using the `MGLShapeSourceOptionClustered` option and render the data using an `MGLCircleStyleLayer` or `MGLSymbolStyleLayer`." + }, "raster": { "doc": "An `MGLRasterStyleLayer` is a style layer that renders georeferenced raster imagery on the map, especially raster tiles.\n\nUse a raster style layer to configure the color parameters of raster tiles loaded by an `MGLRasterSource` object or raster images loaded by an `MGLImageSource` object. For example, you could use a raster style layer to render Mapbox Satellite imagery, a raster tile set uploaded to Mapbox Studio, or a raster map authored in TileMill, the classic Mapbox Editor, or Mapbox Studio Classic.\n\nRaster images may also be used as icons or patterns in a style layer. To register an image for use as an icon or pattern, use the `-[MGLStyle setImage:forName:]` method. To configure a point annotation’s image, use the `MGLAnnotationImage` class." }, @@ -79,6 +82,11 @@ "doc": "The base color of this layer. The extrusion's surfaces will be shaded differently based on this color in combination with the `light` settings. If this color is specified with an alpha component, the alpha component will be ignored; use `fill-extrusion-opacity` to set layer opacityco." } }, + "paint_heatmap": { + "heatmap-color": { + "doc": "Defines the color of each pixel based on its density value in a heatmap. Should be an expression that uses `$heatmapDensity` as input." + } + }, "paint_line": { "line-pattern": { "doc": "Name of image in style images to use for drawing image lines. For seamless patterns, image width must be a factor of two (2, 4, 8, ..., 512)." @@ -114,4 +122,4 @@ "doc": "Distance that the text's anchor is moved from its original placement." } } -} \ No newline at end of file +} diff --git a/platform/darwin/src/MGLHeatmapStyleLayer.h b/platform/darwin/src/MGLHeatmapStyleLayer.h new file mode 100644 index 0000000000..35095fd52e --- /dev/null +++ b/platform/darwin/src/MGLHeatmapStyleLayer.h @@ -0,0 +1,189 @@ +// This file is generated. +// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. + +#import "MGLFoundation.h" +#import "MGLVectorStyleLayer.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + An `MGLHeatmapStyleLayer` is a style layer that renders a heatmap. + + A heatmap visualizes the spatial distribution of a large, dense set of point + data, using color to avoid cluttering the map with individual points at low + zoom levels. The points are weighted by an attribute you specify. Use a heatmap + style layer in conjunction with point or point collection features in vector + tiles loaded by an `MGLVectorSource` object or `MGLPointAnnotation`, + `MGLPointFeature`, `MGLPointCollection`, or `MGLPointCollectionFeature` + instances in an `MGLShapeSource` object. + + Consider accompanying a heatmap style layer with an `MGLCircleStyleLayer` or + `MGLSymbolStyleLayer` at high zoom levels. If you are unsure whether the point + data in an `MGLShapeSource` is dense enough to warrant a heatmap, you can + alternatively cluster the source using the `MGLShapeSourceOptionClustered` + option and render the data using an `MGLCircleStyleLayer` or + `MGLSymbolStyleLayer`. + + You can access an existing heatmap style layer using the + `-[MGLStyle layerWithIdentifier:]` method if you know its identifier; + otherwise, find it using the `MGLStyle.layers` property. You can also create a + new heatmap style layer and add it to the style using a method such as + `-[MGLStyle addLayer:]`. + + ### Example + + ```swift + let layer = MGLHeatmapStyleLayer(identifier: "earthquake-heat", source: earthquakes) + layer.heatmapWeight = NSExpression(format: "FUNCTION(magnitude, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", + [0: 0, + 6: 1]) + layer.heatmapIntensity = NSExpression(format: "FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", + [0: 1, + 9: 3]) + mapView.style?.addLayer(layer) + ``` + */ +MGL_EXPORT +@interface MGLHeatmapStyleLayer : MGLVectorStyleLayer + +/** + Returns a heatmap style layer initialized with an identifier and source. + + After initializing and configuring the style layer, add it to a map view’s + style using the `-[MGLStyle addLayer:]` or + `-[MGLStyle insertLayer:belowLayer:]` method. + + @param identifier A string that uniquely identifies the source in the style to + which it is added. + @param source The source from which to obtain the data to style. If the source + has not yet been added to the current style, the behavior is undefined. + @return An initialized foreground style layer. + */ +- (instancetype)initWithIdentifier:(NSString *)identifier source:(MGLSource *)source; + +#pragma mark - Accessing the Paint Attributes + +/** + Defines the color of each point based on its density value in a heatmap. Should + be an expression that uses `$heatmapDensity` as input. + + The default value of this property is an expression that evaluates to a rainbow + color scale from blue to red. Set this property to `nil` to reset it to the + default value. + + You can set this property to an expression containing any of the following: + + * Constant `UIColor` values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$heatmapDensity` variable + + This property does not support applying interpolation or step functions to + feature attributes. + */ +@property (nonatomic, null_resettable) NSExpression *heatmapColor; + +/** + Similar to `heatmapWeight` but controls the intensity of the heatmap globally. + Primarily used for adjusting the heatmap based on zoom level. + + The default value of this property is an expression that evaluates to the float + `1`. Set this property to `nil` to reset it to the default value. + + You can set this property to an expression containing any of the following: + + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable + + This property does not support applying interpolation or step functions to + feature attributes. + */ +@property (nonatomic, null_resettable) NSExpression *heatmapIntensity; + +/** + The transition affecting any changes to this layer’s `heatmapIntensity` property. + + This property corresponds to the `heatmap-intensity-transition` property in the style JSON file format. +*/ +@property (nonatomic) MGLTransition heatmapIntensityTransition; + +/** + The global opacity at which the heatmap layer will be drawn. + + The default value of this property is an expression that evaluates to the float + `1`. Set this property to `nil` to reset it to the default value. + + You can set this property to an expression containing any of the following: + + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable + + This property does not support applying interpolation or step functions to + feature attributes. + */ +@property (nonatomic, null_resettable) NSExpression *heatmapOpacity; + +/** + The transition affecting any changes to this layer’s `heatmapOpacity` property. + + This property corresponds to the `heatmap-opacity-transition` property in the style JSON file format. +*/ +@property (nonatomic) MGLTransition heatmapOpacityTransition; + +/** + Radius of influence of one heatmap point in points. Increasing the value makes + the heatmap smoother, but less detailed. + + This property is measured in points. + + The default value of this property is an expression that evaluates to the float + `30`. Set this property to `nil` to reset it to the default value. + + You can set this property to an expression containing any of the following: + + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes + */ +@property (nonatomic, null_resettable) NSExpression *heatmapRadius; + +/** + The transition affecting any changes to this layer’s `heatmapRadius` property. + + This property corresponds to the `heatmap-radius-transition` property in the style JSON file format. +*/ +@property (nonatomic) MGLTransition heatmapRadiusTransition; + +/** + A measure of how much an individual point contributes to the heatmap. A value + of 10 would be equivalent to having 10 points of weight 1 in the same spot. + Especially useful when combined with clustering. + + The default value of this property is an expression that evaluates to the float + `1`. Set this property to `nil` to reset it to the default value. + + You can set this property to an expression containing any of the following: + + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes + */ +@property (nonatomic, null_resettable) NSExpression *heatmapWeight; + +@end + +NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/MGLHeatmapStyleLayer.mm b/platform/darwin/src/MGLHeatmapStyleLayer.mm new file mode 100644 index 0000000000..a394dbda3b --- /dev/null +++ b/platform/darwin/src/MGLHeatmapStyleLayer.mm @@ -0,0 +1,210 @@ +// This file is generated. +// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. + +#import "MGLSource.h" +#import "NSPredicate+MGLAdditions.h" +#import "NSDate+MGLAdditions.h" +#import "MGLStyleLayer_Private.h" +#import "MGLStyleValue_Private.h" +#import "MGLHeatmapStyleLayer.h" + +#include +#include + +@interface MGLHeatmapStyleLayer () + +@property (nonatomic, readonly) mbgl::style::HeatmapLayer *rawLayer; + +@end + +@implementation MGLHeatmapStyleLayer + +- (instancetype)initWithIdentifier:(NSString *)identifier source:(MGLSource *)source +{ + auto layer = std::make_unique(identifier.UTF8String, source.identifier.UTF8String); + return self = [super initWithPendingLayer:std::move(layer)]; +} + +- (mbgl::style::HeatmapLayer *)rawLayer +{ + return (mbgl::style::HeatmapLayer *)super.rawLayer; +} + +- (NSString *)sourceIdentifier +{ + MGLAssertStyleLayerIsValid(); + + return @(self.rawLayer->getSourceID().c_str()); +} + +- (NSString *)sourceLayerIdentifier +{ + MGLAssertStyleLayerIsValid(); + + auto layerID = self.rawLayer->getSourceLayer(); + return layerID.empty() ? nil : @(layerID.c_str()); +} + +- (void)setSourceLayerIdentifier:(NSString *)sourceLayerIdentifier +{ + MGLAssertStyleLayerIsValid(); + + self.rawLayer->setSourceLayer(sourceLayerIdentifier.UTF8String ?: ""); +} + +- (void)setPredicate:(NSPredicate *)predicate +{ + MGLAssertStyleLayerIsValid(); + + self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::NullFilter()); +} + +- (NSPredicate *)predicate +{ + MGLAssertStyleLayerIsValid(); + + return [NSPredicate mgl_predicateWithFilter:self.rawLayer->getFilter()]; +} + +#pragma mark - Accessing the Paint Attributes + +- (void)setHeatmapColor:(NSExpression *)heatmapColor { + MGLAssertStyleLayerIsValid(); + + auto mbglValue = MGLStyleValueTransformer().toPropertyValue(heatmapColor); + self.rawLayer->setHeatmapColor(mbglValue); +} + +- (NSExpression *)heatmapColor { + MGLAssertStyleLayerIsValid(); + + auto propertyValue = self.rawLayer->getHeatmapColor(); + if (propertyValue.isUndefined()) { + propertyValue = self.rawLayer->getDefaultHeatmapColor(); + } + return MGLStyleValueTransformer().toExpression(propertyValue); +} + +- (void)setHeatmapIntensity:(NSExpression *)heatmapIntensity { + MGLAssertStyleLayerIsValid(); + + auto mbglValue = MGLStyleValueTransformer().toPropertyValue>(heatmapIntensity); + self.rawLayer->setHeatmapIntensity(mbglValue); +} + +- (NSExpression *)heatmapIntensity { + MGLAssertStyleLayerIsValid(); + + auto propertyValue = self.rawLayer->getHeatmapIntensity(); + if (propertyValue.isUndefined()) { + propertyValue = self.rawLayer->getDefaultHeatmapIntensity(); + } + return MGLStyleValueTransformer().toExpression(propertyValue); +} + +- (void)setHeatmapIntensityTransition:(MGLTransition )transition { + MGLAssertStyleLayerIsValid(); + + mbgl::style::TransitionOptions options { { MGLDurationFromTimeInterval(transition.duration) }, { MGLDurationFromTimeInterval(transition.delay) } }; + self.rawLayer->setHeatmapIntensityTransition(options); +} + +- (MGLTransition)heatmapIntensityTransition { + MGLAssertStyleLayerIsValid(); + + mbgl::style::TransitionOptions transitionOptions = self.rawLayer->getHeatmapIntensityTransition(); + MGLTransition transition; + transition.duration = MGLTimeIntervalFromDuration(transitionOptions.duration.value_or(mbgl::Duration::zero())); + transition.delay = MGLTimeIntervalFromDuration(transitionOptions.delay.value_or(mbgl::Duration::zero())); + + return transition; +} + +- (void)setHeatmapOpacity:(NSExpression *)heatmapOpacity { + MGLAssertStyleLayerIsValid(); + + auto mbglValue = MGLStyleValueTransformer().toPropertyValue>(heatmapOpacity); + self.rawLayer->setHeatmapOpacity(mbglValue); +} + +- (NSExpression *)heatmapOpacity { + MGLAssertStyleLayerIsValid(); + + auto propertyValue = self.rawLayer->getHeatmapOpacity(); + if (propertyValue.isUndefined()) { + propertyValue = self.rawLayer->getDefaultHeatmapOpacity(); + } + return MGLStyleValueTransformer().toExpression(propertyValue); +} + +- (void)setHeatmapOpacityTransition:(MGLTransition )transition { + MGLAssertStyleLayerIsValid(); + + mbgl::style::TransitionOptions options { { MGLDurationFromTimeInterval(transition.duration) }, { MGLDurationFromTimeInterval(transition.delay) } }; + self.rawLayer->setHeatmapOpacityTransition(options); +} + +- (MGLTransition)heatmapOpacityTransition { + MGLAssertStyleLayerIsValid(); + + mbgl::style::TransitionOptions transitionOptions = self.rawLayer->getHeatmapOpacityTransition(); + MGLTransition transition; + transition.duration = MGLTimeIntervalFromDuration(transitionOptions.duration.value_or(mbgl::Duration::zero())); + transition.delay = MGLTimeIntervalFromDuration(transitionOptions.delay.value_or(mbgl::Duration::zero())); + + return transition; +} + +- (void)setHeatmapRadius:(NSExpression *)heatmapRadius { + MGLAssertStyleLayerIsValid(); + + auto mbglValue = MGLStyleValueTransformer().toPropertyValue>(heatmapRadius); + self.rawLayer->setHeatmapRadius(mbglValue); +} + +- (NSExpression *)heatmapRadius { + MGLAssertStyleLayerIsValid(); + + auto propertyValue = self.rawLayer->getHeatmapRadius(); + if (propertyValue.isUndefined()) { + propertyValue = self.rawLayer->getDefaultHeatmapRadius(); + } + return MGLStyleValueTransformer().toExpression(propertyValue); +} + +- (void)setHeatmapRadiusTransition:(MGLTransition )transition { + MGLAssertStyleLayerIsValid(); + + mbgl::style::TransitionOptions options { { MGLDurationFromTimeInterval(transition.duration) }, { MGLDurationFromTimeInterval(transition.delay) } }; + self.rawLayer->setHeatmapRadiusTransition(options); +} + +- (MGLTransition)heatmapRadiusTransition { + MGLAssertStyleLayerIsValid(); + + mbgl::style::TransitionOptions transitionOptions = self.rawLayer->getHeatmapRadiusTransition(); + MGLTransition transition; + transition.duration = MGLTimeIntervalFromDuration(transitionOptions.duration.value_or(mbgl::Duration::zero())); + transition.delay = MGLTimeIntervalFromDuration(transitionOptions.delay.value_or(mbgl::Duration::zero())); + + return transition; +} + +- (void)setHeatmapWeight:(NSExpression *)heatmapWeight { + MGLAssertStyleLayerIsValid(); + + auto mbglValue = MGLStyleValueTransformer().toPropertyValue>(heatmapWeight); + self.rawLayer->setHeatmapWeight(mbglValue); +} + +- (NSExpression *)heatmapWeight { + MGLAssertStyleLayerIsValid(); + + auto propertyValue = self.rawLayer->getHeatmapWeight(); + if (propertyValue.isUndefined()) { + propertyValue = self.rawLayer->getDefaultHeatmapWeight(); + } + return MGLStyleValueTransformer().toExpression(propertyValue); +} + +@end diff --git a/platform/darwin/src/MGLStyle.mm b/platform/darwin/src/MGLStyle.mm index 5128944312..f6fc5533be 100644 --- a/platform/darwin/src/MGLStyle.mm +++ b/platform/darwin/src/MGLStyle.mm @@ -8,6 +8,7 @@ #import "MGLLineStyleLayer.h" #import "MGLCircleStyleLayer.h" #import "MGLSymbolStyleLayer.h" +#import "MGLHeatmapStyleLayer.h" #import "MGLHillshadeStyleLayer.h" #import "MGLRasterStyleLayer.h" #import "MGLBackgroundStyleLayer.h" @@ -36,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -401,6 +403,8 @@ static NSURL *MGLStyleURL_trafficNight; return [[MGLSymbolStyleLayer alloc] initWithRawLayer:symbolLayer]; } else if (auto rasterLayer = rawLayer->as()) { return [[MGLRasterStyleLayer alloc] initWithRawLayer:rasterLayer]; + } else if (auto heatmapLayer = rawLayer->as()) { + return [[MGLHeatmapStyleLayer alloc] initWithRawLayer:heatmapLayer]; } else if (auto hillshadeLayer = rawLayer->as()) { return [[MGLHillshadeStyleLayer alloc] initWithRawLayer:hillshadeLayer]; } else if (auto circleLayer = rawLayer->as()) { diff --git a/platform/darwin/src/MGLStyleLayer.mm.ejs b/platform/darwin/src/MGLStyleLayer.mm.ejs index 41b029791f..ac7676a1cc 100644 --- a/platform/darwin/src/MGLStyleLayer.mm.ejs +++ b/platform/darwin/src/MGLStyleLayer.mm.ejs @@ -157,7 +157,9 @@ namespace mbgl { - (void)set<%- camelize(property.name) %>:(NSExpression *)<%- objCName(property) %> { MGLAssertStyleLayerIsValid(); -<% if (property["property-function"]) { -%> +<% if (property.name === 'heatmap-color') { -%> + auto mbglValue = MGLStyleValueTransformer().toPropertyValue(heatmapColor); +<% } else if (property["property-function"]) { -%> auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toPropertyValue>>(<%- objCName(property) %>); <% } else { -%> auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toPropertyValue>>(<%- objCName(property) %>); diff --git a/platform/darwin/src/MGLStyleValue_Private.h b/platform/darwin/src/MGLStyleValue_Private.h index ba4b413a3c..5124c29a90 100644 --- a/platform/darwin/src/MGLStyleValue_Private.h +++ b/platform/darwin/src/MGLStyleValue_Private.h @@ -10,6 +10,7 @@ #import "MGLConversion.h" #include #include +#include #include #import @@ -52,11 +53,20 @@ public: return mbglValue.evaluate(evaluator); } + // Convert an mbgl heatmap color property value into an mgl style value + NSExpression *toExpression(const mbgl::style::HeatmapColorPropertyValue &mbglValue) { + if (mbglValue.isUndefined()) { + return nil; + } + return [NSExpression mgl_expressionWithJSONObject:MGLJSONObjectFromMBGLExpression(mbglValue.getExpression())]; + } + /** Converts an NSExpression to an mbgl property value. */ template - MBGLValue toPropertyValue(NSExpression *expression) { + typename std::enable_if_t::value, + MBGLValue> toPropertyValue(NSExpression *expression) { if (!expression) { return {}; } @@ -85,6 +95,30 @@ public: return *value; } + + /** + Converts an NSExpression to an mbgl property value. + */ + template + typename std::enable_if_t::value, + MBGLValue> toPropertyValue(NSExpression *expression) { + if (!expression) { + return {}; + } + + NSArray *jsonExpression = expression.mgl_jsonExpressionObject; + + mbgl::style::conversion::Error valueError; + auto value = mbgl::style::conversion::convert( + mbgl::style::conversion::makeConvertible(jsonExpression), valueError); + if (!value) { + [NSException raise:NSInvalidArgumentException + format:@"Invalid property value: %@", @(valueError.message.c_str())]; + return {}; + } + + return *value; + } private: // Private utilities for converting from mgl to mbgl values diff --git a/platform/darwin/src/MGLVectorStyleLayer.h b/platform/darwin/src/MGLVectorStyleLayer.h index 7780a34c7f..177b1b70f0 100644 --- a/platform/darwin/src/MGLVectorStyleLayer.h +++ b/platform/darwin/src/MGLVectorStyleLayer.h @@ -10,10 +10,10 @@ NS_ASSUME_NONNULL_BEGIN is defined by an `MGLShapeSource` or `MGLVectorSource` object. Create instances of `MGLCircleStyleLayer`, `MGLFillStyleLayer`, - `MGLFillExtrusionStyleLayer`, `MGLLineStyleLayer`, and `MGLSymbolStyleLayer` in - order to use `MGLVectorStyleLayer`'s properties and methods. Do not create - instances of `MGLVectorStyleLayer` directly, and do not create your own - subclasses of this class. + `MGLFillExtrusionStyleLayer`, `MGLHeatmapStyleLayer`, `MGLLineStyleLayer`, and + `MGLSymbolStyleLayer` in order to use `MGLVectorStyleLayer`'s properties and + methods. Do not create instances of `MGLVectorStyleLayer` directly, and do not + create your own subclasses of this class. */ MGL_EXPORT @interface MGLVectorStyleLayer : MGLForegroundStyleLayer diff --git a/platform/darwin/test/MGLDocumentationExampleTests.swift b/platform/darwin/test/MGLDocumentationExampleTests.swift index 9bf9924869..5a6e00bc4e 100644 --- a/platform/darwin/test/MGLDocumentationExampleTests.swift +++ b/platform/darwin/test/MGLDocumentationExampleTests.swift @@ -230,6 +230,24 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate { XCTAssertNotNil(mapView.style?.layer(withIdentifier: "buildings")) } + + func testMGLHeatmapStyleLayer() { + let earthquakes = MGLShapeSource(identifier: "earthquakes", url: URL(string: "https://example.com/earthquakes.json")!, options: [:]) + mapView.style?.addSource(earthquakes) + + //#-example-code + let layer = MGLHeatmapStyleLayer(identifier: "earthquake-heat", source: earthquakes) + layer.heatmapWeight = NSExpression(format: "FUNCTION(magnitude, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", + [0: 0, + 6: 1]) + layer.heatmapIntensity = NSExpression(format: "FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", + [0: 1, + 9: 3]) + mapView.style?.addLayer(layer) + //#-end-example-code + + XCTAssertNotNil(mapView.style?.layer(withIdentifier: "earthquake-heat")) + } func testMGLSymbolStyleLayer() { let pois = MGLVectorSource(identifier: "pois", configurationURL: URL(string: "https://example.com/style.json")!) diff --git a/platform/darwin/test/MGLHeatmapColorTests.mm b/platform/darwin/test/MGLHeatmapColorTests.mm new file mode 100644 index 0000000000..8d44064d94 --- /dev/null +++ b/platform/darwin/test/MGLHeatmapColorTests.mm @@ -0,0 +1,61 @@ +#import +#import + +#import "MGLStyleLayer_Private.h" + +#include + +@interface MGLHeatmapColorTests : XCTestCase +@end + +@implementation MGLHeatmapColorTests + +- (void)testProperties { + MGLPointFeature *feature = [[MGLPointFeature alloc] init]; + MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"sourceID" shape:feature options:nil]; + MGLHeatmapStyleLayer *layer = [[MGLHeatmapStyleLayer alloc] initWithIdentifier:@"layerID" source:source]; + + auto rawLayer = layer.rawLayer->as(); + + XCTAssertTrue(rawLayer->getHeatmapColor().isUndefined(), + @"heatmap-color should be unset initially."); + NSExpression *defaultExpression = layer.heatmapColor; + + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]]; + layer.heatmapColor = constantExpression; + + + mbgl::style::PropertyValue propertyValue = { 0xff }; + XCTAssertEqual(rawLayer->getHeatmapColor().evaluate(0.0), mbgl::Color::red(), + @"Setting heatmapColor to a constant value expression should update heatmap-color."); + XCTAssertEqualObjects(layer.heatmapColor, constantExpression, + @"heatmapColor should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]]; + NSExpression *constantExpression2 = [NSExpression expressionWithFormat:@"%@", [MGLColor blueColor]]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($heatmapDensity, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@12: constantExpression2}]; + layer.heatmapColor = functionExpression; + + XCTAssertEqual(rawLayer->getHeatmapColor().evaluate(11.0), mbgl::Color::red(), + @"Setting heatmapColor to an expression depending on $heatmapDensity should update heatmap-color."); + XCTAssertEqual(rawLayer->getHeatmapColor().evaluate(12.0), mbgl::Color::blue(), + @"Setting heatmapColor to an expression depending on $heatmapDensity should update heatmap-color."); + XCTAssertEqualObjects(layer.heatmapColor, functionExpression, + @"heatmapColor should round-trip expressions depending on $heatmapDensity."); + + layer.heatmapColor = nil; + XCTAssertTrue(rawLayer->getHeatmapColor().isUndefined(), + @"Unsetting heatmapColor should return heatmap-color to the default value."); + XCTAssertEqualObjects(layer.heatmapColor, defaultExpression, + @"heatmapColor should return the default value after being unset."); + + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + XCTAssertThrowsSpecificNamed(layer.heatmapColor = functionExpression, NSException, NSInvalidArgumentException, @"MGLHeatmapLayer should raise an exception if a camera expression is applied to heatmapColor."); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.heatmapColor = functionExpression, NSException, NSInvalidArgumentException, @"MGLHeatmapLayer should raise an exception if a data expression is applied to heatmapColor."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.heatmapColor = functionExpression, NSException, NSInvalidArgumentException, @"MGLHeatmapLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); +} + +@end diff --git a/platform/darwin/test/MGLHeatmapStyleLayerTests.mm b/platform/darwin/test/MGLHeatmapStyleLayerTests.mm new file mode 100644 index 0000000000..74121affd8 --- /dev/null +++ b/platform/darwin/test/MGLHeatmapStyleLayerTests.mm @@ -0,0 +1,296 @@ +// This file is generated. +// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. + +#import "MGLStyleLayerTests.h" +#import "../../darwin/src/NSDate+MGLAdditions.h" + +#import "MGLStyleLayer_Private.h" + +#include +#include + +@interface MGLHeatmapLayerTests : MGLStyleLayerTests +@end + +@implementation MGLHeatmapLayerTests + ++ (NSString *)layerType { + return @"heatmap"; +} + +- (void)testPredicates { + MGLPointFeature *feature = [[MGLPointFeature alloc] init]; + MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"sourceID" shape:feature options:nil]; + MGLHeatmapStyleLayer *layer = [[MGLHeatmapStyleLayer alloc] initWithIdentifier:@"layerID" source:source]; + + XCTAssertNil(layer.sourceLayerIdentifier); + layer.sourceLayerIdentifier = @"layerID"; + XCTAssertEqualObjects(layer.sourceLayerIdentifier, @"layerID"); + layer.sourceLayerIdentifier = nil; + XCTAssertNil(layer.sourceLayerIdentifier); + + XCTAssertNil(layer.predicate); + layer.predicate = [NSPredicate predicateWithValue:NO]; + XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithValue:NO]); + layer.predicate = nil; + XCTAssertNil(layer.predicate); +} + +- (void)testProperties { + MGLPointFeature *feature = [[MGLPointFeature alloc] init]; + MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"sourceID" shape:feature options:nil]; + + MGLHeatmapStyleLayer *layer = [[MGLHeatmapStyleLayer alloc] initWithIdentifier:@"layerID" source:source]; + XCTAssertNotEqual(layer.rawLayer, nullptr); + XCTAssertTrue(layer.rawLayer->is()); + auto rawLayer = layer.rawLayer->as(); + + MGLTransition transitionTest = MGLTransitionMake(5, 4); + + + // heatmap-intensity + { + XCTAssertTrue(rawLayer->getHeatmapIntensity().isUndefined(), + @"heatmap-intensity should be unset initially."); + NSExpression *defaultExpression = layer.heatmapIntensity; + + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.heatmapIntensity = constantExpression; + mbgl::style::PropertyValue propertyValue = { 0xff }; + XCTAssertEqual(rawLayer->getHeatmapIntensity(), propertyValue, + @"Setting heatmapIntensity to a constant value expression should update heatmap-intensity."); + XCTAssertEqualObjects(layer.heatmapIntensity, constantExpression, + @"heatmapIntensity should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.heatmapIntensity = functionExpression; + + mbgl::style::IntervalStops intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; + propertyValue = mbgl::style::CameraFunction { intervalStops }; + + XCTAssertEqual(rawLayer->getHeatmapIntensity(), propertyValue, + @"Setting heatmapIntensity to a camera expression should update heatmap-intensity."); + XCTAssertEqualObjects(layer.heatmapIntensity, functionExpression, + @"heatmapIntensity should round-trip camera expressions."); + + + + layer.heatmapIntensity = nil; + XCTAssertTrue(rawLayer->getHeatmapIntensity().isUndefined(), + @"Unsetting heatmapIntensity should return heatmap-intensity to the default value."); + XCTAssertEqualObjects(layer.heatmapIntensity, defaultExpression, + @"heatmapIntensity should return the default value after being unset."); + + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.heatmapIntensity = functionExpression, NSException, NSInvalidArgumentException, @"MGLHeatmapLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.heatmapIntensity = functionExpression, NSException, NSInvalidArgumentException, @"MGLHeatmapLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + // Transition property test + layer.heatmapIntensityTransition = transitionTest; + auto toptions = rawLayer->getHeatmapIntensityTransition(); + XCTAssert(toptions.delay && MGLTimeIntervalFromDuration(*toptions.delay) == transitionTest.delay); + XCTAssert(toptions.duration && MGLTimeIntervalFromDuration(*toptions.duration) == transitionTest.duration); + + MGLTransition heatmapIntensityTransition = layer.heatmapIntensityTransition; + XCTAssertEqual(heatmapIntensityTransition.delay, transitionTest.delay); + XCTAssertEqual(heatmapIntensityTransition.duration, transitionTest.duration); + } + + // heatmap-opacity + { + XCTAssertTrue(rawLayer->getHeatmapOpacity().isUndefined(), + @"heatmap-opacity should be unset initially."); + NSExpression *defaultExpression = layer.heatmapOpacity; + + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.heatmapOpacity = constantExpression; + mbgl::style::PropertyValue propertyValue = { 0xff }; + XCTAssertEqual(rawLayer->getHeatmapOpacity(), propertyValue, + @"Setting heatmapOpacity to a constant value expression should update heatmap-opacity."); + XCTAssertEqualObjects(layer.heatmapOpacity, constantExpression, + @"heatmapOpacity should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.heatmapOpacity = functionExpression; + + mbgl::style::IntervalStops intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; + propertyValue = mbgl::style::CameraFunction { intervalStops }; + + XCTAssertEqual(rawLayer->getHeatmapOpacity(), propertyValue, + @"Setting heatmapOpacity to a camera expression should update heatmap-opacity."); + XCTAssertEqualObjects(layer.heatmapOpacity, functionExpression, + @"heatmapOpacity should round-trip camera expressions."); + + + + layer.heatmapOpacity = nil; + XCTAssertTrue(rawLayer->getHeatmapOpacity().isUndefined(), + @"Unsetting heatmapOpacity should return heatmap-opacity to the default value."); + XCTAssertEqualObjects(layer.heatmapOpacity, defaultExpression, + @"heatmapOpacity should return the default value after being unset."); + + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.heatmapOpacity = functionExpression, NSException, NSInvalidArgumentException, @"MGLHeatmapLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.heatmapOpacity = functionExpression, NSException, NSInvalidArgumentException, @"MGLHeatmapLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + // Transition property test + layer.heatmapOpacityTransition = transitionTest; + auto toptions = rawLayer->getHeatmapOpacityTransition(); + XCTAssert(toptions.delay && MGLTimeIntervalFromDuration(*toptions.delay) == transitionTest.delay); + XCTAssert(toptions.duration && MGLTimeIntervalFromDuration(*toptions.duration) == transitionTest.duration); + + MGLTransition heatmapOpacityTransition = layer.heatmapOpacityTransition; + XCTAssertEqual(heatmapOpacityTransition.delay, transitionTest.delay); + XCTAssertEqual(heatmapOpacityTransition.duration, transitionTest.duration); + } + + // heatmap-radius + { + XCTAssertTrue(rawLayer->getHeatmapRadius().isUndefined(), + @"heatmap-radius should be unset initially."); + NSExpression *defaultExpression = layer.heatmapRadius; + + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.heatmapRadius = constantExpression; + mbgl::style::DataDrivenPropertyValue propertyValue = { 0xff }; + XCTAssertEqual(rawLayer->getHeatmapRadius(), propertyValue, + @"Setting heatmapRadius to a constant value expression should update heatmap-radius."); + XCTAssertEqualObjects(layer.heatmapRadius, constantExpression, + @"heatmapRadius should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.heatmapRadius = functionExpression; + + mbgl::style::IntervalStops intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; + propertyValue = mbgl::style::CameraFunction { intervalStops }; + + XCTAssertEqual(rawLayer->getHeatmapRadius(), propertyValue, + @"Setting heatmapRadius to a camera expression should update heatmap-radius."); + XCTAssertEqualObjects(layer.heatmapRadius, functionExpression, + @"heatmapRadius should round-trip camera expressions."); + + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.heatmapRadius = functionExpression; + + mbgl::style::ExponentialStops exponentialStops = { {{18, 0xff}}, 1.0 }; + propertyValue = mbgl::style::SourceFunction { "keyName", exponentialStops }; + + XCTAssertEqual(rawLayer->getHeatmapRadius(), propertyValue, + @"Setting heatmapRadius to a data expression should update heatmap-radius."); + XCTAssertEqualObjects(layer.heatmapRadius, functionExpression, + @"heatmapRadius should round-trip data expressions."); + + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.heatmapRadius = functionExpression; + + std::map innerStops { {18, 0xff} }; + mbgl::style::CompositeExponentialStops compositeStops { { {10.0, innerStops} }, 1.0 }; + + propertyValue = mbgl::style::CompositeFunction { "keyName", compositeStops }; + + XCTAssertEqual(rawLayer->getHeatmapRadius(), propertyValue, + @"Setting heatmapRadius to a camera-data expression should update heatmap-radius."); + XCTAssertEqualObjects(layer.heatmapRadius, functionExpression, + @"heatmapRadius should round-trip camera-data expressions."); + + + layer.heatmapRadius = nil; + XCTAssertTrue(rawLayer->getHeatmapRadius().isUndefined(), + @"Unsetting heatmapRadius should return heatmap-radius to the default value."); + XCTAssertEqualObjects(layer.heatmapRadius, defaultExpression, + @"heatmapRadius should return the default value after being unset."); + // Transition property test + layer.heatmapRadiusTransition = transitionTest; + auto toptions = rawLayer->getHeatmapRadiusTransition(); + XCTAssert(toptions.delay && MGLTimeIntervalFromDuration(*toptions.delay) == transitionTest.delay); + XCTAssert(toptions.duration && MGLTimeIntervalFromDuration(*toptions.duration) == transitionTest.duration); + + MGLTransition heatmapRadiusTransition = layer.heatmapRadiusTransition; + XCTAssertEqual(heatmapRadiusTransition.delay, transitionTest.delay); + XCTAssertEqual(heatmapRadiusTransition.duration, transitionTest.duration); + } + + // heatmap-weight + { + XCTAssertTrue(rawLayer->getHeatmapWeight().isUndefined(), + @"heatmap-weight should be unset initially."); + NSExpression *defaultExpression = layer.heatmapWeight; + + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.heatmapWeight = constantExpression; + mbgl::style::DataDrivenPropertyValue propertyValue = { 0xff }; + XCTAssertEqual(rawLayer->getHeatmapWeight(), propertyValue, + @"Setting heatmapWeight to a constant value expression should update heatmap-weight."); + XCTAssertEqualObjects(layer.heatmapWeight, constantExpression, + @"heatmapWeight should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.heatmapWeight = functionExpression; + + mbgl::style::IntervalStops intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; + propertyValue = mbgl::style::CameraFunction { intervalStops }; + + XCTAssertEqual(rawLayer->getHeatmapWeight(), propertyValue, + @"Setting heatmapWeight to a camera expression should update heatmap-weight."); + XCTAssertEqualObjects(layer.heatmapWeight, functionExpression, + @"heatmapWeight should round-trip camera expressions."); + + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.heatmapWeight = functionExpression; + + mbgl::style::ExponentialStops exponentialStops = { {{18, 0xff}}, 1.0 }; + propertyValue = mbgl::style::SourceFunction { "keyName", exponentialStops }; + + XCTAssertEqual(rawLayer->getHeatmapWeight(), propertyValue, + @"Setting heatmapWeight to a data expression should update heatmap-weight."); + XCTAssertEqualObjects(layer.heatmapWeight, functionExpression, + @"heatmapWeight should round-trip data expressions."); + + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.heatmapWeight = functionExpression; + + std::map innerStops { {18, 0xff} }; + mbgl::style::CompositeExponentialStops compositeStops { { {10.0, innerStops} }, 1.0 }; + + propertyValue = mbgl::style::CompositeFunction { "keyName", compositeStops }; + + XCTAssertEqual(rawLayer->getHeatmapWeight(), propertyValue, + @"Setting heatmapWeight to a camera-data expression should update heatmap-weight."); + XCTAssertEqualObjects(layer.heatmapWeight, functionExpression, + @"heatmapWeight should round-trip camera-data expressions."); + + + layer.heatmapWeight = nil; + XCTAssertTrue(rawLayer->getHeatmapWeight().isUndefined(), + @"Unsetting heatmapWeight should return heatmap-weight to the default value."); + XCTAssertEqualObjects(layer.heatmapWeight, defaultExpression, + @"heatmapWeight should return the default value after being unset."); + } +} + +- (void)testPropertyNames { + [self testPropertyName:@"heatmap-intensity" isBoolean:NO]; + [self testPropertyName:@"heatmap-opacity" isBoolean:NO]; + [self testPropertyName:@"heatmap-radius" isBoolean:NO]; + [self testPropertyName:@"heatmap-weight" isBoolean:NO]; +} + +@end diff --git a/platform/darwin/test/MGLStyleLayerTests.mm.ejs b/platform/darwin/test/MGLStyleLayerTests.mm.ejs index e26c63e662..e17501ed18 100644 --- a/platform/darwin/test/MGLStyleLayerTests.mm.ejs +++ b/platform/darwin/test/MGLStyleLayerTests.mm.ejs @@ -59,6 +59,7 @@ MGLTransition transitionTest = MGLTransitionMake(5, 4); <% for (const property of properties) { -%> +<% if (property.name === 'heatmap-color') continue; -%> // <%- originalPropertyName(property) %> { @@ -151,6 +152,7 @@ - (void)testPropertyNames { <% for (const property of properties) { -%> +<% if (property.name === 'heatmap-color') continue; -%> [self testPropertyName:@"<%- property.getter || property.name %>" isBoolean:<%- property.type === 'boolean' ? 'YES' : 'NO' %>]; <% } -%> } diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index ef27596d2b..d8b89f4d80 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -10,6 +10,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT ### Styles and rendering +* Added support for a new layer type: `MGLHeatmapStyleLayer`, a powerful way to visualize point data distributions using heatmaps, fully customizable through runtime styling. [#11046](https://github.com/mapbox/mapbox-gl-native/pull/11046) * The layout and paint properties on subclasses of `MGLStyleLayer` are now of type `NSExpression` instead of `MGLStyleValue`. A new “Predicates and Expressions” guide provides an overview of the supported operators. ([#10726](https://github.com/mapbox/mapbox-gl-native/pull/10726)) * Added an `MGLComputedShapeSource` class that allows applications to supply vector data to a style layer on a per-tile basis. ([#9983](https://github.com/mapbox/mapbox-gl-native/pull/9983)) * A style can now display smooth hillshading and customize its appearance at runtime using the `MGLHillshadeStyleLayer` class. Hillshading is based on a rasterized digital elevation model supplied by the `MGLRasterDEMSource` class. ([#10642](https://github.com/mapbox/mapbox-gl-native/pull/10642)) diff --git a/platform/ios/docs/guides/For Style Authors.md b/platform/ios/docs/guides/For Style Authors.md index 72d6f144a0..68d59fbc70 100644 --- a/platform/ios/docs/guides/For Style Authors.md +++ b/platform/ios/docs/guides/For Style Authors.md @@ -192,6 +192,7 @@ In style JSON | In the SDK `circle` | `MGLCircleStyleLayer` `fill` | `MGLFillStyleLayer` `fill-extrusion` | `MGLFillExtrusionStyleLayer` +`heatmap` | `MGLHeatmapStyleLayer` `hillshade` | `MGLHillshadeStyleLayer` `line` | `MGLLineStyleLayer` `raster` | `MGLRasterStyleLayer` diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index e177a9bc87..07fae5945c 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -28,6 +28,8 @@ 16376B471FFDB92B0000563E /* one-liner.json in Resources */ = {isa = PBXBuildFile; fileRef = DA35D0871E1A6309007DED41 /* one-liner.json */; }; 16376B491FFEED010000563E /* MGLMapViewLayoutTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 16376B481FFEED010000563E /* MGLMapViewLayoutTests.m */; }; 165D0CE720005419009A3C66 /* Mapbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA8847D21CBAF91600AB86E3 /* Mapbox.framework */; }; + 170C437C2029D96F00863DF0 /* MGLHeatmapColorTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 170C43782028D49800863DF0 /* MGLHeatmapColorTests.mm */; }; + 170C437D2029D97900863DF0 /* MGLHeatmapStyleLayerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 170C43792028D49800863DF0 /* MGLHeatmapStyleLayerTests.mm */; }; 1753ED421E53CE6F00A9FD90 /* MGLConversion.h in Headers */ = {isa = PBXBuildFile; fileRef = 1753ED411E53CE6F00A9FD90 /* MGLConversion.h */; }; 1753ED431E53CE6F00A9FD90 /* MGLConversion.h in Headers */ = {isa = PBXBuildFile; fileRef = 1753ED411E53CE6F00A9FD90 /* MGLConversion.h */; }; 1F06668A1EC64F8E001C16D7 /* MGLLight.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F0666881EC64F8E001C16D7 /* MGLLight.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -226,6 +228,10 @@ 55E2AD131E5B125400E8C587 /* MGLOfflineStorageTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 55E2AD121E5B125400E8C587 /* MGLOfflineStorageTests.mm */; }; 632281DF1E6F855900D75A5D /* MBXEmbeddedMapViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 632281DE1E6F855900D75A5D /* MBXEmbeddedMapViewController.m */; }; 6407D6701E0085FD00F6A9C3 /* MGLDocumentationExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6407D66F1E0085FD00F6A9C3 /* MGLDocumentationExampleTests.swift */; }; + 8989B17C201A48EB0081CF59 /* MGLHeatmapStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 8989B17A201A48EA0081CF59 /* MGLHeatmapStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8989B17D201A48EB0081CF59 /* MGLHeatmapStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 8989B17A201A48EA0081CF59 /* MGLHeatmapStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8989B17E201A48EB0081CF59 /* MGLHeatmapStyleLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8989B17B201A48EA0081CF59 /* MGLHeatmapStyleLayer.mm */; }; + 8989B17F201A48EB0081CF59 /* MGLHeatmapStyleLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8989B17B201A48EA0081CF59 /* MGLHeatmapStyleLayer.mm */; }; 920A3E5D1E6F995200C16EFC /* MGLSourceQueryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 920A3E5C1E6F995200C16EFC /* MGLSourceQueryTests.m */; }; 927FBCFC1F4DAA8300F8BF1F /* MBXSnapshotsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 927FBCFB1F4DAA8300F8BF1F /* MBXSnapshotsViewController.m */; }; 927FBCFF1F4DB05500F8BF1F /* MGLMapSnapshotter.h in Headers */ = {isa = PBXBuildFile; fileRef = 927FBCFD1F4DB05500F8BF1F /* MGLMapSnapshotter.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -670,6 +676,8 @@ 16376B3F1FFDB4B40000563E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 16376B401FFDB4B40000563E /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 16376B481FFEED010000563E /* MGLMapViewLayoutTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLMapViewLayoutTests.m; sourceTree = ""; }; + 170C43782028D49800863DF0 /* MGLHeatmapColorTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLHeatmapColorTests.mm; path = ../../darwin/test/MGLHeatmapColorTests.mm; sourceTree = ""; }; + 170C43792028D49800863DF0 /* MGLHeatmapStyleLayerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLHeatmapStyleLayerTests.mm; path = ../../darwin/test/MGLHeatmapStyleLayerTests.mm; sourceTree = ""; }; 1753ED411E53CE6F00A9FD90 /* MGLConversion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLConversion.h; sourceTree = ""; }; 1F0666881EC64F8E001C16D7 /* MGLLight.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLLight.h; sourceTree = ""; }; 1F0666891EC64F8E001C16D7 /* MGLLight.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLLight.mm; sourceTree = ""; }; @@ -801,6 +809,8 @@ 632281DD1E6F855900D75A5D /* MBXEmbeddedMapViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MBXEmbeddedMapViewController.h; sourceTree = ""; }; 632281DE1E6F855900D75A5D /* MBXEmbeddedMapViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBXEmbeddedMapViewController.m; sourceTree = ""; }; 6407D66F1E0085FD00F6A9C3 /* MGLDocumentationExampleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MGLDocumentationExampleTests.swift; path = ../../darwin/test/MGLDocumentationExampleTests.swift; sourceTree = ""; }; + 8989B17A201A48EA0081CF59 /* MGLHeatmapStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLHeatmapStyleLayer.h; sourceTree = ""; }; + 8989B17B201A48EA0081CF59 /* MGLHeatmapStyleLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLHeatmapStyleLayer.mm; sourceTree = ""; }; 920A3E5C1E6F995200C16EFC /* MGLSourceQueryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLSourceQueryTests.m; path = ../../darwin/test/MGLSourceQueryTests.m; sourceTree = ""; }; 927FBCFA1F4DAA8300F8BF1F /* MBXSnapshotsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MBXSnapshotsViewController.h; sourceTree = ""; }; 927FBCFB1F4DAA8300F8BF1F /* MBXSnapshotsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBXSnapshotsViewController.m; sourceTree = ""; }; @@ -1236,6 +1246,8 @@ FA68F1481E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.h */, FA68F1491E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.mm */, 35D13AC11D3D19DD00AFB4E0 /* MGLFillStyleLayer.h */, + 8989B17A201A48EA0081CF59 /* MGLHeatmapStyleLayer.h */, + 8989B17B201A48EA0081CF59 /* MGLHeatmapStyleLayer.mm */, 35D13AC21D3D19DD00AFB4E0 /* MGLFillStyleLayer.mm */, 3538AA1B1D542239008EC33D /* MGLForegroundStyleLayer.h */, 3538AA1C1D542239008EC33D /* MGLForegroundStyleLayer.mm */, @@ -1306,6 +1318,8 @@ 3575798F1D513EF1000B822E /* Layers */ = { isa = PBXGroup; children = ( + 170C43782028D49800863DF0 /* MGLHeatmapColorTests.mm */, + 170C43792028D49800863DF0 /* MGLHeatmapStyleLayerTests.mm */, DA2DBBCC1D51E80400D38FF9 /* MGLStyleLayerTests.h */, DA2DBBCD1D51E80400D38FF9 /* MGLStyleLayerTests.m */, DA3C6FF21E2859E700F962BE /* test-Bridging-Header.h */, @@ -1967,6 +1981,7 @@ DA8847F91CBAFA5100AB86E3 /* MGLPolygon.h in Headers */, 4049C2AC1DB6E05500B3F799 /* MGLPointCollection_Private.h in Headers */, DA8847F81CBAFA5100AB86E3 /* MGLPointAnnotation.h in Headers */, + 8989B17C201A48EB0081CF59 /* MGLHeatmapStyleLayer.h in Headers */, 353933F21D3FB753003F57D7 /* MGLCircleStyleLayer.h in Headers */, DA8847F31CBAFA5100AB86E3 /* MGLMultiPoint.h in Headers */, 30E578171DAA85520050F07E /* UIImage+MGLAdditions.h in Headers */, @@ -2104,6 +2119,7 @@ DABFB8631CBE99E500D62B32 /* MGLOfflineRegion.h in Headers */, DA35A2B21CCA141D00E826B2 /* MGLCompassDirectionFormatter.h in Headers */, DAF0D8141DFE0EC500B28378 /* MGLVectorSource_Private.h in Headers */, + 8989B17D201A48EB0081CF59 /* MGLHeatmapStyleLayer.h in Headers */, DABFB8731CBE9A9900D62B32 /* Mapbox.h in Headers */, 357FE2DE1E02D2B20068B753 /* NSCoder+MGLAdditions.h in Headers */, 1753ED431E53CE6F00A9FD90 /* MGLConversion.h in Headers */, @@ -2548,6 +2564,8 @@ 409F43FD1E9E781C0048729D /* MGLMapViewDelegateIntegrationTests.swift in Sources */, DA2E88651CC0382C00F24E7B /* MGLStyleTests.mm in Sources */, DA2E88611CC0382C00F24E7B /* MGLGeometryTests.mm in Sources */, + 170C437D2029D97900863DF0 /* MGLHeatmapStyleLayerTests.mm in Sources */, + 170C437C2029D96F00863DF0 /* MGLHeatmapColorTests.mm in Sources */, 357579801D501E09000B822E /* MGLFillStyleLayerTests.mm in Sources */, 35D9DDE21DA25EEC00DAAD69 /* MGLCodingTests.m in Sources */, DA1F8F3D1EBD287B00367E42 /* MGLDocumentationGuideTests.swift in Sources */, @@ -2660,6 +2678,7 @@ 35B82BFA1D6C5F8400B1B721 /* NSPredicate+MGLAdditions.mm in Sources */, DA8848521CBAFB9800AB86E3 /* MGLAPIClient.m in Sources */, 966FCF4E1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.m in Sources */, + 8989B17E201A48EB0081CF59 /* MGLHeatmapStyleLayer.mm in Sources */, DA8848301CBAFA6200AB86E3 /* NSProcessInfo+MGLAdditions.m in Sources */, 353AFA161D65AB17005A69F4 /* NSDate+MGLAdditions.mm in Sources */, 35D13AC51D3D19DD00AFB4E0 /* MGLFillStyleLayer.mm in Sources */, @@ -2752,6 +2771,7 @@ DAA4E4311CBB730400178DFB /* MGLMapboxEvents.m in Sources */, 966FCF4F1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.m in Sources */, DAA4E4231CBB730400178DFB /* MGLPolygon.mm in Sources */, + 8989B17F201A48EB0081CF59 /* MGLHeatmapStyleLayer.mm in Sources */, 353AFA171D65AB17005A69F4 /* NSDate+MGLAdditions.mm in Sources */, 35D13AC61D3D19DD00AFB4E0 /* MGLFillStyleLayer.mm in Sources */, DAA4E42A1CBB730400178DFB /* NSProcessInfo+MGLAdditions.m in Sources */, diff --git a/platform/ios/jazzy.yml b/platform/ios/jazzy.yml index b3662adc70..61e9ad39e8 100644 --- a/platform/ios/jazzy.yml +++ b/platform/ios/jazzy.yml @@ -95,6 +95,7 @@ custom_categories: - MGLCircleStyleLayer - MGLFillStyleLayer - MGLFillExtrusionStyleLayer + - MGLHeatmapStyleLayer - MGLHillshadeStyleLayer - MGLLineStyleLayer - MGLSymbolStyleLayer diff --git a/platform/ios/src/Mapbox.h b/platform/ios/src/Mapbox.h index d105189bb8..11720ac68e 100644 --- a/platform/ios/src/Mapbox.h +++ b/platform/ios/src/Mapbox.h @@ -45,6 +45,7 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[]; #import "MGLSymbolStyleLayer.h" #import "MGLRasterStyleLayer.h" #import "MGLCircleStyleLayer.h" +#import "MGLHeatmapStyleLayer.h" #import "MGLHillshadeStyleLayer.h" #import "MGLBackgroundStyleLayer.h" #import "MGLOpenGLStyleLayer.h" diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index 0c8c59700d..1f671cb82a 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -4,6 +4,7 @@ ### Styles and rendering +* Added support for a new layer type: `MGLHeatmapStyleLayer`, a powerful way to visualize point data distributions using heatmaps, fully customizable through runtime styling. [#11046](https://github.com/mapbox/mapbox-gl-native/pull/11046) * The layout and paint properties on subclasses of `MGLStyleLayer` are now of type `NSExpression` instead of `MGLStyleValue`. A new “Predicates and Expressions” guide provides an overview of the supported operators. ([#10726](https://github.com/mapbox/mapbox-gl-native/pull/10726)) * Added an `MGLComputedShapeSource` class that allows applications to supply vector data to a style layer on a per-tile basis. ([#9983](https://github.com/mapbox/mapbox-gl-native/pull/9983)) * A style can now display smooth hillshading and customize its appearance at runtime using the `MGLHillshadeStyleLayer` class. Hillshading is based on a rasterized digital elevation model supplied by the `MGLRasterDEMSource` class. ([#10642](https://github.com/mapbox/mapbox-gl-native/pull/10642)) diff --git a/platform/macos/app/Assets.xcassets/Layers/heatmap.imageset/Contents.json b/platform/macos/app/Assets.xcassets/Layers/heatmap.imageset/Contents.json new file mode 100644 index 0000000000..10226ca23a --- /dev/null +++ b/platform/macos/app/Assets.xcassets/Layers/heatmap.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "heatmap.pdf", + "language-direction" : "left-to-right" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "template-rendering-intent" : "template" + } +} \ No newline at end of file diff --git a/platform/macos/app/Assets.xcassets/Layers/heatmap.imageset/heatmap.pdf b/platform/macos/app/Assets.xcassets/Layers/heatmap.imageset/heatmap.pdf new file mode 100644 index 0000000000..88195c3735 Binary files /dev/null and b/platform/macos/app/Assets.xcassets/Layers/heatmap.imageset/heatmap.pdf differ diff --git a/platform/macos/app/StyleLayerIconTransformer.m b/platform/macos/app/StyleLayerIconTransformer.m index 50fe06a2c6..199de86d8b 100644 --- a/platform/macos/app/StyleLayerIconTransformer.m +++ b/platform/macos/app/StyleLayerIconTransformer.m @@ -31,6 +31,9 @@ if ([layer isKindOfClass:[MGLSymbolStyleLayer class]]) { return [NSImage imageNamed:@"symbol"]; } + if ([layer isKindOfClass:[MGLHeatmapStyleLayer class]]) { + return [NSImage imageNamed:@"heatmap"]; + } if ([layer isKindOfClass:[MGLHillshadeStyleLayer class]]) { return [NSImage imageNamed:@"hillshade"]; } diff --git a/platform/macos/app/heatmap.json b/platform/macos/app/heatmap.json new file mode 100644 index 0000000000..6469e57022 --- /dev/null +++ b/platform/macos/app/heatmap.json @@ -0,0 +1,809 @@ +{ + "version": 8, + "name": "Basic Heatmap", + "center": [ + 30.49860107152665, + 50.459868549177486 + ], + "zoom": 14.033276876197775, + "bearing": 0, + "pitch": 0, + "sources": { + "mapbox": { + "url": "mapbox://mapbox.mapbox-streets-v7", + "type": "vector" + } + }, + "sprite": "mapbox://sprites/mourner/cjcgg2bl16cf42snvcbbaf09z", + "glyphs": "mapbox://fonts/mourner/{fontstack}/{range}.pbf", + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "#dedede" + } + }, + { + "id": "landuse_overlay_national_park", + "type": "fill", + "source": "mapbox", + "source-layer": "landuse_overlay", + "filter": [ + "==", + "class", + "national_park" + ], + "paint": { + "fill-color": "#d2edae", + "fill-opacity": 0.75 + } + }, + { + "id": "landuse_park", + "type": "fill", + "source": "mapbox", + "source-layer": "landuse", + "filter": [ + "==", + "class", + "park" + ], + "paint": { + "fill-color": "#d2edae" + } + }, + { + "id": "waterway", + "type": "line", + "source": "mapbox", + "source-layer": "waterway", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "in", + "class", + "canal", + "river" + ] + ], + "paint": { + "line-color": "#a0cfdf", + "line-width": { + "base": 1.4, + "stops": [ + [ + 8, + 0.5 + ], + [ + 20, + 15 + ] + ] + } + } + }, + { + "id": "water", + "type": "fill", + "source": "mapbox", + "source-layer": "water", + "paint": { + "fill-color": "#a0cfdf" + } + }, + { + "id": "building", + "type": "fill", + "source": "mapbox", + "source-layer": "building", + "paint": { + "fill-color": "#d6d6d6" + } + }, + { + "id": "tunnel_minor", + "type": "line", + "source": "mapbox", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "structure", + "tunnel" + ], + [ + "in", + "class", + "link", + "motorway_link", + "path", + "pedestrian", + "service", + "street", + "street_limited", + "track" + ] + ] + ], + "layout": { + "line-cap": "butt", + "line-join": "miter" + }, + "paint": { + "line-color": "#efefef", + "line-width": { + "base": 1.55, + "stops": [ + [ + 4, + 0.25 + ], + [ + 20, + 30 + ] + ] + }, + "line-dasharray": [ + 0.36, + 0.18 + ] + } + }, + { + "id": "tunnel_major", + "type": "line", + "source": "mapbox", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "structure", + "tunnel" + ], + [ + "in", + "class", + "motorway", + "primary", + "secondary", + "tertiary", + "trunk" + ] + ] + ], + "layout": { + "line-cap": "butt", + "line-join": "miter" + }, + "paint": { + "line-color": "#fff", + "line-width": { + "base": 1.4, + "stops": [ + [ + 6, + 0.5 + ], + [ + 20, + 30 + ] + ] + }, + "line-dasharray": [ + 0.28, + 0.14 + ] + } + }, + { + "id": "road_minor", + "type": "line", + "source": "mapbox", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "in", + "class", + "link", + "motorway_link", + "path", + "pedestrian", + "service", + "street", + "street_limited", + "track" + ], + [ + "in", + "structure", + "ford", + "none" + ] + ] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#efefef", + "line-width": { + "base": 1.55, + "stops": [ + [ + 4, + 0.25 + ], + [ + 20, + 30 + ] + ] + } + } + }, + { + "id": "road_major", + "type": "line", + "source": "mapbox", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "in", + "class", + "motorway", + "primary", + "secondary", + "tertiary", + "trunk" + ], + [ + "in", + "structure", + "ford", + "none" + ] + ] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#fff", + "line-width": { + "base": 1.4, + "stops": [ + [ + 6, + 0.5 + ], + [ + 20, + 30 + ] + ] + } + } + }, + { + "id": "bridge_minor case", + "type": "line", + "source": "mapbox", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "structure", + "bridge" + ], + [ + "in", + "class", + "link", + "motorway_link", + "path", + "pedestrian", + "service", + "street", + "street_limited", + "track" + ] + ] + ], + "layout": { + "line-cap": "butt", + "line-join": "miter" + }, + "paint": { + "line-color": "#dedede", + "line-width": { + "base": 1.6, + "stops": [ + [ + 12, + 0.5 + ], + [ + 20, + 10 + ] + ] + }, + "line-gap-width": { + "base": 1.55, + "stops": [ + [ + 4, + 0.25 + ], + [ + 20, + 30 + ] + ] + } + } + }, + { + "id": "bridge_major case", + "type": "line", + "source": "mapbox", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "structure", + "bridge" + ], + [ + "in", + "class", + "motorway", + "primary", + "secondary", + "tertiary", + "trunk" + ] + ] + ], + "layout": { + "line-cap": "butt", + "line-join": "miter" + }, + "paint": { + "line-color": "#dedede", + "line-width": { + "base": 1.6, + "stops": [ + [ + 12, + 0.5 + ], + [ + 20, + 10 + ] + ] + }, + "line-gap-width": { + "base": 1.55, + "stops": [ + [ + 4, + 0.25 + ], + [ + 20, + 30 + ] + ] + } + } + }, + { + "id": "bridge_minor", + "type": "line", + "source": "mapbox", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "structure", + "bridge" + ], + [ + "in", + "class", + "link", + "motorway_link", + "path", + "pedestrian", + "service", + "street", + "street_limited", + "track" + ] + ] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#efefef", + "line-width": { + "base": 1.55, + "stops": [ + [ + 4, + 0.25 + ], + [ + 20, + 30 + ] + ] + } + } + }, + { + "id": "bridge_major", + "type": "line", + "source": "mapbox", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "structure", + "bridge" + ], + [ + "in", + "class", + "motorway", + "primary", + "secondary", + "tertiary", + "trunk" + ] + ] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#fff", + "line-width": { + "base": 1.4, + "stops": [ + [ + 6, + 0.5 + ], + [ + 20, + 30 + ] + ] + } + } + }, + { + "id": "admin_country", + "type": "line", + "source": "mapbox", + "source-layer": "admin", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "<=", + "admin_level", + 2 + ], + [ + "==", + "maritime", + 0 + ] + ] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#8b8a8a", + "line-width": { + "base": 1.3, + "stops": [ + [ + 3, + 0.5 + ], + [ + 22, + 15 + ] + ] + } + } + }, + { + "id": "road_major_label", + "type": "symbol", + "source": "mapbox", + "source-layer": "road_label", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "in", + "class", + "motorway", + "primary", + "secondary", + "tertiary", + "trunk" + ] + ], + "layout": { + "symbol-placement": "line", + "text-field": "{name_en}", + "text-font": [ + "Open Sans Semibold", + "Arial Unicode MS Bold" + ], + "text-transform": "uppercase", + "text-letter-spacing": 0.1, + "text-size": { + "base": 1.4, + "stops": [ + [ + 10, + 8 + ], + [ + 20, + 14 + ] + ] + } + }, + "paint": { + "text-color": "#666", + "text-halo-color": "rgba(255,255,255,0.75)", + "text-halo-width": 2 + } + }, + { + "id": "place_label_other", + "type": "symbol", + "source": "mapbox", + "source-layer": "place_label", + "minzoom": 8, + "filter": [ + "all", + [ + "==", + "$type", + "Point" + ], + [ + "in", + "type", + "hamlet", + "island", + "neighbourhood", + "suburb", + "town", + "village" + ] + ], + "layout": { + "text-field": "{name_en}", + "text-font": [ + "Open Sans Semibold", + "Arial Unicode MS Bold" + ], + "text-max-width": 6, + "text-size": { + "stops": [ + [ + 6, + 12 + ], + [ + 12, + 16 + ] + ] + } + }, + "paint": { + "text-color": "#666", + "text-halo-color": "rgba(255,255,255,0.75)", + "text-halo-width": 1, + "text-halo-blur": 1 + } + }, + { + "id": "place_label_city", + "type": "symbol", + "source": "mapbox", + "source-layer": "place_label", + "maxzoom": 16, + "filter": [ + "all", + [ + "==", + "$type", + "Point" + ], + [ + "==", + "type", + "city" + ] + ], + "layout": { + "text-field": "{name_en}", + "text-font": [ + "Open Sans Bold", + "Arial Unicode MS Bold" + ], + "text-max-width": 10, + "text-size": { + "stops": [ + [ + 3, + 12 + ], + [ + 8, + 16 + ] + ] + } + }, + "paint": { + "text-color": "#666", + "text-halo-color": "rgba(255,255,255,0.75)", + "text-halo-width": 1, + "text-halo-blur": 1 + } + }, + { + "id": "country_label", + "type": "symbol", + "source": "mapbox", + "source-layer": "country_label", + "maxzoom": 12, + "filter": [ + "==", + "$type", + "Point" + ], + "layout": { + "text-field": "{name_en}", + "text-font": [ + "Open Sans Regular", + "Arial Unicode MS Regular" + ], + "text-max-width": 10, + "text-size": { + "stops": [ + [ + 3, + 14 + ], + [ + 8, + 22 + ] + ] + } + }, + "paint": { + "text-color": "#666", + "text-halo-color": "rgba(255,255,255,0.75)", + "text-halo-width": 1, + "text-halo-blur": 1 + } + }, + { + "id": "road-heatmap", + "type": "heatmap", + "source": "mapbox", + "source-layer": "road", + "paint": { + "heatmap-intensity": 1 + } + } + ] +} diff --git a/platform/macos/app/resources/heatmap.svg b/platform/macos/app/resources/heatmap.svg new file mode 100644 index 0000000000..fa2a46590a --- /dev/null +++ b/platform/macos/app/resources/heatmap.svg @@ -0,0 +1,72 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/platform/macos/docs/guides/For Style Authors.md b/platform/macos/docs/guides/For Style Authors.md index 6897d640b3..de838cd78f 100644 --- a/platform/macos/docs/guides/For Style Authors.md +++ b/platform/macos/docs/guides/For Style Authors.md @@ -179,6 +179,7 @@ In style JSON | In the SDK `circle` | `MGLCircleStyleLayer` `fill` | `MGLFillStyleLayer` `fill-extrusion` | `MGLFillExtrusionStyleLayer` +`heatmap` | `MGLHeatmapStyleLayer` `hillshade` | `MGLHillshadeStyleLayer` `line` | `MGLLineStyleLayer` `raster` | `MGLRasterStyleLayer` diff --git a/platform/macos/jazzy.yml b/platform/macos/jazzy.yml index 682ccd99ff..b4dccb620f 100644 --- a/platform/macos/jazzy.yml +++ b/platform/macos/jazzy.yml @@ -80,6 +80,7 @@ custom_categories: - MGLCircleStyleLayer - MGLFillStyleLayer - MGLFillExtrusionStyleLayer + - MGLHeatmapStyleLayer - MGLHillshadeStyleLayer - MGLLineStyleLayer - MGLSymbolStyleLayer diff --git a/platform/macos/macos.xcodeproj/project.pbxproj b/platform/macos/macos.xcodeproj/project.pbxproj index 090902fca4..4327670911 100644 --- a/platform/macos/macos.xcodeproj/project.pbxproj +++ b/platform/macos/macos.xcodeproj/project.pbxproj @@ -16,6 +16,8 @@ 07D9474D1F67441B00E37934 /* MGLAbstractShapeSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 07D947471F6741F500E37934 /* MGLAbstractShapeSource_Private.h */; }; 07F8E2F71F674C8800F794BB /* MGLComputedShapeSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 07F8E2F41F674C8000F794BB /* MGLComputedShapeSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; 07F8E2F81F674C9000F794BB /* MGLComputedShapeSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 07F8E2F51F674C8000F794BB /* MGLComputedShapeSource.mm */; }; + 170A82BF201BDD1B00943087 /* MGLHeatmapStyleLayerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 170A82BE201BDD1B00943087 /* MGLHeatmapStyleLayerTests.mm */; }; + 170A82C4201FB6EC00943087 /* MGLHeatmapColorTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 170A82C2201FAFF800943087 /* MGLHeatmapColorTests.mm */; }; 1753ED401E53CE6100A9FD90 /* MGLConversion.h in Headers */ = {isa = PBXBuildFile; fileRef = 1753ED3F1E53CE5200A9FD90 /* MGLConversion.h */; }; 1F7454A31ECFB00300021D39 /* MGLLight_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F7454A01ECFB00300021D39 /* MGLLight_Private.h */; }; 1F7454A41ECFB00300021D39 /* MGLLight.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F7454A11ECFB00300021D39 /* MGLLight.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -85,6 +87,9 @@ 55D120A31F7906E6004B6D81 /* libmbgl-filesource.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 55D120A41F7906E6004B6D81 /* libmbgl-filesource.a */; }; 55D120A51F790A0C004B6D81 /* libmbgl-filesource.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 55D120A41F7906E6004B6D81 /* libmbgl-filesource.a */; }; 55E2AD111E5B0A6900E8C587 /* MGLOfflineStorageTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 55E2AD101E5B0A6900E8C587 /* MGLOfflineStorageTests.mm */; }; + 89462399200D199100DA8EF2 /* heatmap.json in Resources */ = {isa = PBXBuildFile; fileRef = 89462398200D199100DA8EF2 /* heatmap.json */; }; + 8946239D200E744800DA8EF2 /* MGLHeatmapStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 8946239A200E73CA00DA8EF2 /* MGLHeatmapStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 894623A0200E748000DA8EF2 /* MGLHeatmapStyleLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8946239B200E73CA00DA8EF2 /* MGLHeatmapStyleLayer.mm */; }; 92092EF01F5EB10E00AF5130 /* MGLMapSnapshotter.h in Headers */ = {isa = PBXBuildFile; fileRef = 92092EEE1F5EB10E00AF5130 /* MGLMapSnapshotter.h */; settings = {ATTRIBUTES = (Public, ); }; }; 92092EF11F5EB10E00AF5130 /* MGLMapSnapshotter.mm in Sources */ = {isa = PBXBuildFile; fileRef = 92092EEF1F5EB10E00AF5130 /* MGLMapSnapshotter.mm */; }; 920A3E591E6F859D00C16EFC /* MGLSourceQueryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 920A3E581E6F859D00C16EFC /* MGLSourceQueryTests.m */; }; @@ -298,6 +303,8 @@ 07D947491F6741F500E37934 /* MGLAbstractShapeSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLAbstractShapeSource.mm; sourceTree = ""; }; 07F8E2F41F674C8000F794BB /* MGLComputedShapeSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLComputedShapeSource.h; sourceTree = ""; }; 07F8E2F51F674C8000F794BB /* MGLComputedShapeSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLComputedShapeSource.mm; sourceTree = ""; }; + 170A82BE201BDD1B00943087 /* MGLHeatmapStyleLayerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLHeatmapStyleLayerTests.mm; sourceTree = ""; }; + 170A82C2201FAFF800943087 /* MGLHeatmapColorTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLHeatmapColorTests.mm; sourceTree = ""; }; 1753ED3F1E53CE5200A9FD90 /* MGLConversion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLConversion.h; sourceTree = ""; }; 1F7454A01ECFB00300021D39 /* MGLLight_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLLight_Private.h; sourceTree = ""; }; 1F7454A11ECFB00300021D39 /* MGLLight.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLLight.h; sourceTree = ""; }; @@ -369,6 +376,9 @@ 55D9B4B01D005D3900C1CCE2 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; 55E2AD101E5B0A6900E8C587 /* MGLOfflineStorageTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLOfflineStorageTests.mm; path = ../../darwin/test/MGLOfflineStorageTests.mm; sourceTree = ""; }; 55FE0E8D1D100A0900FD240B /* config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = config.xcconfig; path = ../../build/macos/config.xcconfig; sourceTree = ""; }; + 89462398200D199100DA8EF2 /* heatmap.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = heatmap.json; sourceTree = ""; }; + 8946239A200E73CA00DA8EF2 /* MGLHeatmapStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLHeatmapStyleLayer.h; sourceTree = ""; }; + 8946239B200E73CA00DA8EF2 /* MGLHeatmapStyleLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLHeatmapStyleLayer.mm; sourceTree = ""; }; 92092EEE1F5EB10E00AF5130 /* MGLMapSnapshotter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLMapSnapshotter.h; sourceTree = ""; }; 92092EEF1F5EB10E00AF5130 /* MGLMapSnapshotter.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLMapSnapshotter.mm; sourceTree = ""; }; 920A3E581E6F859D00C16EFC /* MGLSourceQueryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLSourceQueryTests.m; sourceTree = ""; }; @@ -672,6 +682,8 @@ 35602BF91D3EA99F0050646F /* MGLFillStyleLayer.mm */, 35602BFD1D3EA9B40050646F /* MGLForegroundStyleLayer.h */, 35602BFE1D3EA9B40050646F /* MGLForegroundStyleLayer.mm */, + 8946239A200E73CA00DA8EF2 /* MGLHeatmapStyleLayer.h */, + 8946239B200E73CA00DA8EF2 /* MGLHeatmapStyleLayer.mm */, DAF25714201901C200367EF5 /* MGLHillshadeStyleLayer.h */, DAF25713201901C100367EF5 /* MGLHillshadeStyleLayer.mm */, DA8F25891D51CA540010E6B5 /* MGLLineStyleLayer.h */, @@ -825,6 +837,7 @@ DA839EA61CC2E3400062CAFB /* Info.plist */, 96E027331E57C9A7004B8E66 /* Localizable.strings */, DA839E981CC2E3400062CAFB /* Supporting Files */, + 89462398200D199100DA8EF2 /* heatmap.json */, ); name = "Demo App"; path = app; @@ -875,6 +888,8 @@ DA8F257C1D51C5F40010E6B5 /* Layers */ = { isa = PBXGroup; children = ( + 170A82C2201FAFF800943087 /* MGLHeatmapColorTests.mm */, + 170A82BE201BDD1B00943087 /* MGLHeatmapStyleLayerTests.mm */, 40E1601A1DF216E6005EA6D9 /* MGLStyleLayerTests.h */, 40E1601B1DF216E6005EA6D9 /* MGLStyleLayerTests.m */, DA2207BA1DC076930002F84D /* test-Bridging-Header.h */, @@ -1232,6 +1247,7 @@ 359819591E02F611008FC139 /* NSCoder+MGLAdditions.h in Headers */, DAE6C38E1CC31E2A00DB3429 /* MGLOfflineStorage_Private.h in Headers */, DA87A9A01DC9DC6200810D09 /* MGLValueEvaluator.h in Headers */, + 8946239D200E744800DA8EF2 /* MGLHeatmapStyleLayer.h in Headers */, DAE6C3601CC31E0400DB3429 /* MGLOfflineRegion.h in Headers */, DAE6C3681CC31E0400DB3429 /* MGLTilePyramidOfflineRegion.h in Headers */, DA35A2CF1CCAAED300E826B2 /* NSValue+MGLAdditions.h in Headers */, @@ -1422,6 +1438,7 @@ DA839EA01CC2E3400062CAFB /* MapDocument.xib in Resources */, 353BAEF81D6463B8009A8DA9 /* amsterdam.geojson in Resources */, 96E027311E57C9A7004B8E66 /* Localizable.strings in Resources */, + 89462399200D199100DA8EF2 /* heatmap.json in Resources */, DA839EA51CC2E3400062CAFB /* MainMenu.xib in Resources */, DA5589771D320C41006B7F64 /* wms.json in Resources */, DAE6C2E21CC304F900DB3429 /* Credits.rtf in Resources */, @@ -1503,6 +1520,7 @@ 07D9474B1F6743F000E37934 /* MGLAbstractShapeSource.mm in Sources */, DAE6C3941CC31E2A00DB3429 /* MGLStyle.mm in Sources */, DAE6C3871CC31E2A00DB3429 /* MGLGeometry.mm in Sources */, + 894623A0200E748000DA8EF2 /* MGLHeatmapStyleLayer.mm in Sources */, 3527428E1D4C24AB00A1ECE6 /* MGLCircleStyleLayer.mm in Sources */, 35602C011D3EA9B40050646F /* MGLForegroundStyleLayer.mm in Sources */, 408AA86A1DAEEE5D00022900 /* NSDictionary+MGLAdditions.mm in Sources */, @@ -1565,11 +1583,13 @@ DAE6C3D21CC34C9900DB3429 /* MGLGeometryTests.mm in Sources */, DA87A9A41DCACC5000810D09 /* MGLSymbolStyleLayerTests.mm in Sources */, 40E1601D1DF217D6005EA6D9 /* MGLStyleLayerTests.m in Sources */, + 170A82BF201BDD1B00943087 /* MGLHeatmapStyleLayerTests.mm in Sources */, 1F95931B1E6DE2B600D5B294 /* MGLNSDateAdditionsTests.mm in Sources */, DAF25721201902C100367EF5 /* MGLHillshadeStyleLayerTests.mm in Sources */, DA87A9A61DCACC5000810D09 /* MGLCircleStyleLayerTests.mm in Sources */, DA87A99E1DC9DC2100810D09 /* MGLPredicateTests.mm in Sources */, DD58A4C91D822C6700E1F038 /* MGLExpressionTests.mm in Sources */, + 170A82C4201FB6EC00943087 /* MGLHeatmapColorTests.mm in Sources */, 4031ACFC1E9EB3C100A3EA26 /* MGLMapViewDelegateIntegrationTests.swift in Sources */, 4031AD031E9FD6AA00A3EA26 /* MGLSDKTestHelpers.swift in Sources */, DA87A9A71DCACC5000810D09 /* MGLBackgroundStyleLayerTests.mm in Sources */, diff --git a/platform/macos/src/Mapbox.h b/platform/macos/src/Mapbox.h index 7ea9a9dd1d..0e4b546cf7 100644 --- a/platform/macos/src/Mapbox.h +++ b/platform/macos/src/Mapbox.h @@ -44,6 +44,7 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[]; #import "MGLRasterStyleLayer.h" #import "MGLCircleStyleLayer.h" #import "MGLBackgroundStyleLayer.h" +#import "MGLHeatmapStyleLayer.h" #import "MGLHillshadeStyleLayer.h" #import "MGLOpenGLStyleLayer.h" #import "MGLSource.h" diff --git a/platform/node/src/node_map.cpp b/platform/node/src/node_map.cpp index 3e66aaa789..ac14df0228 100644 --- a/platform/node/src/node_map.cpp +++ b/platform/node/src/node_map.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include diff --git a/platform/node/test/ignores.json b/platform/node/test/ignores.json index 4d6f26b056..42751da30f 100644 --- a/platform/node/test/ignores.json +++ b/platform/node/test/ignores.json @@ -23,24 +23,6 @@ "render-tests/fill-extrusion-pattern/opacity": "https://github.com/mapbox/mapbox-gl-js/issues/3327", "render-tests/geojson/inline-linestring-fill": "current behavior is arbitrary", "render-tests/geojson/inline-polygon-symbol": "behavior needs reconciliation with gl-js", - "render-tests/heatmap-color/default": "https://github.com/mapbox/mapbox-gl-native/issues/10146", - "render-tests/heatmap-color/expression": "https://github.com/mapbox/mapbox-gl-native/issues/10146", - "render-tests/heatmap-color/function": "https://github.com/mapbox/mapbox-gl-native/issues/10146", - "render-tests/heatmap-intensity/default": "https://github.com/mapbox/mapbox-gl-native/issues/10146", - "render-tests/heatmap-intensity/function": "https://github.com/mapbox/mapbox-gl-native/issues/10146", - "render-tests/heatmap-intensity/literal": "https://github.com/mapbox/mapbox-gl-native/issues/10146", - "render-tests/heatmap-opacity/default": "https://github.com/mapbox/mapbox-gl-native/issues/10146", - "render-tests/heatmap-opacity/function": "https://github.com/mapbox/mapbox-gl-native/issues/10146", - "render-tests/heatmap-opacity/literal": "https://github.com/mapbox/mapbox-gl-native/issues/10146", - "render-tests/heatmap-radius/antimeridian": "https://github.com/mapbox/mapbox-gl-native/issues/10146", - "render-tests/heatmap-radius/data-expression": "https://github.com/mapbox/mapbox-gl-native/issues/10146", - "render-tests/heatmap-radius/default": "https://github.com/mapbox/mapbox-gl-native/issues/10146", - "render-tests/heatmap-radius/function": "https://github.com/mapbox/mapbox-gl-native/issues/10146", - "render-tests/heatmap-radius/literal": "https://github.com/mapbox/mapbox-gl-native/issues/10146", - "render-tests/heatmap-radius/pitch30": "https://github.com/mapbox/mapbox-gl-native/issues/10146", - "render-tests/heatmap-weight/default": "https://github.com/mapbox/mapbox-gl-native/issues/10146", - "render-tests/heatmap-weight/identity-property-function": "https://github.com/mapbox/mapbox-gl-native/issues/10146", - "render-tests/heatmap-weight/literal": "https://github.com/mapbox/mapbox-gl-native/issues/10146", "render-tests/mixed-zoom/z10-z11": "https://github.com/mapbox/mapbox-gl-native/issues/10397", "render-tests/raster-masking/overlapping-zoom": "https://github.com/mapbox/mapbox-gl-native/issues/10195", "render-tests/regressions/mapbox-gl-js#2305": "https://github.com/mapbox/mapbox-gl-native/issues/6927", diff --git a/scripts/generate-shaders.js b/scripts/generate-shaders.js index 46c097d51a..b1eeffb8a0 100755 --- a/scripts/generate-shaders.js +++ b/scripts/generate-shaders.js @@ -7,9 +7,6 @@ const outputPath = 'src/mbgl/shaders'; var shaders = require('../mapbox-gl-js/src/shaders'); -delete shaders.heatmap; -delete shaders.heatmapTexture; - require('./style-code'); writeIfModified(path.join(outputPath, 'preludes.hpp'), `// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. diff --git a/scripts/generate-style-code.js b/scripts/generate-style-code.js index ff40244f98..6ddb787f19 100755 --- a/scripts/generate-style-code.js +++ b/scripts/generate-style-code.js @@ -97,6 +97,8 @@ global.paintPropertyType = function (property, type) { global.propertyValueType = function (property) { if (isDataDriven(property)) { return `DataDrivenPropertyValue<${evaluatedType(property)}>`; + } else if (property.name === 'heatmap-color') { + return `HeatmapColorPropertyValue`; } else { return `PropertyValue<${evaluatedType(property)}>`; } diff --git a/scripts/style-spec.js b/scripts/style-spec.js index 00daee70d0..196adc0b32 100644 --- a/scripts/style-spec.js +++ b/scripts/style-spec.js @@ -1,5 +1,3 @@ var spec = module.exports = require('../mapbox-gl-js/src/style-spec/reference/v8'); // Make temporary modifications here when Native doesn't have all features that JS has. - -delete spec.layer.type.values.heatmap; diff --git a/src/mbgl/gl/color_mode.hpp b/src/mbgl/gl/color_mode.hpp index c6594a3a77..e394f43501 100644 --- a/src/mbgl/gl/color_mode.hpp +++ b/src/mbgl/gl/color_mode.hpp @@ -85,6 +85,10 @@ public: static ColorMode alphaBlended() { return ColorMode { Add { One, OneMinusSrcAlpha }, {}, { true, true, true, true } }; } + + static ColorMode additive() { + return ColorMode { Add { One, One }, {}, { true, true, true, true } }; + } }; constexpr bool operator!=(const ColorMode::Mask& a, const ColorMode::Mask& b) { diff --git a/src/mbgl/gl/context.cpp b/src/mbgl/gl/context.cpp index b6244d58c7..f40cfa1f2c 100644 --- a/src/mbgl/gl/context.cpp +++ b/src/mbgl/gl/context.cpp @@ -60,6 +60,16 @@ static_assert(std::is_same, GLenum>::value static_assert(underlying_type(TextureFormat::RGBA) == GL_RGBA, "OpenGL type mismatch"); static_assert(underlying_type(TextureFormat::Alpha) == GL_ALPHA, "OpenGL type mismatch"); +static_assert(std::is_same, GLenum>::value, "OpenGL type mismatch"); +static_assert(underlying_type(TextureType::UnsignedByte) == GL_UNSIGNED_BYTE, "OpenGL type mismatch"); + +#if MBGL_USE_GLES2 && GL_HALF_FLOAT_OES +static_assert(underlying_type(TextureType::HalfFloat) == GL_HALF_FLOAT_OES, "OpenGL type mismatch"); +#endif +#if !MBGL_USE_GLES2 && GL_HALF_FLOAT_ARB +static_assert(underlying_type(TextureType::HalfFloat) == GL_HALF_FLOAT_ARB, "OpenGL type mismatch"); +#endif + static_assert(underlying_type(UniformDataType::Float) == GL_FLOAT, "OpenGL type mismatch"); static_assert(underlying_type(UniformDataType::FloatVec2) == GL_FLOAT_VEC2, "OpenGL type mismatch"); static_assert(underlying_type(UniformDataType::FloatVec3) == GL_FLOAT_VEC3, "OpenGL type mismatch"); @@ -116,6 +126,19 @@ void Context::initializeExtensions(const std::function(fn); #endif +#if MBGL_USE_GLES2 + constexpr const char* halfFloatExtensionName = "OES_texture_half_float"; + constexpr const char* halfFloatColorBufferExtensionName = "EXT_color_buffer_half_float"; +#else + constexpr const char* halfFloatExtensionName = "ARB_half_float_pixel"; + constexpr const char* halfFloatColorBufferExtensionName = "ARB_color_buffer_float"; +#endif + if (strstr(extensions, halfFloatExtensionName) != nullptr && + strstr(extensions, halfFloatColorBufferExtensionName) != nullptr) { + + supportsHalfFloatTextures = true; + } + if (!supportsVertexArrays()) { Log::Warning(Event::OpenGL, "Not using Vertex Array Objects"); } @@ -491,10 +514,10 @@ Context::createFramebuffer(const Texture& color, } UniqueTexture -Context::createTexture(const Size size, const void* data, TextureFormat format, TextureUnit unit) { +Context::createTexture(const Size size, const void* data, TextureFormat format, TextureUnit unit, TextureType type) { auto obj = createTexture(); pixelStoreUnpack = { 1 }; - updateTexture(obj, size, data, format, unit); + updateTexture(obj, size, data, format, unit, type); // We are using clamp to edge here since OpenGL ES doesn't allow GL_REPEAT on NPOT textures. // We use those when the pixelRatio isn't a power of two, e.g. on iPhone 6 Plus. MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); @@ -505,11 +528,11 @@ Context::createTexture(const Size size, const void* data, TextureFormat format, } void Context::updateTexture( - TextureID id, const Size size, const void* data, TextureFormat format, TextureUnit unit) { + TextureID id, const Size size, const void* data, TextureFormat format, TextureUnit unit, TextureType type) { activeTextureUnit = unit; texture[unit] = id; MBGL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, static_cast(format), size.width, - size.height, 0, static_cast(format), GL_UNSIGNED_BYTE, + size.height, 0, static_cast(format), static_cast(type), data)); } diff --git a/src/mbgl/gl/context.hpp b/src/mbgl/gl/context.hpp index 14f078367f..67624288e2 100644 --- a/src/mbgl/gl/context.hpp +++ b/src/mbgl/gl/context.hpp @@ -125,23 +125,29 @@ public: // Create a texture from an image with data. template - Texture createTexture(const Image& image, TextureUnit unit = 0) { + Texture createTexture(const Image& image, + TextureUnit unit = 0, + TextureType type = TextureType::UnsignedByte) { auto format = image.channels == 4 ? TextureFormat::RGBA : TextureFormat::Alpha; - return { image.size, createTexture(image.size, image.data.get(), format, unit) }; + return { image.size, createTexture(image.size, image.data.get(), format, unit, type) }; } template - void updateTexture(Texture& obj, const Image& image, TextureUnit unit = 0) { + void updateTexture(Texture& obj, + const Image& image, + TextureUnit unit = 0, + TextureType type = TextureType::UnsignedByte) { auto format = image.channels == 4 ? TextureFormat::RGBA : TextureFormat::Alpha; - updateTexture(obj.texture.get(), image.size, image.data.get(), format, unit); + updateTexture(obj.texture.get(), image.size, image.data.get(), format, unit, type); obj.size = image.size; } // Creates an empty texture with the specified dimensions. Texture createTexture(const Size size, TextureFormat format = TextureFormat::RGBA, - TextureUnit unit = 0) { - return { size, createTexture(size, nullptr, format, unit) }; + TextureUnit unit = 0, + TextureType type = TextureType::UnsignedByte) { + return { size, createTexture(size, nullptr, format, unit, type) }; } void bindTexture(Texture&, @@ -232,6 +238,8 @@ public: State pixelTransferStencil; #endif // MBGL_USE_GLES2 + bool supportsHalfFloatTextures = false; + private: State stencilFunc; State stencilMask; @@ -259,8 +267,8 @@ private: void updateVertexBuffer(UniqueBuffer& buffer, const void* data, std::size_t size); UniqueBuffer createIndexBuffer(const void* data, std::size_t size, const BufferUsage usage); void updateIndexBuffer(UniqueBuffer& buffer, const void* data, std::size_t size); - UniqueTexture createTexture(Size size, const void* data, TextureFormat, TextureUnit); - void updateTexture(TextureID, Size size, const void* data, TextureFormat, TextureUnit); + UniqueTexture createTexture(Size size, const void* data, TextureFormat, TextureUnit, TextureType); + void updateTexture(TextureID, Size size, const void* data, TextureFormat, TextureUnit, TextureType); UniqueFramebuffer createFramebuffer(); UniqueRenderbuffer createRenderbuffer(RenderbufferType, Size size); std::unique_ptr readFramebuffer(Size, TextureFormat, bool flip); diff --git a/src/mbgl/gl/types.hpp b/src/mbgl/gl/types.hpp index da08195e58..376a784a0c 100644 --- a/src/mbgl/gl/types.hpp +++ b/src/mbgl/gl/types.hpp @@ -64,6 +64,15 @@ enum class TextureFormat : uint32_t { #endif // MBGL_USE_GLES2 }; +enum class TextureType : uint32_t { + UnsignedByte = 0x1401, +#if MBGL_USE_GLES2 + HalfFloat = 0x8D61, +#else + HalfFloat = 0x140B, +#endif // MBGL_USE_GLES2 +}; + enum class PrimitiveType { Points = 0x0000, Lines = 0x0001, diff --git a/src/mbgl/programs/attributes.hpp b/src/mbgl/programs/attributes.hpp index 5d7a6474cf..c677c84d5d 100644 --- a/src/mbgl/programs/attributes.hpp +++ b/src/mbgl/programs/attributes.hpp @@ -30,7 +30,7 @@ MBGL_DEFINE_ATTRIBUTE(int16_t, 2, a_anchor_pos); MBGL_DEFINE_ATTRIBUTE(uint16_t, 2, a_texture_pos); MBGL_DEFINE_ATTRIBUTE(int16_t, 4, a_normal_ed); MBGL_DEFINE_ATTRIBUTE(uint8_t, 1, a_fade_opacity); -MBGL_DEFINE_ATTRIBUTE(uint8_t, 2, a_placed); +MBGL_DEFINE_ATTRIBUTE(uint8_t, 2, a_placed); template struct a_data { @@ -142,6 +142,11 @@ struct a_halo_blur { using Type = gl::Attribute; }; +struct a_weight { + static auto name() { return "a_weight"; } + using Type = gl::Attribute; +}; + } // namespace attributes struct PositionOnlyLayoutAttributes : gl::Attributes< diff --git a/src/mbgl/programs/heatmap_program.cpp b/src/mbgl/programs/heatmap_program.cpp new file mode 100644 index 0000000000..67f84fbd52 --- /dev/null +++ b/src/mbgl/programs/heatmap_program.cpp @@ -0,0 +1,7 @@ +#include + +namespace mbgl { + +static_assert(sizeof(HeatmapLayoutVertex) == 4, "expected HeatmapLayoutVertex size"); + +} // namespace mbgl diff --git a/src/mbgl/programs/heatmap_program.hpp b/src/mbgl/programs/heatmap_program.hpp new file mode 100644 index 0000000000..2d9b80404f --- /dev/null +++ b/src/mbgl/programs/heatmap_program.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace mbgl { + +namespace uniforms { +MBGL_DEFINE_UNIFORM_SCALAR(float, u_intensity); +} // namespace uniforms + +class HeatmapProgram : public Program< + shaders::heatmap, + gl::Triangle, + gl::Attributes< + attributes::a_pos>, + gl::Uniforms< + uniforms::u_intensity, + uniforms::u_matrix, + uniforms::heatmap::u_extrude_scale>, + style::HeatmapPaintProperties> +{ +public: + using Program::Program; + + /* + * @param {number} x vertex position + * @param {number} y vertex position + * @param {number} ex extrude normal + * @param {number} ey extrude normal + */ + static LayoutVertex vertex(Point p, float ex, float ey) { + return LayoutVertex { + {{ + static_cast((p.x * 2) + ((ex + 1) / 2)), + static_cast((p.y * 2) + ((ey + 1) / 2)) + }} + }; + } +}; + +using HeatmapLayoutVertex = HeatmapProgram::LayoutVertex; +using HeatmapAttributes = HeatmapProgram::Attributes; + +} // namespace mbgl diff --git a/src/mbgl/programs/heatmap_texture_program.cpp b/src/mbgl/programs/heatmap_texture_program.cpp new file mode 100644 index 0000000000..3b0e24eab8 --- /dev/null +++ b/src/mbgl/programs/heatmap_texture_program.cpp @@ -0,0 +1,7 @@ +#include + +namespace mbgl { + +static_assert(sizeof(HeatmapTextureLayoutVertex) == 4, "expected HeatmapTextureLayoutVertex size"); + +} // namespace mbgl diff --git a/src/mbgl/programs/heatmap_texture_program.hpp b/src/mbgl/programs/heatmap_texture_program.hpp new file mode 100644 index 0000000000..7afe8060d0 --- /dev/null +++ b/src/mbgl/programs/heatmap_texture_program.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace mbgl { + +namespace uniforms { +MBGL_DEFINE_UNIFORM_SCALAR(gl::TextureUnit, u_color_ramp); +} // namespace uniforms + +class HeatmapTextureProgram : public Program< + shaders::heatmap_texture, + gl::Triangle, + gl::Attributes, + gl::Uniforms< + uniforms::u_matrix, + uniforms::u_world, + uniforms::u_image, + uniforms::u_color_ramp, + uniforms::u_opacity>, + style::Properties<>> { +public: + using Program::Program; + + static LayoutVertex layoutVertex(Point p) { + return LayoutVertex{ + {{ + p.x, + p.y + }} + }; + } +}; + +using HeatmapTextureLayoutVertex = HeatmapTextureProgram::LayoutVertex; +using HeatmapTextureAttributes = HeatmapTextureProgram::Attributes; + +} // namespace mbgl diff --git a/src/mbgl/programs/programs.hpp b/src/mbgl/programs/programs.hpp index f533a6f633..b703323d9c 100644 --- a/src/mbgl/programs/programs.hpp +++ b/src/mbgl/programs/programs.hpp @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include #include #include @@ -30,6 +32,8 @@ public: fillPattern(context, programParameters), fillOutline(context, programParameters), fillOutlinePattern(context, programParameters), + heatmap(context, programParameters), + heatmapTexture(context, programParameters), hillshade(context, programParameters), hillshadePrepare(context, programParameters), line(context, programParameters), @@ -55,6 +59,8 @@ public: ProgramMap fillPattern; ProgramMap fillOutline; ProgramMap fillOutlinePattern; + ProgramMap heatmap; + HeatmapTextureProgram heatmapTexture; HillshadeProgram hillshade; HillshadePrepareProgram hillshadePrepare; ProgramMap line; diff --git a/src/mbgl/programs/uniforms.hpp b/src/mbgl/programs/uniforms.hpp index 107c084918..071a27c808 100644 --- a/src/mbgl/programs/uniforms.hpp +++ b/src/mbgl/programs/uniforms.hpp @@ -37,9 +37,14 @@ MBGL_DEFINE_UNIFORM_SCALAR(Size, u_texsize); MBGL_DEFINE_UNIFORM_SCALAR(bool, u_pitch_with_map); MBGL_DEFINE_UNIFORM_SCALAR(float, u_camera_to_center_distance); MBGL_DEFINE_UNIFORM_SCALAR(float, u_fade_change); +MBGL_DEFINE_UNIFORM_SCALAR(float, u_weight); MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_extrude_scale); +namespace heatmap { +MBGL_DEFINE_UNIFORM_SCALAR(float, u_extrude_scale); +} // namespace heatmap + MBGL_DEFINE_UNIFORM_VECTOR(uint16_t, 2, u_pattern_tl_a); MBGL_DEFINE_UNIFORM_VECTOR(uint16_t, 2, u_pattern_br_a); MBGL_DEFINE_UNIFORM_VECTOR(uint16_t, 2, u_pattern_tl_b); diff --git a/src/mbgl/renderer/buckets/heatmap_bucket.cpp b/src/mbgl/renderer/buckets/heatmap_bucket.cpp new file mode 100644 index 0000000000..a185e04ad2 --- /dev/null +++ b/src/mbgl/renderer/buckets/heatmap_bucket.cpp @@ -0,0 +1,98 @@ +#include +#include +#include +#include +#include +#include +#include + +namespace mbgl { + +using namespace style; + +HeatmapBucket::HeatmapBucket(const BucketParameters& parameters, const std::vector& layers) + : mode(parameters.mode) { + for (const auto& layer : layers) { + paintPropertyBinders.emplace( + std::piecewise_construct, + std::forward_as_tuple(layer->getID()), + std::forward_as_tuple( + layer->as()->evaluated, + parameters.tileID.overscaledZ)); + } +} + +void HeatmapBucket::upload(gl::Context& context) { + vertexBuffer = context.createVertexBuffer(std::move(vertices)); + indexBuffer = context.createIndexBuffer(std::move(triangles)); + + for (auto& pair : paintPropertyBinders) { + pair.second.upload(context); + } + + uploaded = true; +} + +bool HeatmapBucket::hasData() const { + return !segments.empty(); +} + +void HeatmapBucket::addFeature(const GeometryTileFeature& feature, + const GeometryCollection& geometry) { + constexpr const uint16_t vertexLength = 4; + + for (auto& points : geometry) { + for(auto& point : points) { + auto x = point.x; + auto y = point.y; + + // Do not include points that are outside the tile boundaries. + // Include all points in Still mode. You need to include points from + // neighbouring tiles so that they are not clipped at tile boundaries. + if ((mode == MapMode::Continuous) && + (x < 0 || x >= util::EXTENT || y < 0 || y >= util::EXTENT)) continue; + + if (segments.empty() || segments.back().vertexLength + vertexLength > std::numeric_limits::max()) { + // Move to a new segments because the old one can't hold the geometry. + segments.emplace_back(vertices.vertexSize(), triangles.indexSize()); + } + + // this geometry will be of the Point type, and we'll derive + // two triangles from it. + // + // ┌─────────┐ + // │ 4 3 │ + // │ │ + // │ 1 2 │ + // └─────────┘ + // + vertices.emplace_back(HeatmapProgram::vertex(point, -1, -1)); // 1 + vertices.emplace_back(HeatmapProgram::vertex(point, 1, -1)); // 2 + vertices.emplace_back(HeatmapProgram::vertex(point, 1, 1)); // 3 + vertices.emplace_back(HeatmapProgram::vertex(point, -1, 1)); // 4 + + auto& segment = segments.back(); + assert(segment.vertexLength <= std::numeric_limits::max()); + uint16_t index = segment.vertexLength; + + // 1, 2, 3 + // 1, 4, 3 + triangles.emplace_back(index, index + 1, index + 2); + triangles.emplace_back(index, index + 3, index + 2); + + segment.vertexLength += vertexLength; + segment.indexLength += 6; + } + } + + for (auto& pair : paintPropertyBinders) { + pair.second.populateVertexVectors(feature, vertices.vertexSize()); + } +} + +float HeatmapBucket::getQueryRadius(const RenderLayer& layer) const { + (void)layer; + return 0; +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/buckets/heatmap_bucket.hpp b/src/mbgl/renderer/buckets/heatmap_bucket.hpp new file mode 100644 index 0000000000..3b9f1edb81 --- /dev/null +++ b/src/mbgl/renderer/buckets/heatmap_bucket.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mbgl { + +class BucketParameters; + +class HeatmapBucket : public Bucket { +public: + HeatmapBucket(const BucketParameters&, const std::vector&); + + void addFeature(const GeometryTileFeature&, + const GeometryCollection&) override; + bool hasData() const override; + + void upload(gl::Context&) override; + + float getQueryRadius(const RenderLayer&) const override; + + gl::VertexVector vertices; + gl::IndexVector triangles; + SegmentVector segments; + + optional> vertexBuffer; + optional> indexBuffer; + + std::map paintPropertyBinders; + + const MapMode mode; +}; + +} // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_heatmap_layer.cpp b/src/mbgl/renderer/layers/render_heatmap_layer.cpp new file mode 100644 index 0000000000..0f9e3239ef --- /dev/null +++ b/src/mbgl/renderer/layers/render_heatmap_layer.cpp @@ -0,0 +1,178 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mbgl { + +using namespace style; + +RenderHeatmapLayer::RenderHeatmapLayer(Immutable _impl) + : RenderLayer(style::LayerType::Heatmap, _impl), + unevaluated(impl().paint.untransitioned()), colorRamp({256, 1}) { +} + +const style::HeatmapLayer::Impl& RenderHeatmapLayer::impl() const { + return static_cast(*baseImpl); +} + +std::unique_ptr RenderHeatmapLayer::createBucket(const BucketParameters& parameters, const std::vector& layers) const { + return std::make_unique(parameters, layers); +} + +void RenderHeatmapLayer::transition(const TransitionParameters& parameters) { + unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated)); +} + +void RenderHeatmapLayer::evaluate(const PropertyEvaluationParameters& parameters) { + evaluated = unevaluated.evaluate(parameters); + + passes = (evaluated.get() > 0) + ? (RenderPass::Translucent | RenderPass::Pass3D) + : RenderPass::None; +} + +bool RenderHeatmapLayer::hasTransition() const { + return unevaluated.hasTransition(); +} + +void RenderHeatmapLayer::render(PaintParameters& parameters, RenderSource*) { + if (parameters.pass == RenderPass::Opaque) { + return; + } + + if (parameters.pass == RenderPass::Pass3D) { + const auto& viewportSize = parameters.staticData.backendSize; + const auto size = Size{viewportSize.width / 4, viewportSize.height / 4}; + + if (!renderTexture || renderTexture->getSize() != size) { + if (parameters.context.supportsHalfFloatTextures) { + renderTexture = OffscreenTexture(parameters.context, size, gl::TextureType::HalfFloat); + + try { + renderTexture->bind(); + } catch (const std::runtime_error& ex) { + // can't render to a half-float texture; falling back to unsigned byte one + renderTexture = nullopt; + parameters.context.supportsHalfFloatTextures = false; + } + } + + if (!renderTexture) { + renderTexture = OffscreenTexture(parameters.context, size, gl::TextureType::UnsignedByte); + renderTexture->bind(); + } + + } else { + renderTexture->bind(); + } + + if (!colorRampTexture) { + colorRampTexture = parameters.context.createTexture(colorRamp, 1, gl::TextureType::UnsignedByte); + } + + parameters.context.clear(Color{ 0.0f, 0.0f, 0.0f, 1.0f }, {}, {}); + + for (const RenderTile& tile : renderTiles) { + assert(dynamic_cast(tile.tile.getBucket(*baseImpl))); + HeatmapBucket& bucket = *reinterpret_cast(tile.tile.getBucket(*baseImpl)); + + const auto extrudeScale = tile.id.pixelsToTileUnits(1, parameters.state.getZoom()); + + const auto stencilMode = parameters.mapMode != MapMode::Continuous + ? parameters.stencilModeForClipping(tile.clip) + : gl::StencilMode::disabled(); + + parameters.programs.heatmap.get(evaluated).draw( + parameters.context, + gl::Triangles(), + parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), + stencilMode, + gl::ColorMode::additive(), + HeatmapProgram::UniformValues { + uniforms::u_intensity::Value{evaluated.get()}, + uniforms::u_matrix::Value{tile.matrix}, + uniforms::heatmap::u_extrude_scale::Value{extrudeScale} + }, + *bucket.vertexBuffer, + *bucket.indexBuffer, + bucket.segments, + bucket.paintPropertyBinders.at(getID()), + evaluated, + parameters.state.getZoom(), + getID() + ); + } + + } else if (parameters.pass == RenderPass::Translucent) { + parameters.context.bindTexture(renderTexture->getTexture(), 0, gl::TextureFilter::Linear); + parameters.context.bindTexture(*colorRampTexture, 1, gl::TextureFilter::Linear); + + const auto& size = parameters.staticData.backendSize; + + mat4 viewportMat; + matrix::ortho(viewportMat, 0, size.width, size.height, 0, 0, 1); + + const Properties<>::PossiblyEvaluated properties; + + parameters.programs.heatmapTexture.draw( + parameters.context, gl::Triangles(), gl::DepthMode::disabled(), + gl::StencilMode::disabled(), parameters.colorModeForRenderPass(), + HeatmapTextureProgram::UniformValues{ + uniforms::u_matrix::Value{ viewportMat }, uniforms::u_world::Value{ size }, + uniforms::u_image::Value{ 0 }, + uniforms::u_color_ramp::Value{ 1 }, + uniforms::u_opacity::Value{ evaluated.get() } }, + parameters.staticData.extrusionTextureVertexBuffer, + parameters.staticData.quadTriangleIndexBuffer, + parameters.staticData.extrusionTextureSegments, + HeatmapTextureProgram::PaintPropertyBinders{ properties, 0 }, properties, + parameters.state.getZoom(), getID()); + } +} + +void RenderHeatmapLayer::updateColorRamp() { + auto colorValue = unevaluated.get().getValue(); + if (colorValue.isUndefined()) { + colorValue = HeatmapLayer::getDefaultHeatmapColor(); + } + + const auto length = colorRamp.bytes(); + + for (uint32_t i = 0; i < length; i += 4) { + const auto color = colorValue.evaluate(static_cast(i) / length); + colorRamp.data[i + 0] = std::floor(color.r * 255); + colorRamp.data[i + 1] = std::floor(color.g * 255); + colorRamp.data[i + 2] = std::floor(color.b * 255); + colorRamp.data[i + 3] = std::floor(color.a * 255); + } + + if (colorRampTexture) { + colorRampTexture = nullopt; + } +} + +bool RenderHeatmapLayer::queryIntersectsFeature( + const GeometryCoordinates& queryGeometry, + const GeometryTileFeature& feature, + const float zoom, + const float bearing, + const float pixelsToTileUnits) const { + (void) queryGeometry; + (void) feature; + (void) zoom; + (void) bearing; + (void) pixelsToTileUnits; + return false; +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_heatmap_layer.hpp b/src/mbgl/renderer/layers/render_heatmap_layer.hpp new file mode 100644 index 0000000000..3f0b1f91b4 --- /dev/null +++ b/src/mbgl/renderer/layers/render_heatmap_layer.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace mbgl { + +class RenderHeatmapLayer: public RenderLayer { +public: + RenderHeatmapLayer(Immutable); + ~RenderHeatmapLayer() final = default; + + void transition(const TransitionParameters&) override; + void evaluate(const PropertyEvaluationParameters&) override; + bool hasTransition() const override; + void render(PaintParameters&, RenderSource*) override; + + bool queryIntersectsFeature( + const GeometryCoordinates&, + const GeometryTileFeature&, + const float, + const float, + const float) const override; + + void updateColorRamp(); + + std::unique_ptr createBucket(const BucketParameters&, const std::vector&) const override; + + // Paint properties + style::HeatmapPaintProperties::Unevaluated unevaluated; + style::HeatmapPaintProperties::PossiblyEvaluated evaluated; + + const style::HeatmapLayer::Impl& impl() const; + + PremultipliedImage colorRamp; + optional renderTexture; + optional colorRampTexture; +}; + +template <> +inline bool RenderLayer::is() const { + return type == style::LayerType::Heatmap; +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/render_layer.cpp b/src/mbgl/renderer/render_layer.cpp index 248905f834..bcdc175f14 100644 --- a/src/mbgl/renderer/render_layer.cpp +++ b/src/mbgl/renderer/render_layer.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -35,6 +36,8 @@ std::unique_ptr RenderLayer::create(Immutable impl) { return std::make_unique(staticImmutableCast(impl)); case LayerType::FillExtrusion: return std::make_unique(staticImmutableCast(impl)); + case LayerType::Heatmap: + return std::make_unique(staticImmutableCast(impl)); } // Not reachable, but placate GCC. diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp index 26a07bb093..2ac714e122 100644 --- a/src/mbgl/renderer/renderer_impl.cpp +++ b/src/mbgl/renderer/renderer_impl.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -185,6 +186,10 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) { if (layerAdded || layerChanged) { layer.transition(transitionParameters); + + if (layer.is()) { + layer.as()->updateColorRamp(); + } } if (layerAdded || layerChanged || zoomChanged || layer.hasTransition()) { @@ -290,7 +295,11 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) { RenderLayer* layer = getRenderLayer(layerImpl->id); assert(layer); - if (!parameters.staticData.has3D && (layer->is() || layer->is())) { + if (!parameters.staticData.has3D && ( + layer->is() || + layer->is() || + layer->is())) { + parameters.staticData.has3D = true; } diff --git a/src/mbgl/shaders/heatmap.cpp b/src/mbgl/shaders/heatmap.cpp new file mode 100644 index 0000000000..19927cfcc6 --- /dev/null +++ b/src/mbgl/shaders/heatmap.cpp @@ -0,0 +1,128 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#include + +namespace mbgl { +namespace shaders { + +const char* heatmap::name = "heatmap"; +const char* heatmap::vertexSource = R"MBGL_SHADER( + +#ifndef HAS_UNIFORM_u_weight +uniform lowp float a_weight_t; +attribute highp vec2 a_weight; +varying highp float weight; +#else +uniform highp float u_weight; +#endif + + +#ifndef HAS_UNIFORM_u_radius +uniform lowp float a_radius_t; +attribute mediump vec2 a_radius; +#else +uniform mediump float u_radius; +#endif + + +uniform mat4 u_matrix; +uniform float u_extrude_scale; +uniform float u_opacity; +uniform float u_intensity; + +attribute vec2 a_pos; + +varying vec2 v_extrude; + +// Effective "0" in the kernel density texture to adjust the kernel size to; +// this empirically chosen number minimizes artifacts on overlapping kernels +// for typical heatmap cases (assuming clustered source) +const highp float ZERO = 1.0 / 255.0 / 16.0; + +// Gaussian kernel coefficient: 1 / sqrt(2 * PI) +#define GAUSS_COEF 0.3989422804014327 + +void main(void) { + +#ifndef HAS_UNIFORM_u_weight + weight = unpack_mix_vec2(a_weight, a_weight_t); +#else + highp float weight = u_weight; +#endif + + +#ifndef HAS_UNIFORM_u_radius + mediump float radius = unpack_mix_vec2(a_radius, a_radius_t); +#else + mediump float radius = u_radius; +#endif + + + // unencode the extrusion vector that we snuck into the a_pos vector + vec2 unscaled_extrude = vec2(mod(a_pos, 2.0) * 2.0 - 1.0); + + // This 'extrude' comes in ranging from [-1, -1], to [1, 1]. We'll use + // it to produce the vertices of a square mesh framing the point feature + // we're adding to the kernel density texture. We'll also pass it as + // a varying, so that the fragment shader can determine the distance of + // each fragment from the point feature. + // Before we do so, we need to scale it up sufficiently so that the + // kernel falls effectively to zero at the edge of the mesh. + // That is, we want to know S such that + // weight * u_intensity * GAUSS_COEF * exp(-0.5 * 3.0^2 * S^2) == ZERO + // Which solves to: + // S = sqrt(-2.0 * log(ZERO / (weight * u_intensity * GAUSS_COEF))) / 3.0 + float S = sqrt(-2.0 * log(ZERO / weight / u_intensity / GAUSS_COEF)) / 3.0; + + // Pass the varying in units of radius + v_extrude = S * unscaled_extrude; + + // Scale by radius and the zoom-based scale factor to produce actual + // mesh position + vec2 extrude = v_extrude * radius * u_extrude_scale; + + // multiply a_pos by 0.5, since we had it * 2 in order to sneak + // in extrusion data + vec4 pos = vec4(floor(a_pos * 0.5) + extrude, 0, 1); + + gl_Position = u_matrix * pos; +} + +)MBGL_SHADER"; +const char* heatmap::fragmentSource = R"MBGL_SHADER( + +#ifndef HAS_UNIFORM_u_weight +varying highp float weight; +#else +uniform highp float u_weight; +#endif + + +uniform highp float u_intensity; +varying vec2 v_extrude; + +// Gaussian kernel coefficient: 1 / sqrt(2 * PI) +#define GAUSS_COEF 0.3989422804014327 + +void main() { + +#ifdef HAS_UNIFORM_u_weight + highp float weight = u_weight; +#endif + + + // Kernel density estimation with a Gaussian kernel of size 5x5 + float d = -0.5 * 3.0 * 3.0 * dot(v_extrude, v_extrude); + float val = weight * u_intensity * GAUSS_COEF * exp(d); + + gl_FragColor = vec4(val, 1.0, 1.0, 1.0); + +#ifdef OVERDRAW_INSPECTOR + gl_FragColor = vec4(1.0); +#endif +} + +)MBGL_SHADER"; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/heatmap.hpp b/src/mbgl/shaders/heatmap.hpp new file mode 100644 index 0000000000..a3c64db942 --- /dev/null +++ b/src/mbgl/shaders/heatmap.hpp @@ -0,0 +1,16 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#pragma once + +namespace mbgl { +namespace shaders { + +class heatmap { +public: + static const char* name; + static const char* vertexSource; + static const char* fragmentSource; +}; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/heatmap_texture.cpp b/src/mbgl/shaders/heatmap_texture.cpp new file mode 100644 index 0000000000..c5d35c48ae --- /dev/null +++ b/src/mbgl/shaders/heatmap_texture.cpp @@ -0,0 +1,42 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#include + +namespace mbgl { +namespace shaders { + +const char* heatmap_texture::name = "heatmap_texture"; +const char* heatmap_texture::vertexSource = R"MBGL_SHADER( +uniform mat4 u_matrix; +uniform vec2 u_world; +attribute vec2 a_pos; +varying vec2 v_pos; + +void main() { + gl_Position = u_matrix * vec4(a_pos * u_world, 0, 1); + + v_pos.x = a_pos.x; + v_pos.y = 1.0 - a_pos.y; +} + +)MBGL_SHADER"; +const char* heatmap_texture::fragmentSource = R"MBGL_SHADER( +uniform sampler2D u_image; +uniform sampler2D u_color_ramp; +uniform float u_opacity; +varying vec2 v_pos; + +void main() { + float t = texture2D(u_image, v_pos).r; + vec4 color = texture2D(u_color_ramp, vec2(t, 0.5)); + gl_FragColor = color * u_opacity; + +#ifdef OVERDRAW_INSPECTOR + gl_FragColor = vec4(0.0); +#endif +} + +)MBGL_SHADER"; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/heatmap_texture.hpp b/src/mbgl/shaders/heatmap_texture.hpp new file mode 100644 index 0000000000..c51dc6b178 --- /dev/null +++ b/src/mbgl/shaders/heatmap_texture.hpp @@ -0,0 +1,16 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#pragma once + +namespace mbgl { +namespace shaders { + +class heatmap_texture { +public: + static const char* name; + static const char* vertexSource; + static const char* fragmentSource; +}; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/style/conversion/layer.cpp b/src/mbgl/style/conversion/layer.cpp index ad6998341d..19472bc8d6 100644 --- a/src/mbgl/style/conversion/layer.cpp +++ b/src/mbgl/style/conversion/layer.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -165,6 +166,8 @@ optional> Converter>::operator()(c converted = convertVectorLayer(*id, value, error); } else if (*type == "raster") { converted = convertRasterLayer(*id, value, error); + } else if (*type == "heatmap") { + converted = convertVectorLayer(*id, value, error); } else if (*type == "hillshade") { converted = convertHillshadeLayer(*id, value, error); } else if (*type == "background") { diff --git a/src/mbgl/style/conversion/make_property_setters.hpp b/src/mbgl/style/conversion/make_property_setters.hpp index adfcc4dd61..25c8fdb1ca 100644 --- a/src/mbgl/style/conversion/make_property_setters.hpp +++ b/src/mbgl/style/conversion/make_property_setters.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -72,6 +73,7 @@ inline auto makeLayoutPropertySetters() { + return result; } @@ -166,6 +168,17 @@ inline auto makePaintPropertySetters() { result["circle-stroke-opacity"] = &setProperty, &CircleLayer::setCircleStrokeOpacity>; result["circle-stroke-opacity-transition"] = &setTransition; + result["heatmap-radius"] = &setProperty, &HeatmapLayer::setHeatmapRadius>; + result["heatmap-radius-transition"] = &setTransition; + result["heatmap-weight"] = &setProperty, &HeatmapLayer::setHeatmapWeight>; + result["heatmap-weight-transition"] = &setTransition; + result["heatmap-intensity"] = &setProperty, &HeatmapLayer::setHeatmapIntensity>; + result["heatmap-intensity-transition"] = &setTransition; + result["heatmap-color"] = &setProperty; + result["heatmap-color-transition"] = &setTransition; + result["heatmap-opacity"] = &setProperty, &HeatmapLayer::setHeatmapOpacity>; + result["heatmap-opacity-transition"] = &setTransition; + result["fill-extrusion-opacity"] = &setProperty, &FillExtrusionLayer::setFillExtrusionOpacity>; result["fill-extrusion-opacity-transition"] = &setTransition; result["fill-extrusion-color"] = &setProperty, &FillExtrusionLayer::setFillExtrusionColor>; diff --git a/src/mbgl/style/conversion/property_setter.hpp b/src/mbgl/style/conversion/property_setter.hpp index 9e382b9c38..e3716a18dc 100644 --- a/src/mbgl/style/conversion/property_setter.hpp +++ b/src/mbgl/style/conversion/property_setter.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include diff --git a/src/mbgl/style/layers/heatmap_layer.cpp b/src/mbgl/style/layers/heatmap_layer.cpp new file mode 100644 index 0000000000..4989ff15f1 --- /dev/null +++ b/src/mbgl/style/layers/heatmap_layer.cpp @@ -0,0 +1,239 @@ +// This file is generated. Edit scripts/generate-style-code.js, then run `make style-code`. + +#include +#include +#include +// for constructing default heatmap-color ramp expression from style JSON +#include +#include +#include + +namespace mbgl { +namespace style { + +HeatmapLayer::HeatmapLayer(const std::string& layerID, const std::string& sourceID) + : Layer(makeMutable(LayerType::Heatmap, layerID, sourceID)) { +} + +HeatmapLayer::HeatmapLayer(Immutable impl_) + : Layer(std::move(impl_)) { +} + +HeatmapLayer::~HeatmapLayer() = default; + +const HeatmapLayer::Impl& HeatmapLayer::impl() const { + return static_cast(*baseImpl); +} + +Mutable HeatmapLayer::mutableImpl() const { + return makeMutable(impl()); +} + +std::unique_ptr HeatmapLayer::cloneRef(const std::string& id_) const { + auto impl_ = mutableImpl(); + impl_->id = id_; + impl_->paint = HeatmapPaintProperties::Transitionable(); + return std::make_unique(std::move(impl_)); +} + +void HeatmapLayer::Impl::stringifyLayout(rapidjson::Writer&) const { +} + +// Source + +const std::string& HeatmapLayer::getSourceID() const { + return impl().source; +} + +void HeatmapLayer::setSourceLayer(const std::string& sourceLayer) { + auto impl_ = mutableImpl(); + impl_->sourceLayer = sourceLayer; + baseImpl = std::move(impl_); +} + +const std::string& HeatmapLayer::getSourceLayer() const { + return impl().sourceLayer; +} + +// Filter + +void HeatmapLayer::setFilter(const Filter& filter) { + auto impl_ = mutableImpl(); + impl_->filter = filter; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +const Filter& HeatmapLayer::getFilter() const { + return impl().filter; +} + +// Visibility + +void HeatmapLayer::setVisibility(VisibilityType value) { + if (value == getVisibility()) + return; + auto impl_ = mutableImpl(); + impl_->visibility = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +// Zoom range + +void HeatmapLayer::setMinZoom(float minZoom) { + auto impl_ = mutableImpl(); + impl_->minZoom = minZoom; + baseImpl = std::move(impl_); +} + +void HeatmapLayer::setMaxZoom(float maxZoom) { + auto impl_ = mutableImpl(); + impl_->maxZoom = maxZoom; + baseImpl = std::move(impl_); +} + +// Layout properties + + +// Paint properties + +DataDrivenPropertyValue HeatmapLayer::getDefaultHeatmapRadius() { + return { 30 }; +} + +DataDrivenPropertyValue HeatmapLayer::getHeatmapRadius() const { + return impl().paint.template get().value; +} + +void HeatmapLayer::setHeatmapRadius(DataDrivenPropertyValue value) { + if (value == getHeatmapRadius()) + return; + auto impl_ = mutableImpl(); + impl_->paint.template get().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +void HeatmapLayer::setHeatmapRadiusTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get().options = options; + baseImpl = std::move(impl_); +} + +TransitionOptions HeatmapLayer::getHeatmapRadiusTransition() const { + return impl().paint.template get().options; +} + +DataDrivenPropertyValue HeatmapLayer::getDefaultHeatmapWeight() { + return { 1 }; +} + +DataDrivenPropertyValue HeatmapLayer::getHeatmapWeight() const { + return impl().paint.template get().value; +} + +void HeatmapLayer::setHeatmapWeight(DataDrivenPropertyValue value) { + if (value == getHeatmapWeight()) + return; + auto impl_ = mutableImpl(); + impl_->paint.template get().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +void HeatmapLayer::setHeatmapWeightTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get().options = options; + baseImpl = std::move(impl_); +} + +TransitionOptions HeatmapLayer::getHeatmapWeightTransition() const { + return impl().paint.template get().options; +} + +PropertyValue HeatmapLayer::getDefaultHeatmapIntensity() { + return { 1 }; +} + +PropertyValue HeatmapLayer::getHeatmapIntensity() const { + return impl().paint.template get().value; +} + +void HeatmapLayer::setHeatmapIntensity(PropertyValue value) { + if (value == getHeatmapIntensity()) + return; + auto impl_ = mutableImpl(); + impl_->paint.template get().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +void HeatmapLayer::setHeatmapIntensityTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get().options = options; + baseImpl = std::move(impl_); +} + +TransitionOptions HeatmapLayer::getHeatmapIntensityTransition() const { + return impl().paint.template get().options; +} + +HeatmapColorPropertyValue HeatmapLayer::getDefaultHeatmapColor() { + conversion::Error error; + std::string rawValue = R"JSON(["interpolate",["linear"],["heatmap-density"],0,"rgba(0, 0, 255, 0)",0.1,"royalblue",0.3,"cyan",0.5,"lime",0.7,"yellow",1,"red"])JSON"; + return *conversion::convertJSON(rawValue, error); +} + +HeatmapColorPropertyValue HeatmapLayer::getHeatmapColor() const { + return impl().paint.template get().value; +} + +void HeatmapLayer::setHeatmapColor(HeatmapColorPropertyValue value) { + if (value == getHeatmapColor()) + return; + auto impl_ = mutableImpl(); + impl_->paint.template get().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +void HeatmapLayer::setHeatmapColorTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get().options = options; + baseImpl = std::move(impl_); +} + +TransitionOptions HeatmapLayer::getHeatmapColorTransition() const { + return impl().paint.template get().options; +} + +PropertyValue HeatmapLayer::getDefaultHeatmapOpacity() { + return { 1 }; +} + +PropertyValue HeatmapLayer::getHeatmapOpacity() const { + return impl().paint.template get().value; +} + +void HeatmapLayer::setHeatmapOpacity(PropertyValue value) { + if (value == getHeatmapOpacity()) + return; + auto impl_ = mutableImpl(); + impl_->paint.template get().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +void HeatmapLayer::setHeatmapOpacityTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get().options = options; + baseImpl = std::move(impl_); +} + +TransitionOptions HeatmapLayer::getHeatmapOpacityTransition() const { + return impl().paint.template get().options; +} + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/layers/heatmap_layer_impl.cpp b/src/mbgl/style/layers/heatmap_layer_impl.cpp new file mode 100644 index 0000000000..af20888d9d --- /dev/null +++ b/src/mbgl/style/layers/heatmap_layer_impl.cpp @@ -0,0 +1,15 @@ +#include + +namespace mbgl { +namespace style { + +bool HeatmapLayer::Impl::hasLayoutDifference(const Layer::Impl& other) const { + assert(dynamic_cast(&other)); + const auto& impl = static_cast(other); + return filter != impl.filter || + visibility != impl.visibility || + paint.hasDataDrivenPropertyDifference(impl.paint); +} + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/layers/heatmap_layer_impl.hpp b/src/mbgl/style/layers/heatmap_layer_impl.hpp new file mode 100644 index 0000000000..cc27c3076a --- /dev/null +++ b/src/mbgl/style/layers/heatmap_layer_impl.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include +#include +#include + +namespace mbgl { +namespace style { + +class HeatmapLayer::Impl : public Layer::Impl { +public: + using Layer::Impl::Impl; + + bool hasLayoutDifference(const Layer::Impl&) const override; + void stringifyLayout(rapidjson::Writer&) const override; + + HeatmapPaintProperties::Transitionable paint; +}; + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/layers/heatmap_layer_properties.cpp b/src/mbgl/style/layers/heatmap_layer_properties.cpp new file mode 100644 index 0000000000..2edb839589 --- /dev/null +++ b/src/mbgl/style/layers/heatmap_layer_properties.cpp @@ -0,0 +1,9 @@ +// This file is generated. Edit scripts/generate-style-code.js, then run `make style-code`. + +#include + +namespace mbgl { +namespace style { + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/layers/heatmap_layer_properties.hpp b/src/mbgl/style/layers/heatmap_layer_properties.hpp new file mode 100644 index 0000000000..f7afa5fbeb --- /dev/null +++ b/src/mbgl/style/layers/heatmap_layer_properties.hpp @@ -0,0 +1,40 @@ +// This file is generated. Edit scripts/generate-style-code.js, then run `make style-code`. + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace mbgl { +namespace style { + +struct HeatmapRadius : DataDrivenPaintProperty { + static float defaultValue() { return 30; } +}; + +struct HeatmapWeight : DataDrivenPaintProperty { + static float defaultValue() { return 1; } +}; + +struct HeatmapIntensity : PaintProperty { + static float defaultValue() { return 1; } +}; + +struct HeatmapOpacity : PaintProperty { + static float defaultValue() { return 1; } +}; + +class HeatmapPaintProperties : public Properties< + HeatmapRadius, + HeatmapWeight, + HeatmapIntensity, + HeatmapColor, + HeatmapOpacity +> {}; + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/layers/layer.cpp.ejs b/src/mbgl/style/layers/layer.cpp.ejs index be44bb353d..657a7f5a8a 100644 --- a/src/mbgl/style/layers/layer.cpp.ejs +++ b/src/mbgl/style/layers/layer.cpp.ejs @@ -8,6 +8,12 @@ #include _layer.hpp> #include _layer_impl.hpp> #include +<% if (type === 'heatmap') { -%> +// for constructing default heatmap-color ramp expression from style JSON +#include +#include +#include +<% } -%> namespace mbgl { namespace style { @@ -134,7 +140,13 @@ void <%- camelize(type) %>Layer::set<%- camelize(property.name) %>(<%- propertyV // Paint properties <% for (const property of paintProperties) { %> <%- propertyValueType(property) %> <%- camelize(type) %>Layer::getDefault<%- camelize(property.name) %>() { +<% if (property.name === 'heatmap-color') { -%> + conversion::Error error; + std::string rawValue = R"JSON(<%- JSON.stringify(property.default) %>)JSON"; + return *conversion::convertJSON<<%- propertyValueType(property)%>>(rawValue, error); +<% } else { -%> return { <%- defaultValue(property) %> }; +<% } -%> } <%- propertyValueType(property) %> <%- camelize(type) %>Layer::get<%- camelize(property.name) %>() const { diff --git a/src/mbgl/style/layers/layer_properties.hpp.ejs b/src/mbgl/style/layers/layer_properties.hpp.ejs index cde1b80b7b..1bceb84960 100644 --- a/src/mbgl/style/layers/layer_properties.hpp.ejs +++ b/src/mbgl/style/layers/layer_properties.hpp.ejs @@ -25,6 +25,7 @@ struct <%- camelize(property.name) %> : <%- layoutPropertyType(property, type) % <% } -%> <% for (const property of paintProperties) { -%> +<% if (property.name === 'heatmap-color') continue; -%> struct <%- camelize(property.name) %> : <%- paintPropertyType(property, type) %> { static <%- evaluatedType(property) %> defaultValue() { return <%- defaultValue(property) %>; } }; diff --git a/src/mbgl/style/paint_property.hpp b/src/mbgl/style/paint_property.hpp index c4c996b3bd..195eb645a9 100644 --- a/src/mbgl/style/paint_property.hpp +++ b/src/mbgl/style/paint_property.hpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -48,5 +49,27 @@ public: static constexpr bool IsDataDriven = false; }; +/* + * Special-case paint property traits for heatmap-color, needed because + * heatmap-color values do not fit into the + * Undefined | Value | {Camera,Source,Composite}Function taxonomy that applies + * to all other paint properties. + * + * These traits are provided here--despite the fact that heatmap-color + * is not used like other paint properties--to allow the parameter-pack-based + * batch evaluation of paint properties to compile properly. + */ +class HeatmapColor { +public: + using TransitionableType = Transitionable; + using UnevaluatedType = Transitioning; + using EvaluatorType = PropertyEvaluator; + using PossiblyEvaluatedType = Color; + using Type = Color; + static constexpr bool IsDataDriven = false; + + static Color defaultValue() { return {}; } +}; + } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/style_impl.cpp b/src/mbgl/style/style_impl.cpp index f2a9c0f222..d330b3120a 100644 --- a/src/mbgl/style/style_impl.cpp +++ b/src/mbgl/style/style_impl.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include diff --git a/src/mbgl/util/offscreen_texture.cpp b/src/mbgl/util/offscreen_texture.cpp index 339e74b250..03f555eae0 100644 --- a/src/mbgl/util/offscreen_texture.cpp +++ b/src/mbgl/util/offscreen_texture.cpp @@ -11,20 +11,21 @@ OffscreenTexture& OffscreenTexture::operator=(OffscreenTexture&&) = default; class OffscreenTexture::Impl { public: - Impl(gl::Context& context_, const Size size_) - : context(context_), size(std::move(size_)) { + Impl(gl::Context& context_, const Size size_, const gl::TextureType type_) + : context(context_), size(std::move(size_)), type(type_) { assert(!size.isEmpty()); } Impl(gl::Context& context_, const Size size_, - gl::Renderbuffer& depth_) - : context(context_), size(std::move(size_)), depth(&depth_) { + gl::Renderbuffer& depth_, + const gl::TextureType type_) + : context(context_), size(std::move(size_)), depth(&depth_), type(type_) { assert(!size.isEmpty()); } void bind() { if (!framebuffer) { - texture = context.createTexture(size, gl::TextureFormat::RGBA); + texture = context.createTexture(size, gl::TextureFormat::RGBA, 0, type); if (depth) { framebuffer = context.createFramebuffer(*texture, *depth); } else { @@ -58,18 +59,21 @@ private: optional framebuffer; optional texture; gl::Renderbuffer* depth = nullptr; + const gl::TextureType type; }; OffscreenTexture::OffscreenTexture(gl::Context& context, - const Size size) - : impl(std::make_unique(context, std::move(size))) { + const Size size, + const gl::TextureType type) + : impl(std::make_unique(context, std::move(size), type)) { assert(!size.isEmpty()); } OffscreenTexture::OffscreenTexture(gl::Context& context, const Size size, - gl::Renderbuffer& renderbuffer) - : impl(std::make_unique(context, std::move(size), renderbuffer)) { + gl::Renderbuffer& renderbuffer, + const gl::TextureType type) + : impl(std::make_unique(context, std::move(size), renderbuffer, type)) { assert(!size.isEmpty()); } diff --git a/src/mbgl/util/offscreen_texture.hpp b/src/mbgl/util/offscreen_texture.hpp index 7f7e0f0338..36f24f16d3 100644 --- a/src/mbgl/util/offscreen_texture.hpp +++ b/src/mbgl/util/offscreen_texture.hpp @@ -12,10 +12,12 @@ class Texture; class OffscreenTexture { public: OffscreenTexture(gl::Context&, - Size size = { 256, 256 }); + Size size = { 256, 256 }, + gl::TextureType type = gl::TextureType::UnsignedByte); OffscreenTexture(gl::Context&, Size size, - gl::Renderbuffer&); + gl::Renderbuffer&, + gl::TextureType type = gl::TextureType::UnsignedByte); ~OffscreenTexture(); OffscreenTexture(OffscreenTexture&&); OffscreenTexture& operator=(OffscreenTexture&&); -- cgit v1.2.1