summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLloyd Sheng <i@lloydsheng.com>2018-03-27 13:47:53 +0800
committerLloyd Sheng <i@lloydsheng.com>2018-03-27 13:47:53 +0800
commitf8039e524e964ccd61721722d4f37460d29d7e5f (patch)
tree25c62c7153199330b6c0a3cc97767ee1463e02a5
parentef9bac737fdb6a07ca3b0260459367c776fbffbb (diff)
parent4f196d48ddfc3bb05dee1cdd032af170a6c53d67 (diff)
downloadqtlocation-mapboxgl-upstream/llooyd.tar.gz
Merge branch 'release-boba' into llooydupstream/llooyd
-rw-r--r--Makefile12
-rw-r--r--circle.yml54
-rw-r--r--include/mbgl/style/data_driven_property_value.hpp9
-rw-r--r--include/mbgl/style/function/camera_function.hpp11
-rw-r--r--include/mbgl/style/function/composite_function.hpp13
-rw-r--r--include/mbgl/style/function/source_function.hpp15
m---------mapbox-gl-js0
-rw-r--r--platform/android/CHANGELOG.md31
-rw-r--r--platform/android/MapboxGLAndroidSDK/gradle.properties1
-rw-r--r--platform/android/MapboxGLAndroidSDK/proguard-rules.pro2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java7
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java90
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java5
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java12
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java6
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Telemetry.java (renamed from platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Events.java)22
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java98
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/BackgroundLayer.java3
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CircleLayer.java30
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillExtrusionLayer.java30
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillLayer.java30
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Filter.java230
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HeatmapLayer.java30
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HillshadeLayer.java3
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Layer.java3
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/LineLayer.java30
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/RasterLayer.java3
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/SymbolLayer.java30
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/layer.java.ejs30
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySource.java6
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java6
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/VectorSource.java6
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/expressions/ExpressionTest.java20
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/layers/FilterTest.java102
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/telemetry/HttpTransportTest.java6
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CircleLayerTest.java20
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillExtrusionLayerTest.java20
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillLayerTest.java20
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/HeatmapLayerTest.java20
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LightTest.java2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LineLayerTest.java20
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/StyleLoaderTest.java77
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java20
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/layer.junit.ejs20
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/light.junit.ejs2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapboxApplication.java3
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/GestureDetectorActivity.java14
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxHighlightActivity.java11
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QuerySourceFeaturesActivity.java13
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/BuildingFillExtrusionActivity.java6
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CircleLayerActivity.java8
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/DataDrivenStyleActivity.java10
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GeoJsonClusteringActivity.java23
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java23
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java14
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_gestures.xml2
-rw-r--r--platform/android/config.cmake2
-rw-r--r--platform/android/gradle/dependencies.gradle8
-rw-r--r--platform/android/src/file_source.cpp10
-rw-r--r--platform/android/src/geojson/conversion/feature.hpp2
-rw-r--r--platform/android/src/geojson/conversion/geometry.hpp184
-rw-r--r--platform/android/src/geojson/geometry.cpp47
-rw-r--r--platform/android/src/geojson/geometry.hpp8
-rw-r--r--platform/android/src/geojson/geometry_collection.cpp63
-rw-r--r--platform/android/src/geojson/geometry_collection.hpp28
-rw-r--r--platform/android/src/geojson/line_string.cpp13
-rw-r--r--platform/android/src/geojson/line_string.hpp10
-rw-r--r--platform/android/src/geojson/multi_line_string.cpp11
-rw-r--r--platform/android/src/geojson/multi_line_string.hpp9
-rw-r--r--platform/android/src/geojson/multi_point.cpp10
-rw-r--r--platform/android/src/geojson/multi_point.hpp10
-rw-r--r--platform/android/src/geojson/multi_polygon.cpp23
-rw-r--r--platform/android/src/geojson/multi_polygon.hpp9
-rw-r--r--platform/android/src/geojson/point.cpp5
-rw-r--r--platform/android/src/geojson/point.hpp9
-rw-r--r--platform/android/src/geojson/polygon.cpp10
-rw-r--r--platform/android/src/geojson/polygon.hpp8
-rw-r--r--platform/android/src/geojson/util.hpp38
-rw-r--r--platform/android/src/java/util.cpp2
-rw-r--r--platform/android/src/java/util.hpp15
-rwxr-xr-xplatform/android/src/jni.cpp2
-rw-r--r--platform/android/src/style/layers/layer.cpp36
-rw-r--r--platform/android/src/style/layers/layer.hpp4
-rw-r--r--platform/darwin/src/MGLPointAnnotation.h3
-rw-r--r--platform/darwin/src/MGLPointCollection.h3
-rw-r--r--platform/darwin/src/MGLPolygon.h3
-rw-r--r--platform/darwin/src/MGLPolyline.h3
-rw-r--r--platform/darwin/src/MGLShape.h3
-rw-r--r--platform/darwin/src/MGLShapeCollection.h3
-rw-r--r--platform/darwin/src/NSExpression+MGLAdditions.mm96
-rw-r--r--platform/darwin/src/NSExpression+MGLPrivateAdditions.h2
-rw-r--r--platform/darwin/src/NSPredicate+MGLAdditions.h4
-rw-r--r--platform/darwin/src/NSPredicate+MGLAdditions.mm31
-rw-r--r--platform/darwin/test/MGLExpressionTests.mm74
-rw-r--r--platform/ios/CHANGELOG.md2
-rw-r--r--platform/ios/app/MBXCustomCalloutView.m7
-rw-r--r--platform/ios/app/MBXViewController.m93
-rw-r--r--platform/ios/src/MGLCalloutView.h25
-rw-r--r--platform/ios/src/MGLMapView.h23
-rw-r--r--platform/ios/src/MGLMapView.mm101
-rw-r--r--platform/ios/test/MGLAnnotationViewTests.m63
-rwxr-xr-xplatform/ios/vendor/SMCalloutView/SMCalloutView.h12
-rwxr-xr-xplatform/ios/vendor/SMCalloutView/SMCalloutView.m43
-rw-r--r--platform/macos/CHANGELOG.md2
-rw-r--r--platform/macos/app/Base.lproj/MainMenu.xib18
-rw-r--r--platform/macos/app/MapDocument.m48
-rw-r--r--platform/macos/src/MGLMapView.h15
-rw-r--r--platform/macos/src/MGLMapView.mm99
-rw-r--r--platform/node/test/ignores.json2
-rw-r--r--src/mbgl/layout/symbol_layout.cpp4
-rw-r--r--src/mbgl/programs/symbol_program.hpp4
-rw-r--r--src/mbgl/shaders/collision_box.cpp5
-rw-r--r--src/mbgl/shaders/symbol_icon.cpp7
-rw-r--r--src/mbgl/shaders/symbol_sdf.cpp7
-rw-r--r--src/mbgl/text/placement.cpp6
117 files changed, 1837 insertions, 892 deletions
diff --git a/Makefile b/Makefile
index 862201d8d1..5c521d674a 100644
--- a/Makefile
+++ b/Makefile
@@ -45,7 +45,7 @@ ifeq ($(V), 1)
export XCPRETTY
NINJA_ARGS ?= -v
else
- export XCPRETTY ?= | xcpretty
+ export XCPRETTY ?= | tee '$(shell pwd)/build/xcodebuild-$(shell date +"%Y-%m-%d_%H%M%S").log' | xcpretty
NINJA_ARGS ?=
endif
@@ -235,13 +235,17 @@ ios-test: $(IOS_PROJ_PATH)
ios-integration-test: $(IOS_PROJ_PATH)
set -o pipefail && $(IOS_XCODEBUILD_SIM) -scheme 'Integration Test Harness' test $(XCPRETTY)
+.PHONY: ios-sanitize
+ios-sanitize: $(IOS_PROJ_PATH)
+ set -o pipefail && $(IOS_XCODEBUILD_SIM) -scheme 'CI' -enableThreadSanitizer YES -enableUndefinedBehaviorSanitizer YES test $(XCPRETTY)
+
.PHONY: ios-sanitize-address
ios-sanitize-address: $(IOS_PROJ_PATH)
set -o pipefail && $(IOS_XCODEBUILD_SIM) -scheme 'CI' -enableAddressSanitizer YES test $(XCPRETTY)
-.PHONY: ios-sanitize-thread
-ios-sanitize-thread: $(IOS_PROJ_PATH)
- set -o pipefail && $(IOS_XCODEBUILD_SIM) -scheme 'CI' -enableThreadSanitizer YES test $(XCPRETTY)
+.PHONY: ios-static-analyzer
+ios-static-analyzer: $(IOS_PROJ_PATH)
+ set -o pipefail && $(IOS_XCODEBUILD_SIM) analyze -scheme 'CI' test $(XCPRETTY)
.PHONY: ipackage
ipackage: $(IOS_PROJ_PATH)
diff --git a/circle.yml b/circle.yml
index 3f67f8f6e6..789f6b0406 100644
--- a/circle.yml
+++ b/circle.yml
@@ -32,8 +32,9 @@ workflows:
- linux-gcc5-release-qt4
- linux-gcc5-release-qt5
- ios-debug
+ - ios-sanitize
#- ios-sanitize-address
- - ios-sanitize-thread
+ - ios-static-analyzer
- ios-release:
filters:
tags:
@@ -223,6 +224,18 @@ step-library:
path: mapbox-gl-js/test/integration/render-tests/index-recycle-map.html
destination: render-tests
+ - &collect-xcode-build-logs
+ run:
+ name: Collect Xcode build logs
+ when: always
+ command: |
+ export XCODE_LOG_DIR=build/logs
+ mkdir -p $XCODE_LOG_DIR
+ cp build/*.log $XCODE_LOG_DIR
+ - &upload-xcode-build-logs
+ store_artifacts:
+ path: build/logs
+
jobs:
nitpick:
docker:
@@ -730,6 +743,29 @@ jobs:
command: scripts/nitpick/generated-code.js darwin
- *show-ccache-stats
- *save-cache
+ - *collect-xcode-build-logs
+ - *upload-xcode-build-logs
+
+# ------------------------------------------------------------------------------
+ ios-sanitize:
+ macos:
+ xcode: "9.2.0"
+ environment:
+ BUILDTYPE: Debug
+ HOMEBREW_NO_AUTO_UPDATE: 1
+ steps:
+ - checkout
+ - *install-macos-dependencies
+ - *generate-cache-key
+ - *restore-cache
+ - *reset-ccache-stats
+ - run:
+ name: Build and run SDK unit tests with thread and undefined behavior sanitizers
+ command: make ios-sanitize
+ - *show-ccache-stats
+ - *save-cache
+ - *collect-xcode-build-logs
+ - *upload-xcode-build-logs
# ------------------------------------------------------------------------------
ios-sanitize-address:
@@ -749,9 +785,11 @@ jobs:
command: make ios-sanitize-address
- *show-ccache-stats
- *save-cache
+ - *collect-xcode-build-logs
+ - *upload-xcode-build-logs
# ------------------------------------------------------------------------------
- ios-sanitize-thread:
+ ios-static-analyzer:
macos:
xcode: "9.2.0"
environment:
@@ -764,10 +802,12 @@ jobs:
- *restore-cache
- *reset-ccache-stats
- run:
- name: Build and run SDK unit tests with thread sanitizer
- command: make ios-sanitize-thread
+ name: Build and run SDK unit tests with the static analyzer
+ command: make ios-static-analyzer
- *show-ccache-stats
- *save-cache
+ - *collect-xcode-build-logs
+ - *upload-xcode-build-logs
# ------------------------------------------------------------------------------
ios-release:
@@ -823,6 +863,8 @@ jobs:
- store_artifacts:
path: test/fixtures
destination: test/fixtures
+ - *collect-xcode-build-logs
+ - *upload-xcode-build-logs
# ------------------------------------------------------------------------------
macos-debug-qt5:
@@ -869,6 +911,8 @@ jobs:
- *run-node-macos-tests
- *publish-node-package
- *upload-render-tests
+ - *collect-xcode-build-logs
+ - *upload-xcode-build-logs
# ------------------------------------------------------------------------------
macos-release-node6:
@@ -890,3 +934,5 @@ jobs:
- *run-node-macos-tests
- *publish-node-package
- *upload-render-tests
+ - *collect-xcode-build-logs
+ - *upload-xcode-build-logs
diff --git a/include/mbgl/style/data_driven_property_value.hpp b/include/mbgl/style/data_driven_property_value.hpp
index 5d7c596363..0a1fce29c7 100644
--- a/include/mbgl/style/data_driven_property_value.hpp
+++ b/include/mbgl/style/data_driven_property_value.hpp
@@ -50,6 +50,15 @@ public:
return !value.template is<CameraFunction<T>>() && !value.template is<CompositeFunction<T>>();
}
+ bool isExpression() const {
+ return value.match(
+ [] (const Undefined&) { return false; },
+ [] (const T&) { return false; },
+ [] (const CameraFunction<T>& fn) { return fn.isExpression; },
+ [] (const SourceFunction<T>& fn) { return fn.isExpression; },
+ [] (const CompositeFunction<T>& fn) { return fn.isExpression; });
+ }
+
template <class... Ts>
auto match(Ts&&... ts) const {
return value.match(std::forward<Ts>(ts)...);
diff --git a/include/mbgl/style/function/camera_function.hpp b/include/mbgl/style/function/camera_function.hpp
index 1da5d2c601..97ba633e44 100644
--- a/include/mbgl/style/function/camera_function.hpp
+++ b/include/mbgl/style/function/camera_function.hpp
@@ -27,15 +27,16 @@ public:
IntervalStops<T>>>;
CameraFunction(std::unique_ptr<expression::Expression> expression_)
- : expression(std::move(expression_)),
+ : isExpression(true),
+ expression(std::move(expression_)),
zoomCurve(expression::findZoomCurveChecked(expression.get()))
{
assert(!expression::isZoomConstant(*expression));
assert(expression::isFeatureConstant(*expression));
}
- CameraFunction(Stops stops_)
- : stops(std::move(stops_)),
+ CameraFunction(const Stops& stops)
+ : isExpression(false),
expression(stops.match([&] (const auto& s) {
return expression::Convert::toExpression(s);
})),
@@ -76,12 +77,10 @@ public:
}
bool useIntegerZoom = false;
+ bool isExpression;
const expression::Expression& getExpression() const { return *expression; }
- // retained for compatibility with pre-expression function API
- Stops stops;
-
private:
std::shared_ptr<expression::Expression> expression;
const variant<const expression::InterpolateBase*, const expression::Step*> zoomCurve;
diff --git a/include/mbgl/style/function/composite_function.hpp b/include/mbgl/style/function/composite_function.hpp
index f391b101ae..614c345c25 100644
--- a/include/mbgl/style/function/composite_function.hpp
+++ b/include/mbgl/style/function/composite_function.hpp
@@ -51,16 +51,16 @@ public:
CompositeCategoricalStops<T>>>;
CompositeFunction(std::unique_ptr<expression::Expression> expression_)
- : expression(std::move(expression_)),
+ : isExpression(true),
+ expression(std::move(expression_)),
zoomCurve(expression::findZoomCurveChecked(expression.get()))
{
assert(!expression::isZoomConstant(*expression));
assert(!expression::isFeatureConstant(*expression));
}
- CompositeFunction(std::string property_, Stops stops_, optional<T> defaultValue_ = {})
- : property(std::move(property_)),
- stops(std::move(stops_)),
+ CompositeFunction(const std::string& property, const Stops& stops, optional<T> defaultValue_ = {})
+ : isExpression(false),
defaultValue(std::move(defaultValue_)),
expression(stops.match([&] (const auto& s) {
return expression::Convert::toExpression(property, s);
@@ -113,12 +113,11 @@ public:
const expression::Expression& getExpression() const { return *expression; }
- std::string property;
- Stops stops;
- optional<T> defaultValue;
bool useIntegerZoom = false;
+ bool isExpression;
private:
+ optional<T> defaultValue;
std::shared_ptr<expression::Expression> expression;
const variant<const expression::InterpolateBase*, const expression::Step*> zoomCurve;
};
diff --git a/include/mbgl/style/function/source_function.hpp b/include/mbgl/style/function/source_function.hpp
index d3caa90ee5..5b51d0bf81 100644
--- a/include/mbgl/style/function/source_function.hpp
+++ b/include/mbgl/style/function/source_function.hpp
@@ -30,15 +30,15 @@ public:
IdentityStops<T>>>;
SourceFunction(std::unique_ptr<expression::Expression> expression_)
- : expression(std::move(expression_))
+ : isExpression(true),
+ expression(std::move(expression_))
{
assert(expression::isZoomConstant(*expression));
assert(!expression::isFeatureConstant(*expression));
}
- SourceFunction(std::string property_, Stops stops_, optional<T> defaultValue_ = {})
- : property(std::move(property_)),
- stops(std::move(stops_)),
+ SourceFunction(const std::string& property, const Stops& stops, optional<T> defaultValue_ = {})
+ : isExpression(false),
defaultValue(std::move(defaultValue_)),
expression(stops.match([&] (const IdentityStops<T>&) {
return expression::Convert::fromIdentityFunction(expression::valueTypeToExpressionType<T>(), property);
@@ -67,15 +67,12 @@ public:
}
bool useIntegerZoom = false;
+ bool isExpression;
const expression::Expression& getExpression() const { return *expression; }
- // retained for compatibility with pre-expression function API
- std::string property;
- Stops stops;
- optional<T> defaultValue;
-
private:
+ optional<T> defaultValue;
std::shared_ptr<expression::Expression> expression;
};
diff --git a/mapbox-gl-js b/mapbox-gl-js
-Subproject 4cd5570ad3ed3e0ad71e2f795e795a78f5ccf60
+Subproject 3c07b72a59d78bc6240a8e3d77a54bec7619af5
diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md
index 60d3dc6b70..63736c1ca4 100644
--- a/platform/android/CHANGELOG.md
+++ b/platform/android/CHANGELOG.md
@@ -2,6 +2,37 @@
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.5.1 - March 26, 2018
+ - Verify optional access of FileSource deactivation [#11480](https://github.com/mapbox/mapbox-gl-native/pull/11480)
+ - Prevent default style loading when style json was set [#11519](https://github.com/mapbox/mapbox-gl-native/pull/11519)
+ - Delete local reference when convering Image.java [#11350](https://github.com/mapbox/mapbox-gl-native/pull/11350)
+ - Use float for pixel ratio when creating a snapshotter [#11367](https://github.com/mapbox/mapbox-gl-native/pull/11367)
+ - Validate if width and height aren't 0 when creating a snapshot [#11364](https://github.com/mapbox/mapbox-gl-native/pull/11364)
+ - Null check body of http request [#11413](https://github.com/mapbox/mapbox-gl-native/pull/11413)
+ - Clamp TileJSON bounds [#11425](https://github.com/mapbox/mapbox-gl-native/pull/11425)
+
+## 6.0.0-beta.4 - March 20, 2018
+ - Gesture library 0.1.0 stable [#11483](https://github.com/mapbox/mapbox-gl-native/pull/11483)
+ - Filters with expressions [#11429](https://github.com/mapbox/mapbox-gl-native/pull/11429)
+ - Telemetry 3.0.0-beta.2 [#11474](https://github.com/mapbox/mapbox-gl-native/pull/11474)
+ - High level JNI conversion for geojson [#11471](https://github.com/mapbox/mapbox-gl-native/pull/11471)
+ - Update to MAS 3.0.0-beta.4 [#11468](https://github.com/mapbox/mapbox-gl-native/pull/11468)
+ - Support for expression literal on arrays [#11457](https://github.com/mapbox/mapbox-gl-native/pull/11457)
+ - Fix telemetry integration for two finger tap gesture [#11460](https://github.com/mapbox/mapbox-gl-native/pull/11460)
+ - Revisit proguard configuration [#11434](https://github.com/mapbox/mapbox-gl-native/pull/11434)
+ - Expressions accessor support [#11352](https://github.com/mapbox/mapbox-gl-native/pull/11352)
+ - Calculate camera's LatLng for bounds without map padding [#11410](https://github.com/mapbox/mapbox-gl-native/pull/11410)
+ - Check for null on http body request [#11413](https://github.com/mapbox/mapbox-gl-native/pull/11413)
+ - Expose more gesture settings [#11407](https://github.com/mapbox/mapbox-gl-native/pull/11407)
+ - Revert java 8 language support [#11398](https://github.com/mapbox/mapbox-gl-native/pull/11398)
+ - Update to MAS 3.0.0-beta.3 [#11373](https://github.com/mapbox/mapbox-gl-native/pull/11373)
+ - Rework match expression to style specification syntax [#11388](https://github.com/mapbox/mapbox-gl-native/pull/11388)
+ - Update javadoc configuration for Gradle 4.4 [#11384](https://github.com/mapbox/mapbox-gl-native/pull/11384)
+ - Rework zoomIn and zoomOut to use ValueAnimators [#11382](https://github.com/mapbox/mapbox-gl-native/pull/11382)
+ - Delete LocalRef when converting Image.java [#11350](https://github.com/mapbox/mapbox-gl-native/pull/11350)
+ - Use float for pixelratio when creating a snapshotter [#11367](https://github.com/mapbox/mapbox-gl-native/pull/11367)
+ - Validate width/height when creating a snapshot [#11364](https://github.com/mapbox/mapbox-gl-native/pull/11364)
+
## 6.0.0-beta.3 - March 2, 2018
- Added missing local reference deletes [#11243](https://github.com/mapbox/mapbox-gl-native/pull/11243), [#11272](https://github.com/mapbox/mapbox-gl-native/pull/11272)
- Remove obsolete camera api [#11201](https://github.com/mapbox/mapbox-gl-native/pull/11201)
diff --git a/platform/android/MapboxGLAndroidSDK/gradle.properties b/platform/android/MapboxGLAndroidSDK/gradle.properties
index f00eb1d77f..2673f889e2 100644
--- a/platform/android/MapboxGLAndroidSDK/gradle.properties
+++ b/platform/android/MapboxGLAndroidSDK/gradle.properties
@@ -18,4 +18,3 @@ POM_PACKAGING=aar
# Only build native dependencies for the current ABI
# See https://code.google.com/p/android/issues/detail?id=221098#c20
android.buildOnlyTargetAbi=true
-
diff --git a/platform/android/MapboxGLAndroidSDK/proguard-rules.pro b/platform/android/MapboxGLAndroidSDK/proguard-rules.pro
index 529c3f4948..ce6e1709a0 100644
--- a/platform/android/MapboxGLAndroidSDK/proguard-rules.pro
+++ b/platform/android/MapboxGLAndroidSDK/proguard-rules.pro
@@ -17,6 +17,8 @@
-dontwarn com.mapbox.android.core.location.MockLocationEngine$LocationUpdateRunnable
-dontwarn java.awt.Color
-dontwarn com.mapzen.android.lost.api**
+-dontwarn org.conscrypt.OpenSSLProvider
+-dontwarn org.conscrypt.Conscrypt
# config for mapbox-sdk-geojson:3.0.0-beta.3
-keep class com.mapbox.geojson.** { *; }
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 b3dcf87c7e..858c1eed67 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
@@ -10,7 +10,7 @@ import android.text.TextUtils;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.exceptions.MapboxConfigurationException;
-import com.mapbox.mapboxsdk.maps.Events;
+import com.mapbox.mapboxsdk.maps.Telemetry;
import com.mapbox.mapboxsdk.net.ConnectivityReceiver;
/**
@@ -46,7 +46,7 @@ public final class Mapbox {
Context appContext = context.getApplicationContext();
INSTANCE = new Mapbox(appContext, accessToken);
- Events.initialize();
+ Telemetry.initialize();
ConnectivityReceiver.instance(appContext);
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java
index ffc55fec46..ab1191c0cc 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java
@@ -8,6 +8,7 @@ import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.util.Log;
+import com.mapbox.android.telemetry.TelemetryUtils;
import com.mapbox.mapboxsdk.BuildConfig;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
@@ -30,7 +31,6 @@ import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
-import okhttp3.internal.Util;
import timber.log.Timber;
import static android.util.Log.DEBUG;
@@ -224,7 +224,7 @@ class HTTPRequest implements Callback {
private String getUserAgent() {
if (userAgentString == null) {
- userAgentString = Util.toHumanReadableAscii(
+ userAgentString = TelemetryUtils.toHumanReadableAscii(
String.format("%s %s (%s) Android/%s (%s)",
getApplicationIdentifier(),
BuildConfig.MAPBOX_VERSION_STRING,
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 5ccd6bd795..39cd25631e 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
@@ -12,6 +12,7 @@ import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Toast;
+import com.mapbox.android.telemetry.TelemetryEnabler;
import com.mapbox.mapboxsdk.R;
import com.mapbox.mapboxsdk.attribution.Attribution;
import com.mapbox.mapboxsdk.attribution.AttributionParser;
@@ -98,7 +99,8 @@ public class AttributionDialogManager implements View.OnClickListener, DialogInt
builder.setPositiveButton(R.string.mapbox_attributionTelemetryPositive, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- Events.obtainTelemetry().enable();
+ TelemetryEnabler.updateTelemetryState(TelemetryEnabler.State.ENABLED);
+ Telemetry.obtainTelemetry().enable();
dialog.cancel();
}
});
@@ -112,7 +114,8 @@ public class AttributionDialogManager implements View.OnClickListener, DialogInt
builder.setNegativeButton(R.string.mapbox_attributionTelemetryNegative, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- Events.obtainTelemetry().disable();
+ Telemetry.obtainTelemetry().disable();
+ TelemetryEnabler.updateTelemetryState(TelemetryEnabler.State.DISABLED);
dialog.cancel();
}
});
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 849719a1a2..de9b4fdbc2 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
@@ -12,6 +12,7 @@ import android.view.MotionEvent;
import android.view.animation.DecelerateInterpolator;
import com.mapbox.android.gestures.AndroidGesturesManager;
+import com.mapbox.android.gestures.Constants;
import com.mapbox.android.gestures.MoveGestureDetector;
import com.mapbox.android.gestures.MultiFingerTapGestureDetector;
import com.mapbox.android.gestures.RotateGestureDetector;
@@ -77,13 +78,6 @@ final class MapGestureDetector {
private final CopyOnWriteArrayList<MapboxMap.OnShoveListener> onShoveListenerList
= new CopyOnWriteArrayList<>();
- private StandardGestureListener standardGestureListener;
- private MoveGestureListener moveGestureListener;
- private ScaleGestureListener scaleGestureListener;
- private RotateGestureListener rotateGestureListener;
- private ShoveGestureListener shoveGestureListener;
- private TapGestureListener tapGestureListener;
-
/**
* User-set focal point.
*/
@@ -112,29 +106,39 @@ final class MapGestureDetector {
// Checking for context != null for testing purposes
if (context != null) {
- // Initialize gesture listeners
- initializeGestureListeners(context);
-
// Initialize gestures manager
AndroidGesturesManager androidGesturesManager = new AndroidGesturesManager(context);
- initializeGesturesManager(androidGesturesManager, true, true);
+ initializeGesturesManager(androidGesturesManager, true);
+
+ // Initialize gesture listeners
+ initializeGestureListeners(context, true);
}
}
- private void initializeGestureListeners(Context context) {
- standardGestureListener = new StandardGestureListener();
- moveGestureListener = new MoveGestureListener();
- scaleGestureListener = new ScaleGestureListener(
- context.getResources().getDimension(R.dimen.mapbox_minimum_scale_velocity));
- rotateGestureListener = new RotateGestureListener(
- context.getResources().getDimension(R.dimen.mapbox_minimum_scale_span_when_rotating),
- context.getResources().getDimension(R.dimen.mapbox_minimum_angular_velocity));
- shoveGestureListener = new ShoveGestureListener();
- tapGestureListener = new TapGestureListener();
+ private void initializeGestureListeners(Context context, boolean attachDefaultListeners) {
+ if (attachDefaultListeners) {
+ StandardGestureListener standardGestureListener = new StandardGestureListener();
+ MoveGestureListener moveGestureListener = new MoveGestureListener();
+ ScaleGestureListener scaleGestureListener = new ScaleGestureListener(
+ context.getResources().getDimension(R.dimen.mapbox_minimum_scale_velocity));
+ RotateGestureListener rotateGestureListener = new RotateGestureListener(
+ context.getResources().getDimension(R.dimen.mapbox_minimum_scale_span_when_rotating),
+ context.getResources().getDimension(R.dimen.mapbox_minimum_angular_velocity),
+ context.getResources().getDimension(R.dimen.mapbox_defaultScaleSpanSinceStartThreshold));
+ ShoveGestureListener shoveGestureListener = new ShoveGestureListener();
+ TapGestureListener tapGestureListener = new TapGestureListener();
+
+ gesturesManager.setStandardGestureListener(standardGestureListener);
+ gesturesManager.setMoveGestureListener(moveGestureListener);
+ gesturesManager.setStandardScaleGestureListener(scaleGestureListener);
+ gesturesManager.setRotateGestureListener(rotateGestureListener);
+ gesturesManager.setShoveGestureListener(shoveGestureListener);
+ gesturesManager.setMultiFingerTapGestureListener(tapGestureListener);
+ }
}
private void initializeGesturesManager(AndroidGesturesManager androidGesturesManager,
- boolean attachDefaultListeners, boolean setDefaultMutuallyExclusives) {
+ boolean setDefaultMutuallyExclusives) {
if (setDefaultMutuallyExclusives) {
Set<Integer> shoveScaleSet = new HashSet<>();
shoveScaleSet.add(AndroidGesturesManager.GESTURE_TYPE_SHOVE);
@@ -151,15 +155,6 @@ final class MapGestureDetector {
androidGesturesManager.setMutuallyExclusiveGestures(shoveScaleSet, shoveRotateSet, ScaleLongPressSet);
}
- if (attachDefaultListeners) {
- androidGesturesManager.setStandardGestureListener(standardGestureListener);
- androidGesturesManager.setMoveGestureListener(moveGestureListener);
- androidGesturesManager.setStandardScaleGestureListener(scaleGestureListener);
- androidGesturesManager.setRotateGestureListener(rotateGestureListener);
- androidGesturesManager.setShoveGestureListener(shoveGestureListener);
- androidGesturesManager.setMultiFingerTapGestureListener(tapGestureListener);
- }
-
gesturesManager = androidGesturesManager;
}
@@ -349,7 +344,7 @@ final class MapGestureDetector {
notifyOnMapClickListeners(tapPoint);
}
- sendTelemetryEvent(Events.SINGLE_TAP, new PointF(motionEvent.getX(), motionEvent.getY()));
+ sendTelemetryEvent(Telemetry.SINGLE_TAP, new PointF(motionEvent.getX(), motionEvent.getY()));
return true;
}
@@ -380,7 +375,7 @@ final class MapGestureDetector {
zoomInAnimated(zoomFocalPoint, false);
- sendTelemetryEvent(Events.DOUBLE_TAP, new PointF(motionEvent.getX(), motionEvent.getY()));
+ sendTelemetryEvent(Telemetry.DOUBLE_TAP, new PointF(motionEvent.getX(), motionEvent.getY()));
return true;
}
@@ -444,7 +439,7 @@ final class MapGestureDetector {
transform.cancelTransitions();
cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
- sendTelemetryEvent(Events.PAN, detector.getFocalPoint());
+ sendTelemetryEvent(Telemetry.PAN, detector.getFocalPoint());
notifyOnMoveBeginListeners(detector);
@@ -501,7 +496,7 @@ final class MapGestureDetector {
if (uiSettings.isIncreaseRotateThresholdWhenScaling()) {
// increase rotate angle threshold when scale is detected first
gesturesManager.getRotateGestureDetector().setAngleThreshold(
- gesturesManager.getRotateGestureDetector().getDefaultAngleThreshold()
+ Constants.DEFAULT_ROTATE_ANGLE_THRESHOLD
+ MapboxConstants.ROTATION_THRESHOLD_INCREASE_WHEN_SCALING
);
}
@@ -509,7 +504,7 @@ final class MapGestureDetector {
// setting focalPoint in #onScaleBegin() as well, because #onScale() might not get called before #onScaleEnd()
setScaleFocalPoint(detector);
- sendTelemetryEvent(Events.PINCH, scaleFocalPoint);
+ sendTelemetryEvent(Telemetry.PINCH, scaleFocalPoint);
notifyOnScaleBeginListeners(detector);
@@ -544,7 +539,7 @@ final class MapGestureDetector {
if (uiSettings.isIncreaseRotateThresholdWhenScaling()) {
// resetting default angle threshold
gesturesManager.getRotateGestureDetector().setAngleThreshold(
- gesturesManager.getRotateGestureDetector().getDefaultAngleThreshold()
+ Constants.DEFAULT_ROTATE_ANGLE_THRESHOLD
);
}
@@ -603,10 +598,13 @@ final class MapGestureDetector {
private PointF rotateFocalPoint;
private final float minimumScaleSpanWhenRotating;
private final float minimumAngularVelocity;
+ private final float defaultSpanSinceStartThreshold;
- RotateGestureListener(float minimumScaleSpanWhenRotating, float minimumAngularVelocity) {
+ public RotateGestureListener(float minimumScaleSpanWhenRotating, float minimumAngularVelocity,
+ float defaultSpanSinceStartThreshold) {
this.minimumScaleSpanWhenRotating = minimumScaleSpanWhenRotating;
this.minimumAngularVelocity = minimumAngularVelocity;
+ this.defaultSpanSinceStartThreshold = defaultSpanSinceStartThreshold;
}
@Override
@@ -628,7 +626,7 @@ final class MapGestureDetector {
// setting in #onRotateBegin() as well, because #onRotate() might not get called before #onRotateEnd()
setRotateFocalPoint(detector);
- sendTelemetryEvent(Events.ROTATION, rotateFocalPoint);
+ sendTelemetryEvent(Telemetry.ROTATION, rotateFocalPoint);
notifyOnRotateBeginListeners(detector);
@@ -660,8 +658,7 @@ final class MapGestureDetector {
if (uiSettings.isIncreaseScaleThresholdWhenRotating()) {
// resetting default scale threshold values
- gesturesManager.getStandardScaleGestureDetector().setSpanSinceStartThreshold(
- gesturesManager.getStandardScaleGestureDetector().getDefaultSpanSinceStartThreshold());
+ gesturesManager.getStandardScaleGestureDetector().setSpanSinceStartThreshold(defaultSpanSinceStartThreshold);
}
notifyOnRotateEndListeners(detector);
@@ -747,7 +744,7 @@ final class MapGestureDetector {
transform.cancelTransitions();
cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
- sendTelemetryEvent(Events.PITCH, detector.getFocalPoint());
+ sendTelemetryEvent(Telemetry.PITCH, detector.getFocalPoint());
// disabling move gesture during shove
gesturesManager.getMoveGestureDetector().setEnabled(false);
@@ -796,7 +793,7 @@ final class MapGestureDetector {
transform.cancelTransitions();
cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
- sendTelemetryEvent(Events.TWO_FINGER_TAP, detector.getFocalPoint());
+ sendTelemetryEvent(Telemetry.TWO_FINGER_TAP, detector.getFocalPoint());
PointF zoomFocalPoint;
// Single finger double tap
@@ -893,7 +890,7 @@ final class MapGestureDetector {
LatLng latLng = projection.fromScreenLocation(focalPoint);
MapState state = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom());
state.setGesture(eventType);
- Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, state));
+ Telemetry.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, state));
}
}
@@ -1109,8 +1106,9 @@ final class MapGestureDetector {
return gesturesManager;
}
- void setGesturesManager(AndroidGesturesManager gesturesManager, boolean attachDefaultListeners,
+ void setGesturesManager(Context context, AndroidGesturesManager gesturesManager, boolean attachDefaultListeners,
boolean setDefaultMutuallyExclusives) {
- initializeGesturesManager(gesturesManager, attachDefaultListeners, setDefaultMutuallyExclusives);
+ initializeGesturesManager(gesturesManager, setDefaultMutuallyExclusives);
+ initializeGestureListeners(context, attachDefaultListeners);
}
} \ No newline at end of file
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 9c8ed7de2b..22d5dd8f19 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
@@ -268,7 +268,7 @@ public class MapView extends FrameLayout {
@UiThread
public void onCreate(@Nullable Bundle savedInstanceState) {
if (savedInstanceState == null) {
- MapboxTelemetry telemetry = Events.obtainTelemetry();
+ MapboxTelemetry telemetry = Telemetry.obtainTelemetry();
AppUserTurnstile turnstileEvent = new AppUserTurnstile(BuildConfig.MAPBOX_SDK_IDENTIFIER,
BuildConfig.MAPBOX_SDK_VERSION);
telemetry.push(turnstileEvent);
@@ -1026,7 +1026,8 @@ public class MapView extends FrameLayout {
@Override
public void setGesturesManager(AndroidGesturesManager gesturesManager, boolean attachDefaultListeners,
boolean setDefaultMutuallyExclusives) {
- mapGestureDetector.setGesturesManager(gesturesManager, attachDefaultListeners, setDefaultMutuallyExclusives);
+ mapGestureDetector.setGesturesManager(
+ getContext(), gesturesManager, attachDefaultListeners, setDefaultMutuallyExclusives);
}
@Override
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 45cb75b2da..f23d3bd7f9 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
@@ -40,7 +40,7 @@ import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.constants.Style;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
-import com.mapbox.mapboxsdk.style.layers.Filter;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.Layer;
import com.mapbox.mapboxsdk.style.light.Light;
import com.mapbox.mapboxsdk.style.sources.Source;
@@ -103,7 +103,7 @@ public final class MapboxMap {
*/
void onStart() {
nativeMapView.update();
- if (TextUtils.isEmpty(nativeMapView.getStyleUrl())) {
+ if (TextUtils.isEmpty(nativeMapView.getStyleUrl()) && TextUtils.isEmpty(nativeMapView.getStyleJson())) {
// if user hasn't loaded a Style yet
nativeMapView.setStyleUrl(Style.MAPBOX_STREETS);
}
@@ -2079,13 +2079,13 @@ public final class MapboxMap {
* Queries the map for rendered features
*
* @param coordinates the point to query
- * @param filter filters the returned features
+ * @param filter filters the returned features with an expression
* @param layerIds optionally - only query these layers
* @return the list of feature
*/
@NonNull
public List<Feature> queryRenderedFeatures(@NonNull PointF coordinates,
- @Nullable Filter.Statement filter,
+ @Nullable Expression filter,
@Nullable String... layerIds) {
return nativeMapView.queryRenderedFeatures(coordinates, layerIds, filter);
}
@@ -2107,13 +2107,13 @@ public final class MapboxMap {
* Queries the map for rendered features
*
* @param coordinates the box to query
- * @param filter filters the returned features
+ * @param filter filters the returned features with an expression
* @param layerIds optionally - only query these layers
* @return the list of feature
*/
@NonNull
public List<Feature> queryRenderedFeatures(@NonNull RectF coordinates,
- @Nullable Filter.Statement filter,
+ @Nullable Expression filter,
@Nullable String... layerIds) {
return nativeMapView.queryRenderedFeatures(coordinates, layerIds, filter);
}
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 1b6522d348..976277dcac 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
@@ -24,8 +24,8 @@ import com.mapbox.mapboxsdk.geometry.LatLngBounds;
import com.mapbox.mapboxsdk.geometry.ProjectedMeters;
import com.mapbox.mapboxsdk.maps.renderer.MapRenderer;
import com.mapbox.mapboxsdk.storage.FileSource;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.CannotAddLayerException;
-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.CannotAddSourceException;
@@ -799,7 +799,7 @@ final class NativeMapView {
@NonNull
public List<Feature> queryRenderedFeatures(@NonNull PointF coordinates,
@Nullable String[] layerIds,
- @Nullable Filter.Statement filter) {
+ @Nullable Expression filter) {
if (isDestroyedOn("queryRenderedFeatures")) {
return new ArrayList<>();
}
@@ -811,7 +811,7 @@ final class NativeMapView {
@NonNull
public List<Feature> queryRenderedFeatures(@NonNull RectF coordinates,
@Nullable String[] layerIds,
- @Nullable Filter.Statement filter) {
+ @Nullable Expression filter) {
if (isDestroyedOn("queryRenderedFeatures")) {
return new ArrayList<>();
}
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/Telemetry.java
index 7ba592f121..e6b93e4b91 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Events.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Telemetry.java
@@ -2,11 +2,12 @@ package com.mapbox.mapboxsdk.maps;
import com.mapbox.android.telemetry.MapboxTelemetry;
+import com.mapbox.android.telemetry.SessionInterval;
import com.mapbox.android.telemetry.TelemetryEnabler;
import com.mapbox.mapboxsdk.BuildConfig;
import com.mapbox.mapboxsdk.Mapbox;
-public class Events {
+public class Telemetry {
static final String TWO_FINGER_TAP = "TwoFingerTap";
static final String DOUBLE_TAP = "DoubleTap";
static final String SINGLE_TAP = "SingleTap";
@@ -16,12 +17,11 @@ public class Events {
static final String PITCH = "Pitch";
private MapboxTelemetry telemetry;
- private Events() {
+ private Telemetry() {
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)) {
+ if (TelemetryEnabler.State.ENABLED.equals(telemetryState)) {
telemetry.enable();
}
}
@@ -30,11 +30,19 @@ public class Events {
obtainTelemetry();
}
- private static class EventsHolder {
- private static final Events INSTANCE = new Events();
+ public static void updateDebugLoggingEnabled(boolean debugLoggingEnabled) {
+ TelemetryHolder.INSTANCE.telemetry.updateDebugLoggingEnabled(debugLoggingEnabled);
+ }
+
+ public static boolean updateSessionIdRotationInterval(SessionInterval interval) {
+ return TelemetryHolder.INSTANCE.telemetry.updateSessionIdRotationInterval(interval);
+ }
+
+ private static class TelemetryHolder {
+ private static final Telemetry INSTANCE = new Telemetry();
}
static MapboxTelemetry obtainTelemetry() {
- return EventsHolder.INSTANCE.telemetry;
+ return TelemetryHolder.INSTANCE.telemetry;
}
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java
index 8af140019f..efb2660da0 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java
@@ -112,6 +112,9 @@ public class Expression {
* @return the expression
*/
public static Expression literal(@NonNull Object object) {
+ if (object.getClass().isArray()) {
+ return literal(ExpressionArray.toObjectArray(object));
+ }
return new ExpressionLiteral(object);
}
@@ -132,7 +135,7 @@ public class Expression {
* @return the color expression
*/
public static Expression color(@ColorInt int color) {
- return new ExpressionLiteral(new Color(color));
+ return toColor(literal(PropertyFactory.colorToRgbaString(color)));
}
/**
@@ -1354,7 +1357,7 @@ public class Expression {
* @return expression
* @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-types-string">Style specification</a>
*/
- public static Expression string(@NonNull Expression input) {
+ public static Expression string(@NonNull Expression... input) {
return new Expression("string", input);
}
@@ -1367,7 +1370,7 @@ public class Expression {
* @return expression
* @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-types-number">Style specification</a>
*/
- public static Expression number(@NonNull Expression input) {
+ public static Expression number(@NonNull Expression... input) {
return new Expression("number", input);
}
@@ -1380,7 +1383,7 @@ public class Expression {
* @return expression
* @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-types-boolean">Style specification</a>
*/
- public static Expression bool(@NonNull Expression input) {
+ public static Expression bool(@NonNull Expression... input) {
return new Expression("boolean", input);
}
@@ -1520,13 +1523,14 @@ public class Expression {
* Returns the output value of the stop just less than the input,
* or the first input if the input is less than the first stop.
*
- * @param input the input value
- * @param stops pair of input and output values
+ * @param input the input value
+ * @param defaultOutput the default output expression
+ * @param stops pair of input and output values
* @return expression
* @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-step">Style specification</a>
*/
- public static Expression step(@NonNull Number input, @NonNull Expression expression, Expression... stops) {
- return step(literal(input), expression, stops);
+ public static Expression step(@NonNull Number input, @NonNull Expression defaultOutput, Expression... stops) {
+ return step(literal(input), defaultOutput, stops);
}
/**
@@ -1536,13 +1540,14 @@ public class Expression {
* Returns the output value of the stop just less than the input,
* or the first input if the input is less than the first stop.
*
- * @param expression the input expression
- * @param stops pair of input and output values
+ * @param input the input expression
+ * @param defaultOutput the default output expression
+ * @param stops pair of input and output values
* @return expression
* @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-step">Style specification</a>
*/
- public static Expression step(@NonNull Expression input, @NonNull Expression expression, Expression... stops) {
- return new Expression("step", join(new Expression[] {input, expression}, stops));
+ public static Expression step(@NonNull Expression input, @NonNull Expression defaultOutput, Expression... stops) {
+ return new Expression("step", join(new Expression[] {input, defaultOutput}, stops));
}
/**
@@ -1552,13 +1557,14 @@ public class Expression {
* Returns the output value of the stop just less than the input,
* or the first input if the input is less than the first stop.
*
- * @param input the input value
- * @param stops pair of input and output values
+ * @param input the input value
+ * @param defaultOutput the default output expression
+ * @param stops pair of input and output values
* @return expression
* @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-step">Style specification</a>
*/
- public static Expression step(@NonNull Number input, @NonNull Expression expression, Stop... stops) {
- return step(literal(input), expression, Stop.toExpressionArray(stops));
+ public static Expression step(@NonNull Number input, @NonNull Expression defaultOutput, Stop... stops) {
+ return step(literal(input), defaultOutput, Stop.toExpressionArray(stops));
}
/**
@@ -1568,13 +1574,14 @@ public class Expression {
* Returns the output value of the stop just less than the input,
* or the first input if the input is less than the first stop.
*
- * @param input the input value
- * @param stops pair of input and output values
+ * @param input the input value
+ * @param defaultOutput the default output expression
+ * @param stops pair of input and output values
* @return expression
* @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-step">Style specification</a>
*/
- public static Expression step(@NonNull Expression input, @NonNull Expression expression, Stop... stops) {
- return step(input, expression, Stop.toExpressionArray(stops));
+ public static Expression step(@NonNull Expression input, @NonNull Expression defaultOutput, Stop... stops) {
+ return step(input, defaultOutput, Stop.toExpressionArray(stops));
}
/**
@@ -1728,9 +1735,7 @@ public class Expression {
*/
private Object toValue(ExpressionLiteral expressionValue) {
Object value = expressionValue.toValue();
- if (value instanceof Expression.Color) {
- return ((Expression.Color) value).convertColor();
- } else if (value instanceof Expression.ExpressionLiteral) {
+ if (value instanceof Expression.ExpressionLiteral) {
return toValue((ExpressionLiteral) value);
} else if (value instanceof Expression) {
return ((Expression) value).toArray();
@@ -1887,32 +1892,6 @@ public class Expression {
}
/**
- * Expression color type.
- */
- public static class Color {
-
- private int color;
-
- /**
- * Creates a color color type from a color int.
- *
- * @param color the int color
- */
- public Color(@ColorInt int color) {
- this.color = color;
- }
-
- /**
- * Converts the int color to rgba(d, d, d, d) string representation
- *
- * @return the string representation of a color
- */
- String convertColor() {
- return PropertyFactory.colorToRgbaString(color);
- }
- }
-
- /**
* Expression array type.
*/
public static class Array {
@@ -2033,6 +2012,11 @@ public class Expression {
};
}
+ /**
+ * Convert the expression array to a string representation.
+ *
+ * @return the string representation of the expression array
+ */
@Override
public String toString() {
StringBuilder builder = new StringBuilder("[\"literal\"], [");
@@ -2053,4 +2037,20 @@ public class Expression {
return builder.toString();
}
}
+
+ /**
+ * Converts an object that is a primitive array to an Object[]
+ *
+ * @param object the object to convert to an object array
+ * @return the converted object array
+ */
+ static Object[] toObjectArray(Object object) {
+ // object is a primitive array
+ int len = java.lang.reflect.Array.getLength(object);
+ Object[] objects = new Object[len];
+ for (int i = 0; i < len; i++) {
+ objects[i] = java.lang.reflect.Array.get(object, i);
+ }
+ return objects;
+ }
} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/BackgroundLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/BackgroundLayer.java
index 978fa29ef8..60c2aa907b 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/BackgroundLayer.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/BackgroundLayer.java
@@ -4,10 +4,13 @@ package com.mapbox.mapboxsdk.style.layers;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import static com.mapbox.mapboxsdk.utils.ColorUtils.rgbaToColor;
+import com.google.gson.JsonArray;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
/**
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CircleLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CircleLayer.java
index 10663142b5..8f1c00f1b5 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CircleLayer.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CircleLayer.java
@@ -4,10 +4,13 @@ package com.mapbox.mapboxsdk.style.layers;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import static com.mapbox.mapboxsdk.utils.ColorUtils.rgbaToColor;
+import com.google.gson.JsonArray;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
/**
@@ -69,26 +72,41 @@ public class CircleLayer extends Layer {
}
/**
- * Set a single filter.
+ * Set a single expression filter.
*
- * @param filter the filter to set
+ * @param filter the expression filter to set
*/
- public void setFilter(Filter.Statement filter) {
+ public void setFilter(Expression filter) {
nativeSetFilter(filter.toArray());
}
/**
- * Set a single filter.
+ * Set a single expression filter.
*
- * @param filter the filter to set
+ * @param filter the expression filter to set
* @return This
*/
- public CircleLayer withFilter(Filter.Statement filter) {
+ public CircleLayer withFilter(Expression filter) {
setFilter(filter);
return this;
}
/**
+ * Get a single expression filter.
+ *
+ * @return the expression filter to get
+ */
+ @Nullable
+ public Expression getFilter() {
+ Expression expression = null;
+ JsonArray array = nativeGetFilter();
+ if (array != null) {
+ expression = Expression.Converter.convert(array);
+ }
+ return expression;
+ }
+
+ /**
* Set a property or properties.
*
* @param properties the var-args properties
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillExtrusionLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillExtrusionLayer.java
index 6772da73b1..34f062b4d6 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillExtrusionLayer.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillExtrusionLayer.java
@@ -4,10 +4,13 @@ package com.mapbox.mapboxsdk.style.layers;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import static com.mapbox.mapboxsdk.utils.ColorUtils.rgbaToColor;
+import com.google.gson.JsonArray;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
/**
@@ -69,26 +72,41 @@ public class FillExtrusionLayer extends Layer {
}
/**
- * Set a single filter.
+ * Set a single expression filter.
*
- * @param filter the filter to set
+ * @param filter the expression filter to set
*/
- public void setFilter(Filter.Statement filter) {
+ public void setFilter(Expression filter) {
nativeSetFilter(filter.toArray());
}
/**
- * Set a single filter.
+ * Set a single expression filter.
*
- * @param filter the filter to set
+ * @param filter the expression filter to set
* @return This
*/
- public FillExtrusionLayer withFilter(Filter.Statement filter) {
+ public FillExtrusionLayer withFilter(Expression filter) {
setFilter(filter);
return this;
}
/**
+ * Get a single expression filter.
+ *
+ * @return the expression filter to get
+ */
+ @Nullable
+ public Expression getFilter() {
+ Expression expression = null;
+ JsonArray array = nativeGetFilter();
+ if (array != null) {
+ expression = Expression.Converter.convert(array);
+ }
+ return expression;
+ }
+
+ /**
* Set a property or properties.
*
* @param properties the var-args properties
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillLayer.java
index 3719ae9e1b..1865349c42 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillLayer.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillLayer.java
@@ -4,10 +4,13 @@ package com.mapbox.mapboxsdk.style.layers;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import static com.mapbox.mapboxsdk.utils.ColorUtils.rgbaToColor;
+import com.google.gson.JsonArray;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
/**
@@ -69,26 +72,41 @@ public class FillLayer extends Layer {
}
/**
- * Set a single filter.
+ * Set a single expression filter.
*
- * @param filter the filter to set
+ * @param filter the expression filter to set
*/
- public void setFilter(Filter.Statement filter) {
+ public void setFilter(Expression filter) {
nativeSetFilter(filter.toArray());
}
/**
- * Set a single filter.
+ * Set a single expression filter.
*
- * @param filter the filter to set
+ * @param filter the expression filter to set
* @return This
*/
- public FillLayer withFilter(Filter.Statement filter) {
+ public FillLayer withFilter(Expression filter) {
setFilter(filter);
return this;
}
/**
+ * Get a single expression filter.
+ *
+ * @return the expression filter to get
+ */
+ @Nullable
+ public Expression getFilter() {
+ Expression expression = null;
+ JsonArray array = nativeGetFilter();
+ if (array != null) {
+ expression = Expression.Converter.convert(array);
+ }
+ return expression;
+ }
+
+ /**
* Set a property or properties.
*
* @param properties the var-args properties
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Filter.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Filter.java
deleted file mode 100644
index 4dbb461e4c..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Filter.java
+++ /dev/null
@@ -1,230 +0,0 @@
-package com.mapbox.mapboxsdk.style.layers;
-
-import java.util.ArrayList;
-import java.util.Collections;
-
-/**
- * Utility to build filter expressions more easily.
- *
- * @see <a href="https://www.mapbox.com/mapbox-gl-style-spec/#types-filter">The online documentation</a>
- */
-public class Filter {
-
- /**
- * Base Filter statement. Subclassed to provide concrete statements.
- */
- public abstract static class Statement {
- protected final String operator;
-
- public Statement(String operator) {
- this.operator = operator;
- }
-
- /**
- * Generate a raw array representation of the filter
- *
- * @return the filter represented as an array
- */
- public abstract Object[] toArray();
- }
-
- /**
- * Represents a {@link Filter} statement. Can be unary (eg `has()`, etc) or take any number of values.
- */
- private static class SimpleStatement extends Statement {
- private final String key;
- private final Object[] values;
-
- /**
- * @param operator the operator (eg `=`, etc)
- * @param key the property key
- * @param values the values to operate on, if any
- */
- SimpleStatement(String operator, String key, Object... values) {
- super(operator);
- this.key = key;
- this.values = values;
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Object[] toArray() {
- ArrayList<Object> array = new ArrayList<>(2 + values.length);
- array.add(operator);
- array.add(key);
- Collections.addAll(array, values);
- return array.toArray();
- }
- }
-
- /**
- * Represents a collection of {@link Statement}s with an operator that describes their relationship
- */
- private static class CompoundStatement extends Statement {
- private final Statement[] statements;
-
- /**
- * @param operator the relationship operator
- * @param statements the statements to compound
- */
- CompoundStatement(String operator, Statement... statements) {
- super(operator);
- this.statements = statements;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Object[] toArray() {
- ArrayList<Object> array = new ArrayList<>(1 + statements.length);
- array.add(operator);
- for (Statement statement : statements) {
- array.add(statement.toArray());
- }
- return array.toArray();
- }
- }
-
- /**
- * Groups a collection of statements in an 'all' relationship
- *
- * @param statements the collection of statements
- * @return the statements compounded
- */
- public static Statement all(Statement... statements) {
- return new CompoundStatement("all", statements);
- }
-
- /**
- * Groups a collection of statements in an 'any' relationship
- *
- * @param statements the collection of statements
- * @return the statements compounded
- */
- public static Statement any(Statement... statements) {
- return new CompoundStatement("any", statements);
- }
-
- /**
- * Groups a collection of statements in an 'none' relationship
- *
- * @param statements the collection of statements
- * @return the statements compounded
- */
- public static Statement none(Statement... statements) {
- return new CompoundStatement("none", statements);
- }
-
- /**
- * Check the property's existence
- *
- * @param key the property key
- * @return the statement
- */
- public static Statement has(String key) {
- return new SimpleStatement("has", key);
- }
-
- /**
- * Check the property's existence, negated
- *
- * @param key the property key
- * @return the statement
- */
- public static Statement notHas(String key) {
- return new SimpleStatement("!has", key);
- }
-
- /**
- * Check the property equals the given value
- *
- * @param key the property key
- * @param value the value to check against
- * @return the statement
- */
- public static Statement eq(String key, Object value) {
- return new SimpleStatement("==", key, value);
- }
-
- /**
- * Check the property does not equals the given value
- *
- * @param key the property key
- * @param value the value to check against
- * @return the statement
- */
- public static Statement neq(String key, Object value) {
- return new SimpleStatement("!=", key, value);
- }
-
- /**
- * Check the property exceeds the given value
- *
- * @param key the property key
- * @param value the value to check against
- * @return the statement
- */
- public static Statement gt(String key, Object value) {
- return new SimpleStatement(">", key, value);
- }
-
- /**
- * Check the property exceeds or equals the given value
- *
- * @param key the property key
- * @param value the value to check against
- * @return the statement
- */
- public static Statement gte(String key, Object value) {
- return new SimpleStatement(">=", key, value);
- }
-
- /**
- * Check the property does not exceeds the given value
- *
- * @param key the property key
- * @param value the value to check against
- * @return the statement
- */
- public static Statement lt(String key, Object value) {
- return new SimpleStatement("<", key, value);
- }
-
- /**
- * Check the property equals or does not exceeds the given value
- *
- * @param key the property key
- * @param value the value to check against
- * @return the statement
- */
- public static Statement lte(String key, Object value) {
- return new SimpleStatement("<=", key, value);
- }
-
- /**
- * Check the property is within the given set
- *
- * @param key the property key
- * @param values the set of values to check against
- * @return the statement
- */
- public static Statement in(String key, Object... values) {
- return new SimpleStatement("in", key, values);
- }
-
- /**
- * Check the property is not within the given set
- *
- * @param key the property key
- * @param values the set of values to check against
- * @return the statement
- */
- public static Statement notIn(String key, Object... values) {
- return new SimpleStatement("!in", key, values);
- }
-
-}
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
index bfc663449f..a8d07bdbb4 100644
--- 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
@@ -4,10 +4,13 @@ package com.mapbox.mapboxsdk.style.layers;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import static com.mapbox.mapboxsdk.utils.ColorUtils.rgbaToColor;
+import com.google.gson.JsonArray;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
/**
@@ -69,26 +72,41 @@ public class HeatmapLayer extends Layer {
}
/**
- * Set a single filter.
+ * Set a single expression filter.
*
- * @param filter the filter to set
+ * @param filter the expression filter to set
*/
- public void setFilter(Filter.Statement filter) {
+ public void setFilter(Expression filter) {
nativeSetFilter(filter.toArray());
}
/**
- * Set a single filter.
+ * Set a single expression filter.
*
- * @param filter the filter to set
+ * @param filter the expression filter to set
* @return This
*/
- public HeatmapLayer withFilter(Filter.Statement filter) {
+ public HeatmapLayer withFilter(Expression filter) {
setFilter(filter);
return this;
}
/**
+ * Get a single expression filter.
+ *
+ * @return the expression filter to get
+ */
+ @Nullable
+ public Expression getFilter() {
+ Expression expression = null;
+ JsonArray array = nativeGetFilter();
+ if (array != null) {
+ expression = Expression.Converter.convert(array);
+ }
+ return expression;
+ }
+
+ /**
* Set a property or properties.
*
* @param properties the var-args properties
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 7e3d3a7779..fb086f424b 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
@@ -4,10 +4,13 @@ package com.mapbox.mapboxsdk.style.layers;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import static com.mapbox.mapboxsdk.utils.ColorUtils.rgbaToColor;
+import com.google.gson.JsonArray;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
/**
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Layer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Layer.java
index 66db88bafb..26fc9d32e1 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Layer.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Layer.java
@@ -2,6 +2,7 @@ package com.mapbox.mapboxsdk.style.layers;
import android.support.annotation.NonNull;
+import com.google.gson.JsonArray;
import com.mapbox.mapboxsdk.style.expressions.Expression;
/**
@@ -71,6 +72,8 @@ public abstract class Layer {
protected native void nativeSetFilter(Object[] filter);
+ protected native JsonArray nativeGetFilter();
+
protected native void nativeSetSourceLayer(String sourceLayer);
protected native String nativeGetSourceLayer();
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/LineLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/LineLayer.java
index ca106cc106..9dbd3aeb19 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/LineLayer.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/LineLayer.java
@@ -4,10 +4,13 @@ package com.mapbox.mapboxsdk.style.layers;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import static com.mapbox.mapboxsdk.utils.ColorUtils.rgbaToColor;
+import com.google.gson.JsonArray;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
/**
@@ -69,26 +72,41 @@ public class LineLayer extends Layer {
}
/**
- * Set a single filter.
+ * Set a single expression filter.
*
- * @param filter the filter to set
+ * @param filter the expression filter to set
*/
- public void setFilter(Filter.Statement filter) {
+ public void setFilter(Expression filter) {
nativeSetFilter(filter.toArray());
}
/**
- * Set a single filter.
+ * Set a single expression filter.
*
- * @param filter the filter to set
+ * @param filter the expression filter to set
* @return This
*/
- public LineLayer withFilter(Filter.Statement filter) {
+ public LineLayer withFilter(Expression filter) {
setFilter(filter);
return this;
}
/**
+ * Get a single expression filter.
+ *
+ * @return the expression filter to get
+ */
+ @Nullable
+ public Expression getFilter() {
+ Expression expression = null;
+ JsonArray array = nativeGetFilter();
+ if (array != null) {
+ expression = Expression.Converter.convert(array);
+ }
+ return expression;
+ }
+
+ /**
* Set a property or properties.
*
* @param properties the var-args properties
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/RasterLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/RasterLayer.java
index a0f45535f6..0c7948f62a 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/RasterLayer.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/RasterLayer.java
@@ -4,10 +4,13 @@ package com.mapbox.mapboxsdk.style.layers;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import static com.mapbox.mapboxsdk.utils.ColorUtils.rgbaToColor;
+import com.google.gson.JsonArray;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
/**
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/SymbolLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/SymbolLayer.java
index d0fb82dce5..bc1e02a89f 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/SymbolLayer.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/SymbolLayer.java
@@ -4,10 +4,13 @@ package com.mapbox.mapboxsdk.style.layers;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import static com.mapbox.mapboxsdk.utils.ColorUtils.rgbaToColor;
+import com.google.gson.JsonArray;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
/**
@@ -69,26 +72,41 @@ public class SymbolLayer extends Layer {
}
/**
- * Set a single filter.
+ * Set a single expression filter.
*
- * @param filter the filter to set
+ * @param filter the expression filter to set
*/
- public void setFilter(Filter.Statement filter) {
+ public void setFilter(Expression filter) {
nativeSetFilter(filter.toArray());
}
/**
- * Set a single filter.
+ * Set a single expression filter.
*
- * @param filter the filter to set
+ * @param filter the expression filter to set
* @return This
*/
- public SymbolLayer withFilter(Filter.Statement filter) {
+ public SymbolLayer withFilter(Expression filter) {
setFilter(filter);
return this;
}
/**
+ * Get a single expression filter.
+ *
+ * @return the expression filter to get
+ */
+ @Nullable
+ public Expression getFilter() {
+ Expression expression = null;
+ JsonArray array = nativeGetFilter();
+ if (array != null) {
+ expression = Expression.Converter.convert(array);
+ }
+ return expression;
+ }
+
+ /**
* Set a property or properties.
*
* @param properties the var-args properties
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/layer.java.ejs b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/layer.java.ejs
index 77fa11808e..f3941aacf6 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/layer.java.ejs
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/layer.java.ejs
@@ -9,10 +9,13 @@ package com.mapbox.mapboxsdk.style.layers;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import static com.mapbox.mapboxsdk.utils.ColorUtils.rgbaToColor;
+import com.google.gson.JsonArray;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
/**
@@ -89,25 +92,40 @@ public class <%- camelize(type) %>Layer extends Layer {
}
/**
- * Set a single filter.
+ * Set a single expression filter.
*
- * @param filter the filter to set
+ * @param filter the expression filter to set
*/
- public void setFilter(Filter.Statement filter) {
+ public void setFilter(Expression filter) {
nativeSetFilter(filter.toArray());
}
/**
- * Set a single filter.
+ * Set a single expression filter.
*
- * @param filter the filter to set
+ * @param filter the expression filter to set
* @return This
*/
- public <%- camelize(type) %>Layer withFilter(Filter.Statement filter) {
+ public <%- camelize(type) %>Layer withFilter(Expression filter) {
setFilter(filter);
return this;
}
+ /**
+ * Get a single expression filter.
+ *
+ * @return the expression filter to get
+ */
+ @Nullable
+ public Expression getFilter() {
+ Expression expression = null;
+ JsonArray array = nativeGetFilter();
+ if (array != null) {
+ expression = Expression.Converter.convert(array);
+ }
+ return expression;
+ }
+
<% } -%>
/**
* Set a property or properties.
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 1b0999ae2e..21a34f6064 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
@@ -8,7 +8,7 @@ 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.mapboxsdk.style.expressions.Expression;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -94,11 +94,11 @@ public class CustomGeometrySource extends Source {
/**
* Queries the source for features.
*
- * @param filter an optional filter statement to filter the returned Features
+ * @param filter an optional filter expression to filter the returned Features
* @return the features
*/
@NonNull
- public List<Feature> querySourceFeatures(@Nullable Filter.Statement filter) {
+ public List<Feature> querySourceFeatures(@Nullable Expression filter) {
Feature[] features = querySourceFeatures(filter != null ? filter.toArray() : null);
return features != null ? Arrays.asList(features) : new ArrayList<Feature>();
}
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 4a2f29b503..efacc18741 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
@@ -7,7 +7,7 @@ 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.mapboxsdk.style.expressions.Expression;
import java.net.URL;
import java.util.ArrayList;
@@ -238,11 +238,11 @@ public class GeoJsonSource extends Source {
/**
* Queries the source for features.
*
- * @param filter an optional filter statement to filter the returned Features
+ * @param filter an optional filter expression to filter the returned Features
* @return the features
*/
@NonNull
- public List<Feature> querySourceFeatures(@Nullable Filter.Statement filter) {
+ public List<Feature> querySourceFeatures(@Nullable Expression filter) {
Feature[] features = querySourceFeatures(filter != null ? filter.toArray() : null);
return features != null ? Arrays.asList(features) : new ArrayList<Feature>();
}
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 62b08a90ed..d82eaaa1c7 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
@@ -6,7 +6,7 @@ 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.mapboxsdk.style.expressions.Expression;
import java.net.URL;
import java.util.ArrayList;
@@ -64,12 +64,12 @@ public class VectorSource extends Source {
* Queries the source for features.
*
* @param sourceLayerIds the source layer identifiers. At least one must be specified.
- * @param filter an optional filter statement to filter the returned Features
+ * @param filter an optional filter expression to filter the returned Features
* @return the features
*/
@NonNull
public List<Feature> querySourceFeatures(@Size(min = 1) String[] sourceLayerIds,
- @Nullable Filter.Statement filter) {
+ @Nullable Expression filter) {
Feature[] features = querySourceFeatures(
sourceLayerIds,
filter != null ? filter.toArray() : null);
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/expressions/ExpressionTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/expressions/ExpressionTest.java
index a2cf34738d..22c25fd0df 100644
--- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/expressions/ExpressionTest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/expressions/ExpressionTest.java
@@ -2,8 +2,6 @@ package com.mapbox.mapboxsdk.style.expressions;
import android.graphics.Color;
-import com.mapbox.mapboxsdk.style.layers.PropertyFactory;
-
import org.junit.Test;
import java.util.Arrays;
@@ -113,7 +111,7 @@ public class ExpressionTest {
@Test
public void testToRgba() throws Exception {
- Object[] expected = new Object[] {"to-rgba", PropertyFactory.colorToRgbaString(Color.RED)};
+ Object[] expected = new Object[] {"to-rgba", new Object[] {"to-color", "rgba(255, 0, 0, 255)"}};
Object[] actual = toRgba(color(Color.RED)).toArray();
assertTrue("expression should match", Arrays.deepEquals(expected, actual));
}
@@ -1081,4 +1079,20 @@ public class ExpressionTest {
String actual = literal(array).toString();
assertEquals("literal array should match", expected, actual);
}
+
+ @Test
+ public void testLiteralPrimitiveArrayConversion() throws Exception {
+ float[] array = new float[] {0.2f, 0.5f};
+ Object[] expected = new Object[] {"literal", new Object[] {0.2f, 0.5f}};
+ Object[] actual = literal(array).toArray();
+ assertEquals("primitive array should be converted", expected, actual);
+ }
+
+ @Test
+ public void testColorConversion() {
+ Expression greenColor = color(0xFF00FF00);
+ Object[] expected = new Object[] {"to-color", "rgba(0, 255, 0, 255)"};
+ assertTrue("expression should match", Arrays.deepEquals(expected, greenColor.toArray()));
+ }
+
} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/layers/FilterTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/layers/FilterTest.java
deleted file mode 100644
index 933bf05b39..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/layers/FilterTest.java
+++ /dev/null
@@ -1,102 +0,0 @@
-package com.mapbox.mapboxsdk.style.layers;
-
-import org.junit.Test;
-
-import static com.mapbox.mapboxsdk.style.layers.Filter.all;
-import static com.mapbox.mapboxsdk.style.layers.Filter.any;
-import static com.mapbox.mapboxsdk.style.layers.Filter.eq;
-import static com.mapbox.mapboxsdk.style.layers.Filter.gt;
-import static com.mapbox.mapboxsdk.style.layers.Filter.gte;
-import static com.mapbox.mapboxsdk.style.layers.Filter.has;
-import static com.mapbox.mapboxsdk.style.layers.Filter.in;
-import static com.mapbox.mapboxsdk.style.layers.Filter.lt;
-import static com.mapbox.mapboxsdk.style.layers.Filter.lte;
-import static com.mapbox.mapboxsdk.style.layers.Filter.neq;
-import static com.mapbox.mapboxsdk.style.layers.Filter.none;
-import static com.mapbox.mapboxsdk.style.layers.Filter.notHas;
-import static com.mapbox.mapboxsdk.style.layers.Filter.notIn;
-import static org.junit.Assert.assertArrayEquals;
-
-/**
- * Tests for Filter
- */
-public class FilterTest {
-
- @Test
- public void testAll() {
- assertArrayEquals(all().toArray(), new Object[] {"all"});
- assertArrayEquals(
- all(eq("key", 2), neq("key", 3)).toArray(),
- new Object[] {"all", new Object[] {"==", "key", 2}, new Object[] {"!=", "key", 3}}
- );
- }
-
- @Test
- public void testAny() {
- assertArrayEquals(any().toArray(), new Object[] {"any"});
- assertArrayEquals(
- any(eq("key", 2), neq("key", 3)).toArray(),
- new Object[] {"any", new Object[] {"==", "key", 2}, new Object[] {"!=", "key", 3}}
- );
- }
-
- @Test
- public void testNone() {
- assertArrayEquals(none().toArray(), new Object[] {"none"});
- assertArrayEquals(
- none(eq("key", 2), neq("key", 3)).toArray(),
- new Object[] {"none", new Object[] {"==", "key", 2}, new Object[] {"!=", "key", 3}}
- );
- }
-
- @Test
- public void testHas() {
- assertArrayEquals(has("key").toArray(), new Object[] {"has", "key"});
- }
-
- @Test
- public void testHasNot() {
- assertArrayEquals(notHas("key").toArray(), new Object[] {"!has", "key"});
- }
-
- @Test
- public void testEq() {
- assertArrayEquals(eq("key", 1).toArray(), new Object[] {"==", "key", 1});
-
- }
-
- @Test
- public void testNeq() {
- assertArrayEquals(neq("key", 1).toArray(), new Object[] {"!=", "key", 1});
- }
-
- @Test
- public void testGt() {
- assertArrayEquals(gt("key", 1).toArray(), new Object[] {">", "key", 1});
- }
-
- @Test
- public void testGte() {
- assertArrayEquals(gte("key", 1).toArray(), new Object[] {">=", "key", 1});
- }
-
- @Test
- public void testLt() {
- assertArrayEquals(lt("key", 1).toArray(), new Object[] {"<", "key", 1});
- }
-
- @Test
- public void testLte() {
- assertArrayEquals(lte("key", 1).toArray(), new Object[] {"<=", "key", 1});
- }
-
- @Test
- public void testIn() {
- assertArrayEquals(in("key", 1, 2, "Aap").toArray(), new Object[] {"in", "key", 1, 2, "Aap"});
- }
-
- @Test
- public void testNotIn() {
- assertArrayEquals(notIn("key", 1, 2, "Noot").toArray(), new Object[] {"!in", "key", 1, 2, "Noot"});
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/telemetry/HttpTransportTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/telemetry/HttpTransportTest.java
index 94a6dc2194..519124e1eb 100644
--- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/telemetry/HttpTransportTest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/telemetry/HttpTransportTest.java
@@ -1,8 +1,8 @@
package com.mapbox.mapboxsdk.telemetry;
-import org.junit.Test;
+import com.mapbox.android.telemetry.TelemetryUtils;
-import okhttp3.internal.Util;
+import org.junit.Test;
import static junit.framework.Assert.assertEquals;
@@ -15,6 +15,6 @@ public class HttpTransportTest {
final String asciiVersion = "Sveriges Fj?ll/1.0/1 MapboxEventsAndroid/4.0.0-SNAPSHOT";
assertEquals("asciiVersion and swedishUserAgent should match", asciiVersion,
- Util.toHumanReadableAscii(swedishUserAgent));
+ TelemetryUtils.toHumanReadableAscii(swedishUserAgent));
}
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CircleLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CircleLayerTest.java
index 0460a59534..a851fde6dd 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CircleLayerTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CircleLayerTest.java
@@ -86,6 +86,26 @@ public class CircleLayerTest extends BaseActivityTest {
}
@Test
+ public void testFilter() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("Filter");
+ invoke(mapboxMap, (uiController, mapboxMap1) -> {
+ assertNotNull(layer);
+
+ // Get initial
+ assertEquals(layer.getFilter(), null);
+
+ // Set
+ Expression filter = eq(get("undefined"), literal(1.0));
+ layer.setFilter(filter);
+ assertEquals(layer.getFilter().toString(), filter.toString());
+ });
+ }
+
+
+
+ @Test
public void testCircleRadiusTransition() {
validateTestSetup();
setupLayer();
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillExtrusionLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillExtrusionLayerTest.java
index dd8929e2f4..f22956072d 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillExtrusionLayerTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillExtrusionLayerTest.java
@@ -86,6 +86,26 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
}
@Test
+ public void testFilter() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("Filter");
+ invoke(mapboxMap, (uiController, mapboxMap1) -> {
+ assertNotNull(layer);
+
+ // Get initial
+ assertEquals(layer.getFilter(), null);
+
+ // Set
+ Expression filter = eq(get("undefined"), literal(1.0));
+ layer.setFilter(filter);
+ assertEquals(layer.getFilter().toString(), filter.toString());
+ });
+ }
+
+
+
+ @Test
public void testFillExtrusionOpacityTransition() {
validateTestSetup();
setupLayer();
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillLayerTest.java
index c70ea39ffa..bdbd8958d2 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillLayerTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillLayerTest.java
@@ -86,6 +86,26 @@ public class FillLayerTest extends BaseActivityTest {
}
@Test
+ public void testFilter() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("Filter");
+ invoke(mapboxMap, (uiController, mapboxMap1) -> {
+ assertNotNull(layer);
+
+ // Get initial
+ assertEquals(layer.getFilter(), null);
+
+ // Set
+ Expression filter = eq(get("undefined"), literal(1.0));
+ layer.setFilter(filter);
+ assertEquals(layer.getFilter().toString(), filter.toString());
+ });
+ }
+
+
+
+ @Test
public void testFillAntialiasAsConstant() {
validateTestSetup();
setupLayer();
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
index 9a0374a763..549f309e4f 100644
--- 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
@@ -86,6 +86,26 @@ public class HeatmapLayerTest extends BaseActivityTest {
}
@Test
+ public void testFilter() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("Filter");
+ invoke(mapboxMap, (uiController, mapboxMap1) -> {
+ assertNotNull(layer);
+
+ // Get initial
+ assertEquals(layer.getFilter(), null);
+
+ // Set
+ Expression filter = eq(get("undefined"), literal(1.0));
+ layer.setFilter(filter);
+ assertEquals(layer.getFilter().toString(), filter.toString());
+ });
+ }
+
+
+
+ @Test
public void testHeatmapRadiusTransition() {
validateTestSetup();
setupLayer();
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LightTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LightTest.java
index 560aa91420..fce73bdead 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LightTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LightTest.java
@@ -24,7 +24,7 @@ import org.junit.runner.RunWith;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static com.mapbox.mapboxsdk.style.layers.Filter.eq;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.eq;
import static com.mapbox.mapboxsdk.style.layers.Property.ANCHOR_MAP;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionBase;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionColor;
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LineLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LineLayerTest.java
index a8e306d7a6..40cf0f2927 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LineLayerTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LineLayerTest.java
@@ -86,6 +86,26 @@ public class LineLayerTest extends BaseActivityTest {
}
@Test
+ public void testFilter() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("Filter");
+ invoke(mapboxMap, (uiController, mapboxMap1) -> {
+ assertNotNull(layer);
+
+ // Get initial
+ assertEquals(layer.getFilter(), null);
+
+ // Set
+ Expression filter = eq(get("undefined"), literal(1.0));
+ layer.setFilter(filter);
+ assertEquals(layer.getFilter().toString(), filter.toString());
+ });
+ }
+
+
+
+ @Test
public void testLineCapAsConstant() {
validateTestSetup();
setupLayer();
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/StyleLoaderTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/StyleLoaderTest.java
new file mode 100644
index 0000000000..1a5201193c
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/StyleLoaderTest.java
@@ -0,0 +1,77 @@
+package com.mapbox.mapboxsdk.testapp.style;
+
+
+import android.support.test.espresso.UiController;
+
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.action.MapboxMapAction;
+import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest;
+import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity;
+import com.mapbox.mapboxsdk.testapp.utils.ResourceUtils;
+
+import org.junit.Test;
+
+import java.io.IOException;
+
+import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests around style loading
+ */
+public class StyleLoaderTest extends BaseActivityTest {
+
+
+ @Override
+ protected Class getActivityClass() {
+ return EspressoTestActivity.class;
+ }
+
+ @Test
+ public void testSetGetStyleJsonString() throws Exception {
+ validateTestSetup();
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ try {
+ String expected = ResourceUtils.readRawResource(rule.getActivity(), R.raw.local_style);
+ mapboxMap.setStyleJson(expected);
+ String actual = mapboxMap.getStyleJson();
+ assertEquals("Style json should match", expected, actual);
+ } catch (IOException exception) {
+ exception.printStackTrace();
+ }
+ }
+ });
+ }
+
+ @Test
+ public void testDefaultStyleLoadWithActivityLifecycleChange() throws Exception {
+ validateTestSetup();
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ try {
+ String expected = ResourceUtils.readRawResource(rule.getActivity(), R.raw.local_style);
+ mapboxMap.setStyleJson(expected);
+
+ // fake activity stop/start
+ MapView mapView = (MapView) rule.getActivity().findViewById(R.id.mapView);
+ mapView.onPause();
+ mapView.onStop();
+
+ mapView.onStart();
+ mapView.onResume();
+
+ String actual = mapboxMap.getStyleJson();
+ assertEquals("Style URL should be empty", "", mapboxMap.getStyleUrl());
+ assertEquals("Style json should match", expected, actual);
+ } catch (IOException exception) {
+ exception.printStackTrace();
+ }
+ }
+ });
+ }
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java
index 443c3d682a..62f73b1507 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java
@@ -86,6 +86,26 @@ public class SymbolLayerTest extends BaseActivityTest {
}
@Test
+ public void testFilter() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("Filter");
+ invoke(mapboxMap, (uiController, mapboxMap1) -> {
+ assertNotNull(layer);
+
+ // Get initial
+ assertEquals(layer.getFilter(), null);
+
+ // Set
+ Expression filter = eq(get("undefined"), literal(1.0));
+ layer.setFilter(filter);
+ assertEquals(layer.getFilter().toString(), filter.toString());
+ });
+ }
+
+
+
+ @Test
public void testSymbolPlacementAsConstant() {
validateTestSetup();
setupLayer();
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/layer.junit.ejs b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/layer.junit.ejs
index 09d4328b70..935813899f 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/layer.junit.ejs
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/layer.junit.ejs
@@ -96,6 +96,26 @@ public class <%- camelize(type) %>LayerTest extends BaseActivityTest {
assertEquals(layer.getSourceLayer(), sourceLayer);
});
}
+
+ @Test
+ public void testFilter() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("Filter");
+ invoke(mapboxMap, (uiController, mapboxMap1) -> {
+ assertNotNull(layer);
+
+ // Get initial
+ assertEquals(layer.getFilter(), null);
+
+ // Set
+ Expression filter = eq(get("undefined"), literal(1.0));
+ layer.setFilter(filter);
+ assertEquals(layer.getFilter().toString(), filter.toString());
+ });
+ }
+
+
<% } -%>
<% for (const property of properties) { -%>
<% if (property.name != 'heatmap-color') { -%>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/light.junit.ejs b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/light.junit.ejs
index 96f23871c5..a8207a903b 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/light.junit.ejs
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/light.junit.ejs
@@ -27,7 +27,7 @@ import org.junit.runner.RunWith;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static com.mapbox.mapboxsdk.style.layers.Filter.eq;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.eq;
import static com.mapbox.mapboxsdk.style.layers.Property.ANCHOR_MAP;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionBase;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionColor;
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapboxApplication.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapboxApplication.java
index fba33bb380..fa13959112 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapboxApplication.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapboxApplication.java
@@ -5,6 +5,7 @@ import android.os.StrictMode;
import android.text.TextUtils;
import com.mapbox.mapboxsdk.Mapbox;
+import com.mapbox.mapboxsdk.maps.Telemetry;
import com.mapbox.mapboxsdk.testapp.utils.TokenUtils;
import com.squareup.leakcanary.LeakCanary;
@@ -57,6 +58,8 @@ public class MapboxApplication extends Application {
}
Mapbox.getInstance(getApplicationContext(), mapboxAccessToken);
+
+ Telemetry.updateDebugLoggingEnabled(true);
}
private void initializeLogger() {
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/GestureDetectorActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/GestureDetectorActivity.java
index 2b79e2691d..c1698e20ab 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/GestureDetectorActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/GestureDetectorActivity.java
@@ -58,7 +58,6 @@ public class GestureDetectorActivity extends AppCompatActivity {
private Marker marker;
@Nullable
private LatLng focalPointLatLng;
- private boolean animationsEnabled = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -198,9 +197,7 @@ public class GestureDetectorActivity extends AppCompatActivity {
if (focalPointLatLng != null) {
gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_OTHER, "REVERTING MOVE THRESHOLD"));
- gesturesManager.getMoveGestureDetector().setMoveThreshold(
- gesturesManager.getMoveGestureDetector().getDefaultMoveThreshold()
- );
+ gesturesManager.getMoveGestureDetector().setMoveThreshold(0f);
}
}
});
@@ -242,8 +239,7 @@ public class GestureDetectorActivity extends AppCompatActivity {
mapboxMap.getUiSettings().setFocalPoint(mapboxMap.getProjection().toScreenLocation(focalPointLatLng));
return true;
case R.id.menu_gesture_animation:
- animationsEnabled = !animationsEnabled;
- mapboxMap.getUiSettings().setAllVelocityAnimationsEnabled(animationsEnabled);
+ mapboxMap.getUiSettings().setAllVelocityAnimationsEnabled(false);
}
return super.onOptionsItemSelected(item);
}
@@ -251,10 +247,8 @@ public class GestureDetectorActivity extends AppCompatActivity {
private void resetModes() {
focalPointLatLng = null;
mapboxMap.getUiSettings().setFocalPoint(null);
-
- gesturesManager.getMoveGestureDetector().setMoveThreshold(
- gesturesManager.getMoveGestureDetector().getDefaultMoveThreshold()
- );
+ gesturesManager.getMoveGestureDetector().setMoveThreshold(0f);
+ mapboxMap.getUiSettings().setAllVelocityAnimationsEnabled(true);
if (marker != null) {
mapboxMap.removeMarker(marker);
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 df608360ad..7953824c36 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
@@ -11,16 +11,19 @@ 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.expressions.Expression;
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 java.util.List;
import timber.log.Timber;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.get;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.literal;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.lt;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.toNumber;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillColor;
/**
@@ -58,7 +61,9 @@ public class QueryRenderedFeaturesBoxHighlightActivity extends AppCompatActivity
int left = selectionBox.getLeft() - mapView.getLeft();
RectF box = new RectF(left, top, left + selectionBox.getWidth(), top + selectionBox.getHeight());
Timber.i("Querying box %s for buildings", box);
- List<Feature> features = mapboxMap.queryRenderedFeatures(box, Filter.lt("height", 10), "building");
+
+ Expression filter = lt(toNumber(get("height")), literal(10));
+ List<Feature> features = mapboxMap.queryRenderedFeatures(box, filter, "building");
// Show count
Toast.makeText(
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 14de81ab30..79069a26f7 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
@@ -11,13 +11,16 @@ 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 java.util.List;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.eq;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.get;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.literal;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.neq;
+
/**
* Test activity showcasing using the query source features API to query feature counts
*/
@@ -31,8 +34,6 @@ public class QuerySourceFeaturesActivity extends AppCompatActivity {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_query_source_features);
- final float density = getResources().getDisplayMetrics().density;
-
// Initialize map as normal
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
@@ -47,12 +48,12 @@ public class QuerySourceFeaturesActivity extends AppCompatActivity {
}));
mapboxMap.addSource(source);
- mapboxMap.addLayer(new CircleLayer("test-layer", source.getId()).withFilter(Filter.neq("key1", "value1")));
+ mapboxMap.addLayer(new CircleLayer("test-layer", source.getId()).withFilter(neq(get("key1"), literal("value1"))));
// Add a click listener
mapboxMap.setOnMapClickListener(point -> {
// Query
- List<Feature> features = source.querySourceFeatures(Filter.eq("key1", "value1"));
+ List<Feature> features = source.querySourceFeatures(eq(get("key1"), literal("value1")));
Toast.makeText(QuerySourceFeaturesActivity.this, String.format("Found %s features",
features.size()), Toast.LENGTH_SHORT).show();
});
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/BuildingFillExtrusionActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/BuildingFillExtrusionActivity.java
index c86e995956..97b4fbf6af 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/BuildingFillExtrusionActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/BuildingFillExtrusionActivity.java
@@ -16,7 +16,9 @@ import com.mapbox.mapboxsdk.style.light.Light;
import com.mapbox.mapboxsdk.style.light.Position;
import com.mapbox.mapboxsdk.testapp.R;
-import static com.mapbox.mapboxsdk.style.layers.Filter.eq;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.eq;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.get;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.literal;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionBase;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionColor;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionHeight;
@@ -52,7 +54,7 @@ public class BuildingFillExtrusionActivity extends AppCompatActivity {
private void setupBuildings() {
FillExtrusionLayer fillExtrusionLayer = new FillExtrusionLayer("3d-buildings", "composite");
fillExtrusionLayer.setSourceLayer("building");
- fillExtrusionLayer.setFilter(eq("extrude", "true"));
+ fillExtrusionLayer.setFilter(eq(get("extrude"), literal("true")));
fillExtrusionLayer.setMinZoom(15);
fillExtrusionLayer.setProperties(
fillExtrusionColor(Color.LTGRAY),
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CircleLayerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CircleLayerActivity.java
index 6aa8777777..9437422d84 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CircleLayerActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CircleLayerActivity.java
@@ -13,6 +13,7 @@ import com.mapbox.mapboxsdk.constants.Style;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.CircleLayer;
import com.mapbox.mapboxsdk.style.layers.LineLayer;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
@@ -23,7 +24,10 @@ import java.net.URL;
import timber.log.Timber;
-import static com.mapbox.mapboxsdk.style.layers.Filter.in;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.array;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.get;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.has;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.literal;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleColor;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleRadius;
@@ -117,7 +121,7 @@ public class CircleLayerActivity extends AppCompatActivity implements View.OnCli
}
private void applyBusRouteFilterToBusStopSource() {
- layer.setFilter(in("number", (Object[]) Data.STOPS_FOR_ROUTE));
+ layer.setFilter(has(Expression.toString(get("number")), array(literal(Data.STOPS_FOR_ROUTE))));
}
private void addBusRouteSource() {
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/DataDrivenStyleActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/DataDrivenStyleActivity.java
index eebe95a411..dae0714d42 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/DataDrivenStyleActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/DataDrivenStyleActivity.java
@@ -1,5 +1,6 @@
package com.mapbox.mapboxsdk.testapp.activity.style;
+import android.graphics.Color;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
@@ -33,6 +34,7 @@ import static com.mapbox.mapboxsdk.style.expressions.Expression.rgba;
import static com.mapbox.mapboxsdk.style.expressions.Expression.step;
import static com.mapbox.mapboxsdk.style.expressions.Expression.stop;
import static com.mapbox.mapboxsdk.style.expressions.Expression.zoom;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.color;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillAntialias;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillColor;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillOpacity;
@@ -173,9 +175,9 @@ public class DataDrivenStyleActivity extends AppCompatActivity {
fillColor(
interpolate(
exponential(0.5f), zoom(),
- stop(1, rgb(255, 0, 0)),
- stop(5, rgb(0, 0, 255)),
- stop(10, rgb(0, 255, 0))
+ stop(1, color(Color.RED)),
+ stop(5, color(Color.BLUE)),
+ stop(10, color(Color.GREEN))
)
)
);
@@ -460,7 +462,7 @@ public class DataDrivenStyleActivity extends AppCompatActivity {
// Add a fill layer
mapboxMap.addLayer(new FillLayer(AMSTERDAM_PARKS_LAYER, source.getId())
.withProperties(
- fillColor(rgba(0.0f, 0.0f, 0.0f, 0.5f)),
+ fillColor(color(Color.GREEN)),
fillOutlineColor(rgb(0, 0, 255)),
fillAntialias(true)
)
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GeoJsonClusteringActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GeoJsonClusteringActivity.java
index 8664979292..655d4a8936 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GeoJsonClusteringActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GeoJsonClusteringActivity.java
@@ -10,6 +10,7 @@ import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.CircleLayer;
import com.mapbox.mapboxsdk.style.layers.SymbolLayer;
import com.mapbox.mapboxsdk.style.sources.GeoJsonOptions;
@@ -21,9 +22,12 @@ import java.net.URL;
import timber.log.Timber;
-import static com.mapbox.mapboxsdk.style.layers.Filter.all;
-import static com.mapbox.mapboxsdk.style.layers.Filter.gte;
-import static com.mapbox.mapboxsdk.style.layers.Filter.lt;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.all;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.get;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.gte;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.literal;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.lt;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.toNumber;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleColor;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleRadius;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconImage;
@@ -124,7 +128,7 @@ public class GeoJsonClusteringActivity extends AppCompatActivity {
)
);
} catch (MalformedURLException malformedUrlException) {
- Timber.e(malformedUrlException,"That's not an url... ");
+ Timber.e(malformedUrlException, "That's not an url... ");
}
// Add unclustered layer
@@ -145,10 +149,15 @@ public class GeoJsonClusteringActivity extends AppCompatActivity {
circleColor(layers[i][1]),
circleRadius(18f)
);
+
+ Expression pointCount = toNumber(get("point_count"));
circles.setFilter(
i == 0
- ? gte("point_count", layers[i][0]) :
- all(gte("point_count", layers[i][0]), lt("point_count", layers[i - 1][0]))
+ ? gte(pointCount, literal(layers[i][0])) :
+ all(
+ gte(pointCount, literal(layers[i][0])),
+ lt(pointCount, literal(layers[i - 1][0]))
+ )
);
mapboxMap.addLayer(circles);
}
@@ -156,7 +165,7 @@ public class GeoJsonClusteringActivity extends AppCompatActivity {
// Add the count labels
SymbolLayer count = new SymbolLayer("count", "earthquakes");
count.setProperties(
- textField("{point_count}"),
+ textField(get("point_count")),
textSize(12f),
textColor(Color.WHITE)
);
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 6eb4772b15..f686308a63 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
@@ -31,7 +31,6 @@ import com.mapbox.mapboxsdk.style.sources.VectorSource;
import com.mapbox.mapboxsdk.testapp.R;
import com.mapbox.mapboxsdk.testapp.utils.ResourceUtils;
-
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
@@ -39,15 +38,18 @@ import java.util.List;
import timber.log.Timber;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.all;
import static com.mapbox.mapboxsdk.style.expressions.Expression.color;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.eq;
import static com.mapbox.mapboxsdk.style.expressions.Expression.exponential;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.get;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.gte;
import static com.mapbox.mapboxsdk.style.expressions.Expression.interpolate;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.literal;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.lt;
import static com.mapbox.mapboxsdk.style.expressions.Expression.stop;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.toNumber;
import static com.mapbox.mapboxsdk.style.expressions.Expression.zoom;
-import static com.mapbox.mapboxsdk.style.layers.Filter.all;
-import static com.mapbox.mapboxsdk.style.layers.Filter.eq;
-import static com.mapbox.mapboxsdk.style.layers.Filter.gte;
-import static com.mapbox.mapboxsdk.style.layers.Filter.lt;
import static com.mapbox.mapboxsdk.style.layers.Property.FILL_TRANSLATE_ANCHOR_MAP;
import static com.mapbox.mapboxsdk.style.layers.Property.NONE;
import static com.mapbox.mapboxsdk.style.layers.Property.SYMBOL_PLACEMENT_POINT;
@@ -295,7 +297,7 @@ public class RuntimeStyleActivity extends AppCompatActivity {
);
// Only show me parks (except westerpark with stroke-width == 3)
- layer.setFilter(all(eq("type", "park"), eq("stroke-width", 2)));
+ layer.setFilter(all(eq(get("type"), literal("park")), eq(get("stroke-width"), literal(3))));
mapboxMap.addLayerBelow(layer, "building");
// layer.setPaintProperty(fillColor(Color.RED)); // XXX But not after the object is attached
@@ -345,7 +347,7 @@ public class RuntimeStyleActivity extends AppCompatActivity {
);
// Only show me parks
- layer.setFilter(all(eq("type", "park")));
+ layer.setFilter(all(eq(get("type"), literal("park"))));
mapboxMap.addLayer(layer);
@@ -499,7 +501,7 @@ public class RuntimeStyleActivity extends AppCompatActivity {
FillLayer states = (FillLayer) mapboxMap.getLayer("states");
if (states != null) {
- states.setFilter(eq("name", "Texas"));
+ states.setFilter(eq(get("name"), literal("Texas")));
states.setFillOpacityTransition(new TransitionOptions(2500, 0));
states.setFillColorTransition(new TransitionOptions(2500, 0));
states.setProperties(
@@ -555,7 +557,10 @@ public class RuntimeStyleActivity extends AppCompatActivity {
FillLayer regions = (FillLayer) mapboxMap.getLayer("regions");
if (regions != null) {
- regions.setFilter(all(gte("HRRNUM", 200), lt("HRRNUM", 300)));
+ regions.setFilter(all(
+ gte(toNumber(get("HRRNUM")), literal(200)),
+ lt(toNumber(get("HRRNUM")), literal(300)))
+ );
regions.setProperties(
fillColor(Color.BLUE),
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 5a30d9ce30..c42f264478 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
@@ -21,7 +21,6 @@ import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.style.expressions.Expression;
-import com.mapbox.mapboxsdk.style.layers.Filter;
import com.mapbox.mapboxsdk.style.layers.SymbolLayer;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
import com.mapbox.mapboxsdk.style.sources.Source;
@@ -38,6 +37,7 @@ import timber.log.Timber;
import static com.mapbox.mapboxsdk.style.expressions.Expression.concat;
import static com.mapbox.mapboxsdk.style.expressions.Expression.division;
import static com.mapbox.mapboxsdk.style.expressions.Expression.downcase;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.eq;
import static com.mapbox.mapboxsdk.style.expressions.Expression.get;
import static com.mapbox.mapboxsdk.style.expressions.Expression.literal;
import static com.mapbox.mapboxsdk.style.expressions.Expression.match;
@@ -45,8 +45,11 @@ import static com.mapbox.mapboxsdk.style.expressions.Expression.number;
import static com.mapbox.mapboxsdk.style.expressions.Expression.pi;
import static com.mapbox.mapboxsdk.style.expressions.Expression.product;
import static com.mapbox.mapboxsdk.style.expressions.Expression.rgba;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.step;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.stop;
import static com.mapbox.mapboxsdk.style.expressions.Expression.string;
import static com.mapbox.mapboxsdk.style.expressions.Expression.upcase;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.zoom;
import static com.mapbox.mapboxsdk.style.layers.Property.ICON_ANCHOR_BOTTOM;
import static com.mapbox.mapboxsdk.style.layers.Property.TEXT_ANCHOR_TOP;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconAllowOverlap;
@@ -121,8 +124,8 @@ public class SymbolGeneratorActivity extends AppCompatActivity implements OnMapR
return true;
} else if (item.getItemId() == R.id.menu_action_filter) {
SymbolLayer layer = mapboxMap.getLayerAs(LAYER_ID);
- layer.setFilter(Filter.eq(FEATURE_RANK, 1));
- //layer.setFilter(eq(get(FEATURE_RANK), 1));
+ layer.setFilter(eq(get(FEATURE_RANK), literal(1)));
+ Timber.e("Filter that was set: %s", layer.getFilter());
return true;
}
return super.onOptionsItemSelected(item);
@@ -265,7 +268,10 @@ public class SymbolGeneratorActivity extends AppCompatActivity implements OnMapR
iconAllowOverlap(false),
iconSize(iconSizeExpression),
iconAnchor(ICON_ANCHOR_BOTTOM),
- iconOffset(new Float[] {0.0f, -5.0f}),
+ iconOffset(step(zoom(), literal(new float[] {0f, 0f}),
+ stop(1, new Float[] {0f, 0f}),
+ stop(10, new Float[] {0f, -35f})
+ )),
// text field configuration
textField(textFieldExpression),
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_gestures.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_gestures.xml
index 3f96188da4..8b3a540ffa 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_gestures.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_gestures.xml
@@ -8,5 +8,5 @@
android:title="Focus on a point" />
<item
android:id="@+id/menu_gesture_animation"
- android:title="Toggle velocity animations" />
+ android:title="Turn off velocity animations" />
</menu> \ No newline at end of file
diff --git a/platform/android/config.cmake b/platform/android/config.cmake
index 2694068dac..80d0810564 100644
--- a/platform/android/config.cmake
+++ b/platform/android/config.cmake
@@ -229,6 +229,8 @@ add_library(mbgl-android STATIC
platform/android/src/geojson/feature_collection.hpp
platform/android/src/geojson/geometry.cpp
platform/android/src/geojson/geometry.hpp
+ platform/android/src/geojson/geometry_collection.cpp
+ platform/android/src/geojson/geometry_collection.hpp
platform/android/src/geojson/line_string.cpp
platform/android/src/geojson/line_string.hpp
platform/android/src/geojson/multi_line_string.cpp
diff --git a/platform/android/gradle/dependencies.gradle b/platform/android/gradle/dependencies.gradle
index bf0c919986..c875569830 100644
--- a/platform/android/gradle/dependencies.gradle
+++ b/platform/android/gradle/dependencies.gradle
@@ -9,8 +9,8 @@ ext {
versions = [
mapboxServices : '3.0.0-beta.4',
- mapboxTelemetry: '3.0.0-beta.1',
- mapboxGestures : '0.1.0-20180228.152340-13',
+ mapboxTelemetry: '3.0.0-beta.2',
+ mapboxGestures : '0.1.0',
supportLib : '25.4.0',
espresso : '3.0.1',
testRunner : '1.0.1',
@@ -20,14 +20,14 @@ ext {
mockito : '2.10.0',
robolectric : '3.5.1',
timber : '4.5.1',
- okhttp : '3.9.1'
+ okhttp : '3.10.0'
]
dependenciesList = [
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}",
- mapboxAndroidGestures : "com.mapbox.mapboxsdk:mapbox-android-gestures:${versions.mapboxGestures}@aar",
+ mapboxAndroidGestures : "com.mapbox.mapboxsdk:mapbox-android-gestures:${versions.mapboxGestures}",
// for testApp
mapboxJavaTurf : "com.mapbox.mapboxsdk:mapbox-sdk-turf:${versions.mapboxServices}",
diff --git a/platform/android/src/file_source.cpp b/platform/android/src/file_source.cpp
index 612619a30b..d8d715dbd3 100644
--- a/platform/android/src/file_source.cpp
+++ b/platform/android/src/file_source.cpp
@@ -70,14 +70,16 @@ void FileSource::resume(jni::JNIEnv&) {
activationCounter.value()++;
if (activationCounter == 1) {
- fileSource->resume();
+ fileSource->resume();
}
}
void FileSource::pause(jni::JNIEnv&) {
- activationCounter.value()--;
- if (activationCounter == 0) {
- fileSource->pause();
+ if (activationCounter) {
+ activationCounter.value()--;
+ if (activationCounter == 0) {
+ fileSource->pause();
+ }
}
}
diff --git a/platform/android/src/geojson/conversion/feature.hpp b/platform/android/src/geojson/conversion/feature.hpp
index 86aa5fc03c..8fc62a2789 100644
--- a/platform/android/src/geojson/conversion/feature.hpp
+++ b/platform/android/src/geojson/conversion/feature.hpp
@@ -182,7 +182,7 @@ struct Converter<jni::Object<android::geojson::Feature>, mbgl::Feature> {
auto properties = jni::Object<gson::JsonObject>(*convert<jni::jobject*>(env, value.properties));
// Convert geometry
- auto geometry = jni::Object<android::geojson::Geometry>(*convert<jni::jobject*>(env, value.geometry));
+ auto geometry = *convert<jni::Object<android::geojson::Geometry>>(env, value.geometry);
// Create feature
auto feature = android::geojson::Feature::fromGeometry(env, geometry, properties, jid);
diff --git a/platform/android/src/geojson/conversion/geometry.hpp b/platform/android/src/geojson/conversion/geometry.hpp
index 48b9089692..242a68df02 100644
--- a/platform/android/src/geojson/conversion/geometry.hpp
+++ b/platform/android/src/geojson/conversion/geometry.hpp
@@ -1,192 +1,24 @@
#pragma once
-#include "../../conversion/constant.hpp"
-#include "../../conversion/collection.hpp"
-
#include <mapbox/geometry.hpp>
+#include "../geometry.hpp"
#include <jni/jni.hpp>
-#include "../../jni/local_object.hpp"
namespace mbgl {
namespace android {
namespace conversion {
/**
- * Turn mapbox::geometry type into Java GeoJson Geometries
- */
-template <typename T>
-class GeometryEvaluator {
-public:
-
- jni::JNIEnv& env;
-
- /**
- * static Point fromLngLat(double longitude,double latitude)
- */
- jni::jobject* operator()(const mapbox::geometry::point<T> &geometry) const {
- 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;");
-
- return reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromLngLat, geometry.x, geometry.y));
- }
-
- /**
- * static LineString fromLngLats(double [][])
- */
- jni::jobject* operator()(const mapbox::geometry::line_string<T> &geometry) const {
- static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/geojson/LineString")).release();
- static jni::jmethodID* fromLngLats = &jni::GetStaticMethodID(env, *javaClass, "fromLngLats", "([[D)Lcom/mapbox/geojson/LineString;");
-
- // Create
- jni::LocalObject<jni::jarray<jni::jobject>> lngLatsArray =
- jni::NewLocalObject(env, toLngLatArray(env, geometry));
- return reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromLngLats, lngLatsArray.get()));
- }
-
- /**
- * static MultiPoint fromLngLats(double [][])
- */
- jni::jobject* operator()(const mapbox::geometry::multi_point<T> &geometry) const {
- static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/geojson/MultiPoint")).release();
- static jni::jmethodID* fromLngLats = &jni::GetStaticMethodID(env, *javaClass, "fromLngLats", "([[D)Lcom/mapbox/geojson/MultiPoint;");
-
- // Create
- jni::LocalObject<jni::jarray<jni::jobject>> lngLatsArray =
- jni::NewLocalObject(env, toLngLatArray(env, geometry));
- return reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromLngLats, lngLatsArray.get()));
- }
-
- /**
- * static Polygon fromLngLats(double [][][])
- */
- jni::jobject* operator()(const mapbox::geometry::polygon<T> &geometry) const {
- static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/geojson/Polygon")).release();
- static jni::jmethodID* fromLngLats = &jni::GetStaticMethodID(env, *javaClass, "fromLngLats", "([[[D)Lcom/mapbox/geojson/Polygon;");
-
- // Create
- jni::LocalObject<jni::jarray<jni::jobject>> shape =
- jni::NewLocalObject(env, shapeToLngLatArray<>(env, geometry));
- return reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromLngLats, shape.get()));
- }
-
- /**
- * static MultiLineString fromLngLats(double [][][])
- */
- jni::jobject* operator()(const mapbox::geometry::multi_line_string<T> &geometry) const {
- static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/geojson/MultiLineString")).release();
- static jni::jmethodID* fromLngLats = &jni::GetStaticMethodID(env, *javaClass, "fromLngLats", "([[[D)Lcom/mapbox/geojson/MultiLineString;");
-
- // Create
- jni::LocalObject<jni::jarray<jni::jobject>> shape =
- jni::NewLocalObject(env,shapeToLngLatArray<>(env, geometry));
- return reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromLngLats, shape.get()));
- }
-
- /**
- * MultiPolygon (double[][][][]) -> [[[D + Object array == [[[[D
- */
- jni::jobject* operator()(const mapbox::geometry::multi_polygon<T> &geometry) const {
-
- static jni::jclass* arrayClass = jni::NewGlobalRef(env, &jni::FindClass(env, "[[[D")).release();
- jni::LocalObject<jni::jarray<jni::jobject>> jarray =
- jni::NewLocalObject(env, &jni::NewObjectArray(env, geometry.size(), *arrayClass));
-
- for(size_t i = 0; i < geometry.size(); i = i + 1) {
- jni::LocalObject<jni::jarray<jni::jobject>> shape =
- jni::NewLocalObject(env, shapeToLngLatArray<>(env, geometry.at(i)));
- jni::SetObjectArrayElement(env, *jarray, i, shape.get());
- }
-
- // Create the MultiPolygon
- static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/geojson/MultiPolygon")).release();
- static jni::jmethodID* fromGeometries = &jni::GetStaticMethodID(env, *javaClass, "fromLngLats", "([[[[D)Lcom/mapbox/geojson/MultiPolygon;");
- return reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromGeometries, jarray.get()));
- }
-
- /**
- * GeometryCollection
- */
- jni::jobject* operator()(const mapbox::geometry::geometry_collection<T> &collection) const {
- static jni::jclass* geometryClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/geojson/Geometry")).release();
- jni::LocalObject<jni::jarray<jni::jobject>> jarray = jni::NewLocalObject(env, &jni::NewObjectArray(env, collection.size(), *geometryClass));
-
- for(size_t i = 0; i < collection.size(); i = i + 1) {
- auto& geometry = collection.at(i);
- jni::LocalObject<jni::jobject> converted = jni::NewLocalObject(env, mapbox::geometry::geometry<T>::visit(geometry, *this));
- jni::SetObjectArrayElement(env, *jarray, i, converted.get());
- }
-
- // Turn into array list and create the 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<jni::jobject> list = jni::NewLocalObject(env, toArrayList<>(env, *jarray));
- return reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromGeometries, list.get()));
- }
-
-private:
-
- /**
- * x, y -> jarray<jdouble> ([x,y])
- *
- */
- static jni::jarray<jni::jdouble> *toLngLatArray(JNIEnv &env, double x, double y) {
- jni::jarray<jni::jdouble> &jarray = jni::NewArray<jni::jdouble>(env, 2);
- jni::jdouble array[] = {x, y};
- jni::SetArrayRegion(env, jarray, 0, 2, array);
- return &jarray;
- }
-
- /**
- * vector<point<T>> -> jarray<jobject> -> [D + Object array == [[D (double[][])
- */
- static jni::jarray<jni::jobject>* toLngLatArray(JNIEnv &env,
- std::vector<mapbox::geometry::point<T>> points) {
-
- static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "[D")).release();
- jni::jarray<jni::jobject >& jarray = jni::NewObjectArray(env, points.size(), *javaClass);
-
- for(size_t i = 0; i < points.size(); i = i + 1) {
- mapbox::geometry::point<T> point = points.at(i);
- jni::jarray<jni::jdouble> *lngLatArray = toLngLatArray(env, point.x, point.y);
- jni::SetObjectArrayElement(env, jarray, i, lngLatArray);
- }
-
- return &jarray;
- }
-
- /**
- * polygon<T>
- * multi_line_string<T> -> jarray<jobject> -> [[D + Object array == [[[D (double[][][])
- */
- template <class SHAPE>
- static jni::jarray<jni::jobject>* shapeToLngLatArray(JNIEnv &env, SHAPE value) {
- static jni::jclass *javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "[[D")).release();
- jni::jarray<jni::jobject> &jarray = jni::NewObjectArray(env, value.size(), *javaClass);
-
- for (size_t i = 0; i < value.size(); i = i + 1) {
- jni::LocalObject<jni::jarray<jni::jobject>> lngLatsArray =
- jni::NewLocalObject(env, toLngLatArray(env, value.at(i)));
- jni::SetObjectArrayElement(env, jarray, i, lngLatsArray.get());
- }
-
- return &jarray;
- }
-};
-
-/**
- * mapbox::geometry::geometry<T> -> Java GeoJson Geometry<>
+ * mapbox::geometry::geometry<T> -> Java GeoJson Geometry
*/
template <class T>
-struct Converter<jni::jobject*, mapbox::geometry::geometry<T>> {
- Result<jni::jobject*> operator()(jni::JNIEnv& env, const mapbox::geometry::geometry<T>& value) const {
- GeometryEvaluator<double> evaluator { env } ;
- jni::jobject* converted = mapbox::geometry::geometry<double>::visit(value, evaluator);
- return {converted};
+struct Converter<jni::Object<android::geojson::Geometry>, mapbox::geometry::geometry<T>> {
+ Result<jni::Object<android::geojson::Geometry>> operator()(jni::JNIEnv& env, const mapbox::geometry::geometry<T>& value) const {
+ return { android::geojson::Geometry::New(env, value) };
}
};
-}
-}
-}
+} // conversion
+} // android
+} // mbgl
diff --git a/platform/android/src/geojson/geometry.cpp b/platform/android/src/geojson/geometry.cpp
index ca19d8fb03..5635b5a0f5 100644
--- a/platform/android/src/geojson/geometry.cpp
+++ b/platform/android/src/geojson/geometry.cpp
@@ -6,6 +6,7 @@
#include "multi_line_string.hpp"
#include "polygon.hpp"
#include "multi_polygon.hpp"
+#include "geometry_collection.hpp"
#include <string>
@@ -13,7 +14,49 @@ namespace mbgl {
namespace android {
namespace geojson {
-mapbox::geojson::geometry Geometry::convert(jni::JNIEnv &env, jni::Object<Geometry> jGeometry) {
+/**
+ * Turn mapbox::geometry type into Java GeoJson Geometries
+ */
+class GeometryEvaluator {
+public:
+
+ jni::JNIEnv& env;
+
+ jni::Object<Geometry> operator()(const mbgl::Point<double> &geometry) const {
+ return jni::Cast(env, Point::New(env, geometry), Geometry::javaClass);
+ }
+
+ jni::Object<Geometry> operator()(const mbgl::LineString<double> &geometry) const {
+ return jni::Cast(env, LineString::New(env, geometry), Geometry::javaClass);
+ }
+
+ jni::Object<Geometry> operator()(const mbgl::MultiLineString<double> &geometry) const {
+ return jni::Cast(env, MultiLineString::New(env, geometry), Geometry::javaClass);
+ }
+
+ jni::Object<Geometry> operator()(const mbgl::MultiPoint<double> &geometry) const {
+ return jni::Cast(env, MultiPoint::New(env, geometry), Geometry::javaClass);
+ }
+
+ jni::Object<Geometry> operator()(const mbgl::Polygon<double> &geometry) const {
+ return jni::Cast(env, Polygon::New(env, geometry), Geometry::javaClass);
+ }
+
+ jni::Object<Geometry> operator()(const mbgl::MultiPolygon<double> &geometry) const {
+ return jni::Cast(env, MultiPolygon::New(env, geometry), Geometry::javaClass);
+ }
+
+ jni::Object<Geometry> operator()(const mapbox::geometry::geometry_collection<double> &geometry) const {
+ return jni::Cast(env, GeometryCollection::New(env, geometry), Geometry::javaClass);
+ }
+};
+
+jni::Object<Geometry> Geometry::New(jni::JNIEnv& env, mbgl::Geometry<double> geometry) {
+ GeometryEvaluator evaluator { env } ;
+ return mbgl::Geometry<double>::visit(geometry, evaluator);
+}
+
+mbgl::Geometry<double> Geometry::convert(jni::JNIEnv &env, jni::Object<Geometry> jGeometry) {
auto type = Geometry::getType(env, jGeometry);
if (type == Point::Type()) {
return { Point::convert(env, jni::Object<Point>(jGeometry.Get())) };
@@ -27,6 +70,8 @@ mapbox::geojson::geometry Geometry::convert(jni::JNIEnv &env, jni::Object<Geomet
return { Polygon::convert(env, jni::Object<Polygon>(jGeometry.Get())) };
} else if (type == MultiPolygon::Type()) {
return { MultiPolygon::convert(env, jni::Object<MultiPolygon>(jGeometry.Get())) };
+ } else if (type == GeometryCollection::Type()) {
+ return { GeometryCollection::convert(env, jni::Object<GeometryCollection>(jGeometry.Get())) };
}
throw std::runtime_error(std::string {"Unsupported GeoJSON type: " } + type);
diff --git a/platform/android/src/geojson/geometry.hpp b/platform/android/src/geojson/geometry.hpp
index b7f8909f6f..a1bb886683 100644
--- a/platform/android/src/geojson/geometry.hpp
+++ b/platform/android/src/geojson/geometry.hpp
@@ -1,8 +1,10 @@
#pragma once
-#include <mbgl/util/geojson.hpp>
+#include <mbgl/util/geometry.hpp>
#include <mbgl/util/noncopyable.hpp>
+#include "../java/util.hpp"
+
#include <jni/jni.hpp>
namespace mbgl {
@@ -13,7 +15,9 @@ class Geometry : private mbgl::util::noncopyable {
public:
static constexpr auto Name() { return "com/mapbox/geojson/Geometry"; };
- static mapbox::geojson::geometry convert(jni::JNIEnv&, jni::Object<Geometry>);
+ static jni::Object<Geometry> New(jni::JNIEnv&, mbgl::Geometry<double>);
+
+ static mbgl::Geometry<double> convert(jni::JNIEnv&, jni::Object<Geometry>);
static std::string getType(jni::JNIEnv&, jni::Object<Geometry>);
diff --git a/platform/android/src/geojson/geometry_collection.cpp b/platform/android/src/geojson/geometry_collection.cpp
new file mode 100644
index 0000000000..eb3a790404
--- /dev/null
+++ b/platform/android/src/geojson/geometry_collection.cpp
@@ -0,0 +1,63 @@
+#include "geometry_collection.hpp"
+#include "../java/util.hpp"
+
+namespace mbgl {
+namespace android {
+namespace geojson {
+
+jni::Object<GeometryCollection> GeometryCollection::New(jni::JNIEnv& env, const mapbox::geometry::geometry_collection<double>& collection) {
+ // Create an array of geometries
+ auto jarray = jni::Array<jni::Object<Geometry>>::New(env, collection.size(), Geometry::javaClass);
+
+ for (size_t i = 0; i < collection.size(); i++) {
+ auto& geometry = collection.at(i);
+ auto jGeometry = Geometry::New(env, geometry);
+ jarray.Set(env, i, jGeometry);
+ jni::DeleteLocalRef(env, jGeometry);
+ }
+
+ // Turn into array list
+ auto jList = java::util::Arrays::asList(env, jarray);
+ jni::DeleteLocalRef(env, jarray);
+
+ // create the GeometryCollection
+ static auto method = javaClass.GetStaticMethod<jni::Object<GeometryCollection> (jni::Object<java::util::List>)>(env, "fromGeometries");
+ auto jCollection = javaClass.Call(env, method, jList);
+
+ jni::DeleteLocalRef(env, jList);
+ return jCollection;
+}
+
+mapbox::geometry::geometry_collection<double> GeometryCollection::convert(jni::JNIEnv &env, jni::Object<GeometryCollection> jCollection) {
+ // Get geometries
+ static auto getGeometries = javaClass.GetMethod<jni::Object<java::util::List> ()>(env, "getGeometries");
+ auto jList = jCollection.Call(env, getGeometries);
+
+ // Turn into array
+ auto jarray = java::util::List::toArray<Geometry>(env, jList);
+ jni::DeleteLocalRef(env, jList);
+
+ // Convert each geometry
+ mapbox::geometry::geometry_collection<double> collection{};
+
+ auto size = jarray.Length(env);
+ for (jni::jsize i = 0; i < size; i++) {
+ auto element = jarray.Get(env, i);
+ collection.push_back(Geometry::convert(env, element));
+ jni::DeleteLocalRef(env, element);
+ }
+
+ jni::DeleteLocalRef(env, jarray);
+ return collection;
+}
+
+void GeometryCollection::registerNative(jni::JNIEnv &env) {
+ // Lookup the class
+ javaClass = *jni::Class<GeometryCollection>::Find(env).NewGlobalRef(env).release();
+}
+
+jni::Class<GeometryCollection> GeometryCollection::javaClass;
+
+} // namespace geojson
+} // namespace android
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/geojson/geometry_collection.hpp b/platform/android/src/geojson/geometry_collection.hpp
new file mode 100644
index 0000000000..9ed9953b0d
--- /dev/null
+++ b/platform/android/src/geojson/geometry_collection.hpp
@@ -0,0 +1,28 @@
+#pragma once
+
+#include "geometry.hpp"
+
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace android {
+namespace geojson {
+
+class GeometryCollection : public Geometry {
+public:
+ static constexpr auto Name() { return "com/mapbox/geojson/GeometryCollection"; };
+
+ static constexpr auto Type() { return "GeometryCollection"; };
+
+ static jni::Object<GeometryCollection> New(jni::JNIEnv&, const mapbox::geometry::geometry_collection<double>&);
+
+ static mapbox::geometry::geometry_collection<double> convert(jni::JNIEnv&, jni::Object<GeometryCollection>);
+
+ static jni::Class<GeometryCollection> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+};
+
+} // namespace geojson
+} // namespace android
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/geojson/line_string.cpp b/platform/android/src/geojson/line_string.cpp
index 8eebd53550..a5f1a468ce 100644
--- a/platform/android/src/geojson/line_string.cpp
+++ b/platform/android/src/geojson/line_string.cpp
@@ -1,11 +1,22 @@
#include "line_string.hpp"
-
#include "point.hpp"
+#include "util.hpp"
+#include "../java/util.hpp"
namespace mbgl {
namespace android {
namespace geojson {
+jni::Object<LineString> LineString::New(jni::JNIEnv& env, const mbgl::LineString<double>& lineString) {
+ auto jList = asPointsList(env, lineString);
+
+ static auto method = javaClass.GetStaticMethod<jni::Object<LineString>(jni::Object<java::util::List>)>(env, "fromLngLats");
+ auto jLineString = javaClass.Call(env, method, jList);
+
+ jni::DeleteLocalRef(env, jList);
+ return jLineString;
+}
+
mapbox::geojson::line_string LineString::convert(jni::JNIEnv &env, jni::Object<LineString> jLineString) {
mapbox::geojson::line_string lineString;
diff --git a/platform/android/src/geojson/line_string.hpp b/platform/android/src/geojson/line_string.hpp
index 86033c2e6a..98dc414642 100644
--- a/platform/android/src/geojson/line_string.hpp
+++ b/platform/android/src/geojson/line_string.hpp
@@ -1,23 +1,27 @@
#pragma once
#include <mbgl/util/geojson.hpp>
+#include <mbgl/util/geometry.hpp>
#include <mbgl/util/noncopyable.hpp>
-#include <jni/jni.hpp>
-
+#include "geometry.hpp"
#include "../java/util.hpp"
+#include <jni/jni.hpp>
+
namespace mbgl {
namespace android {
namespace geojson {
-class LineString : private mbgl::util::noncopyable {
+class LineString : public Geometry {
public:
static constexpr auto Name() { return "com/mapbox/geojson/LineString"; };
static constexpr auto Type() { return "LineString"; };
+ static jni::Object<LineString> New(jni::JNIEnv&, const mbgl::LineString<double>&);
+
static mapbox::geojson::line_string convert(jni::JNIEnv&, jni::Object<LineString>);
static mapbox::geojson::line_string convert(jni::JNIEnv&, jni::Object<java::util::List/*<Point>*/>);
diff --git a/platform/android/src/geojson/multi_line_string.cpp b/platform/android/src/geojson/multi_line_string.cpp
index c748d4786f..4a6ea37dd1 100644
--- a/platform/android/src/geojson/multi_line_string.cpp
+++ b/platform/android/src/geojson/multi_line_string.cpp
@@ -1,11 +1,22 @@
#include "multi_line_string.hpp"
#include "line_string.hpp"
+#include "util.hpp"
namespace mbgl {
namespace android {
namespace geojson {
+jni::Object<MultiLineString> MultiLineString::New(jni::JNIEnv& env, const mbgl::MultiLineString<double>& multiLineString) {
+ auto jList = asPointsListsList(env, multiLineString);
+
+ static auto method = javaClass.GetStaticMethod<jni::Object<MultiLineString> (jni::Object<java::util::List>)>(env, "fromLngLats");
+ auto jMultiLineString = javaClass.Call(env, method, jList);
+
+ jni::DeleteLocalRef(env, jList);
+ return jMultiLineString;
+}
+
mapbox::geojson::multi_line_string MultiLineString::convert(jni::JNIEnv &env, jni::Object<MultiLineString> jMultiLineString) {
mapbox::geojson::multi_line_string multiLineString;
diff --git a/platform/android/src/geojson/multi_line_string.hpp b/platform/android/src/geojson/multi_line_string.hpp
index 358a6b5dda..934a0cb6b5 100644
--- a/platform/android/src/geojson/multi_line_string.hpp
+++ b/platform/android/src/geojson/multi_line_string.hpp
@@ -3,20 +3,23 @@
#include <mbgl/util/geojson.hpp>
#include <mbgl/util/noncopyable.hpp>
-#include <jni/jni.hpp>
-
#include "../java/util.hpp"
+#include "geometry.hpp"
+
+#include <jni/jni.hpp>
namespace mbgl {
namespace android {
namespace geojson {
-class MultiLineString : private mbgl::util::noncopyable {
+class MultiLineString : public Geometry {
public:
static constexpr auto Name() { return "com/mapbox/geojson/MultiLineString"; };
static constexpr auto Type() { return "MultiLineString"; };
+ static jni::Object<MultiLineString> New(jni::JNIEnv&, const mbgl::MultiLineString<double>&);
+
static mapbox::geojson::multi_line_string convert(jni::JNIEnv&, jni::Object<MultiLineString>);
static mapbox::geojson::multi_line_string convert(jni::JNIEnv&, jni::Object<java::util::List/*<java::util::List<Point>>*/>);
diff --git a/platform/android/src/geojson/multi_point.cpp b/platform/android/src/geojson/multi_point.cpp
index 4f9ff596b2..6f62541209 100644
--- a/platform/android/src/geojson/multi_point.cpp
+++ b/platform/android/src/geojson/multi_point.cpp
@@ -8,6 +8,16 @@ namespace mbgl {
namespace android {
namespace geojson {
+jni::Object<MultiPoint> MultiPoint::New(JNIEnv& env, const mbgl::MultiPoint<double>& multiPoint) {
+ auto jList = asPointsList(env, multiPoint);
+
+ static auto method = javaClass.GetStaticMethod<jni::Object<MultiPoint>(jni::Object<java::util::List>)>(env, "fromLngLats");
+ auto jMultiPoint = javaClass.Call(env, method, jList);
+
+ jni::DeleteLocalRef(env, jList);
+ return jMultiPoint;
+}
+
mapbox::geojson::multi_point MultiPoint::convert(jni::JNIEnv &env, jni::Object<MultiPoint> jMultiPoint) {
mapbox::geojson::multi_point multiPoint;
diff --git a/platform/android/src/geojson/multi_point.hpp b/platform/android/src/geojson/multi_point.hpp
index e893e879af..cfe80cd34c 100644
--- a/platform/android/src/geojson/multi_point.hpp
+++ b/platform/android/src/geojson/multi_point.hpp
@@ -1,22 +1,26 @@
#pragma once
#include <mbgl/util/geojson.hpp>
+#include <mbgl/util/geometry.hpp>
#include <mbgl/util/noncopyable.hpp>
-#include <jni/jni.hpp>
-
+#include "geometry.hpp"
#include "../java/util.hpp"
+#include <jni/jni.hpp>
+
namespace mbgl {
namespace android {
namespace geojson {
-class MultiPoint : private mbgl::util::noncopyable {
+class MultiPoint : public Geometry {
public:
static constexpr auto Name() { return "com/mapbox/geojson/MultiPoint"; };
static constexpr auto Type() { return "MultiPoint"; };
+ static jni::Object<MultiPoint> New(jni::JNIEnv&, const mbgl::MultiPoint<double>&);
+
static mapbox::geojson::multi_point convert(jni::JNIEnv&, jni::Object<MultiPoint>);
static jni::Object<java::util::List> coordinates(jni::JNIEnv&, jni::Object<MultiPoint>);
diff --git a/platform/android/src/geojson/multi_polygon.cpp b/platform/android/src/geojson/multi_polygon.cpp
index aadba8c8a6..cc872d4955 100644
--- a/platform/android/src/geojson/multi_polygon.cpp
+++ b/platform/android/src/geojson/multi_polygon.cpp
@@ -1,11 +1,34 @@
#include "multi_polygon.hpp"
#include "polygon.hpp"
+#include "util.hpp"
namespace mbgl {
namespace android {
namespace geojson {
+jni::Object<MultiPolygon> MultiPolygon::New(JNIEnv& env, const mbgl::MultiPolygon<double>& multiPolygon) {
+ auto jarray = jni::Array<jni::Object<java::util::List>>::New(env, multiPolygon.size(), java::util::List::javaClass);
+
+ for (size_t i = 0; i < multiPolygon.size(); i++) {
+ auto& geometry = multiPolygon.at(i);
+ auto jPolygon = asPointsListsList(env, geometry);
+ jarray.Set(env, i, jPolygon);
+ jni::DeleteLocalRef(env, jPolygon);
+ }
+
+ // Turn into array list
+ auto jList = java::util::Arrays::asList(env, jarray);
+ jni::DeleteLocalRef(env, jarray);
+
+ // create the MultiPolygon
+ static auto method = javaClass.GetStaticMethod<jni::Object<MultiPolygon> (jni::Object<java::util::List>)>(env, "fromLngLats");
+ auto jMultiPolygon = javaClass.Call(env, method, jList);
+
+ jni::DeleteLocalRef(env, jList);
+ return jMultiPolygon;
+}
+
mapbox::geojson::multi_polygon MultiPolygon::convert(jni::JNIEnv &env, jni::Object<MultiPolygon> jMultiPolygon) {
mapbox::geojson::multi_polygon multiPolygon;
diff --git a/platform/android/src/geojson/multi_polygon.hpp b/platform/android/src/geojson/multi_polygon.hpp
index 6e1dfacfc8..b4657af09d 100644
--- a/platform/android/src/geojson/multi_polygon.hpp
+++ b/platform/android/src/geojson/multi_polygon.hpp
@@ -3,20 +3,23 @@
#include <mbgl/util/geojson.hpp>
#include <mbgl/util/noncopyable.hpp>
-#include <jni/jni.hpp>
-
#include "../java/util.hpp"
+#include "geometry.hpp"
+
+#include <jni/jni.hpp>
namespace mbgl {
namespace android {
namespace geojson {
-class MultiPolygon : private mbgl::util::noncopyable {
+class MultiPolygon : public Geometry {
public:
static constexpr auto Name() { return "com/mapbox/geojson/MultiPolygon"; };
static constexpr auto Type() { return "MultiPolygon"; };
+ static jni::Object<MultiPolygon> New(jni::JNIEnv&, const mbgl::MultiPolygon<double>&);
+
static mapbox::geojson::multi_polygon convert(jni::JNIEnv&, jni::Object<MultiPolygon>);
static jni::Object<java::util::List> coordinates(jni::JNIEnv&, jni::Object<MultiPolygon>);
diff --git a/platform/android/src/geojson/point.cpp b/platform/android/src/geojson/point.cpp
index e95376cd2e..aa9dc1a7f6 100644
--- a/platform/android/src/geojson/point.cpp
+++ b/platform/android/src/geojson/point.cpp
@@ -7,6 +7,11 @@ namespace mbgl {
namespace android {
namespace geojson {
+jni::Object<Point> Point::New(jni::JNIEnv& env, const mbgl::Point<double>& point) {
+ static auto method = javaClass.GetStaticMethod<jni::Object<Point> (jni::jdouble, jni::jdouble)>(env, "fromLngLat");
+ return javaClass.Call(env, method, point.x, point.y);
+}
+
mapbox::geojson::point Point::convert(jni::JNIEnv &env, jni::Object<Point> jPoint) {
mapbox::geojson::point point;
diff --git a/platform/android/src/geojson/point.hpp b/platform/android/src/geojson/point.hpp
index c6412299bf..627bd1b649 100644
--- a/platform/android/src/geojson/point.hpp
+++ b/platform/android/src/geojson/point.hpp
@@ -3,20 +3,23 @@
#include <mbgl/util/geojson.hpp>
#include <mbgl/util/noncopyable.hpp>
-#include <jni/jni.hpp>
-
#include "../java/util.hpp"
+#include "geometry.hpp"
+
+#include <jni/jni.hpp>
namespace mbgl {
namespace android {
namespace geojson {
-class Point : private mbgl::util::noncopyable {
+class Point : public Geometry {
public:
static constexpr auto Name() { return "com/mapbox/geojson/Point"; };
static constexpr auto Type() { return "Point"; };
+ static jni::Object<Point> New(jni::JNIEnv&, const mbgl::Point<double>&);
+
static mapbox::geojson::point convert(jni::JNIEnv&, jni::Object<Point>);
static mapbox::geojson::point convert(jni::JNIEnv&, jni::Object<java::util::List/*<Double>*/>);
diff --git a/platform/android/src/geojson/polygon.cpp b/platform/android/src/geojson/polygon.cpp
index 30ba996640..96058b63b3 100644
--- a/platform/android/src/geojson/polygon.cpp
+++ b/platform/android/src/geojson/polygon.cpp
@@ -8,6 +8,16 @@ namespace mbgl {
namespace android {
namespace geojson {
+jni::Object<Polygon> Polygon::New(jni::JNIEnv& env, const mbgl::Polygon<double>& polygon) {
+ auto jList = asPointsListsList(env, polygon);
+
+ static auto method = javaClass.GetStaticMethod<jni::Object<Polygon> (jni::Object<java::util::List>)>(env, "fromLngLats");
+ auto jPolygon = javaClass.Call(env, method, jList);
+
+ jni::DeleteLocalRef(env, jList);
+ return jPolygon;
+}
+
mapbox::geojson::polygon Polygon::convert(jni::JNIEnv &env, jni::Object<Polygon> jPolygon) {
mapbox::geojson::polygon polygon;
diff --git a/platform/android/src/geojson/polygon.hpp b/platform/android/src/geojson/polygon.hpp
index a8b2b93827..f3c23b4d7b 100644
--- a/platform/android/src/geojson/polygon.hpp
+++ b/platform/android/src/geojson/polygon.hpp
@@ -3,20 +3,24 @@
#include <mbgl/util/geojson.hpp>
#include <mbgl/util/noncopyable.hpp>
+#include "geometry.hpp"
+#include "../java/util.hpp"
+
#include <jni/jni.hpp>
-#include "../java/util.hpp"
namespace mbgl {
namespace android {
namespace geojson {
-class Polygon : private mbgl::util::noncopyable {
+class Polygon : public Geometry {
public:
static constexpr auto Name() { return "com/mapbox/geojson/Polygon"; };
static constexpr auto Type() { return "Polygon"; };
+ static jni::Object<Polygon> New(jni::JNIEnv&, const mbgl::Polygon<double>&);
+
static mapbox::geojson::polygon convert(jni::JNIEnv &, jni::Object<Polygon>);
static mapbox::geojson::polygon convert(jni::JNIEnv&, jni::Object<java::util::List/*<java::util::List<Point>>*/>);
diff --git a/platform/android/src/geojson/util.hpp b/platform/android/src/geojson/util.hpp
index ece8e52433..5e6d90a953 100644
--- a/platform/android/src/geojson/util.hpp
+++ b/platform/android/src/geojson/util.hpp
@@ -1,5 +1,7 @@
#pragma once
+#include "point.hpp"
+
#include <type_traits>
namespace mbgl {
@@ -17,6 +19,42 @@ To convertExplicit(From&& src) {
return *reinterpret_cast<std::add_pointer_t<To>>(&src);
}
+/**
+ * Geometry -> List<Point>
+ */
+template <class T>
+static jni::Object<java::util::List> asPointsList(jni::JNIEnv& env, const T& pointsList) {
+ auto jarray = jni::Array<jni::Object<Point>>::New(env, pointsList.size(), Point::javaClass);
+
+ for (jni::jsize i = 0; i < pointsList.size(); i++) {
+ auto jPoint = Point::New(env, pointsList.at(i));
+ jarray.Set(env, i, jPoint);
+ jni::DeleteLocalRef(env, jPoint);
+ }
+
+ auto jList = java::util::Arrays::asList(env, jarray);
+ jni::DeleteLocalRef(env, jarray);
+ return jList;
+}
+
+/**
+ * Geometry -> List<List<Point>>
+ */
+template <class SHAPE>
+static jni::Object<java::util::List> asPointsListsList(JNIEnv& env, SHAPE value) {
+ auto jarray = jni::Array<jni::Object<java::util::List>>::New(env, value.size(), java::util::List::javaClass);
+
+ for (size_t i = 0; i < value.size(); i = i + 1) {
+ auto pointsList = asPointsList(env, value[i]);
+ jarray.Set(env, i, pointsList);
+ jni::DeleteLocalRef(env, pointsList);
+ }
+
+ auto jList = java::util::Arrays::asList(env, jarray);
+ jni::DeleteLocalRef(env, jarray);
+ return jList;
+}
+
} // namespace geojson
} // namespace android
} // namespace mbgl
diff --git a/platform/android/src/java/util.cpp b/platform/android/src/java/util.cpp
index effd2ae0d0..89c4c77794 100644
--- a/platform/android/src/java/util.cpp
+++ b/platform/android/src/java/util.cpp
@@ -5,12 +5,14 @@ namespace android {
namespace java {
namespace util {
+jni::Class<Arrays> Arrays::javaClass;
jni::Class<List> List::javaClass;
jni::Class<Set> Set::javaClass;
jni::Class<Map> Map::javaClass;
jni::Class<Map::Entry> Map::Entry::javaClass;
void registerNative(jni::JNIEnv& env) {
+ Arrays::javaClass = *jni::Class<Arrays>::Find(env).NewGlobalRef(env).release();
List::javaClass = *jni::Class<List>::Find(env).NewGlobalRef(env).release();
Set::javaClass = *jni::Class<Set>::Find(env).NewGlobalRef(env).release();
Map::javaClass = *jni::Class<Map>::Find(env).NewGlobalRef(env).release();
diff --git a/platform/android/src/java/util.hpp b/platform/android/src/java/util.hpp
index dedb8ac348..c6b07acac5 100644
--- a/platform/android/src/java/util.hpp
+++ b/platform/android/src/java/util.hpp
@@ -24,6 +24,21 @@ public:
};
+class Arrays : private mbgl::util::noncopyable {
+public:
+
+ static constexpr auto Name() { return "java/util/Arrays"; };
+
+ template <class T>
+ static jni::Object<List> asList(jni::JNIEnv& env, jni::Array<jni::Object<T>> array) {
+ static auto asList = Arrays::javaClass.GetStaticMethod<jni::Object<List>(jni::Array<jni::Object<>>)>(env, "asList");
+ return javaClass.Call(env, asList, (jni::Array<jni::Object<>>) array);
+ }
+
+ static jni::Class<Arrays> javaClass;
+
+};
+
class Set : private mbgl::util::noncopyable {
public:
diff --git a/platform/android/src/jni.cpp b/platform/android/src/jni.cpp
index 68db977407..c8c20e939a 100755
--- a/platform/android/src/jni.cpp
+++ b/platform/android/src/jni.cpp
@@ -14,6 +14,7 @@
#include "geojson/feature.hpp"
#include "geojson/feature_collection.hpp"
#include "geojson/geometry.hpp"
+#include "geojson/geometry_collection.hpp"
#include "geojson/line_string.hpp"
#include "geojson/multi_line_string.hpp"
#include "geojson/multi_point.hpp"
@@ -116,6 +117,7 @@ void registerNatives(JavaVM *vm) {
geojson::Feature::registerNative(env);
geojson::FeatureCollection::registerNative(env);
geojson::Geometry::registerNative(env);
+ geojson::GeometryCollection::registerNative(env);
geojson::LineString::registerNative(env);
geojson::MultiLineString::registerNative(env);
geojson::MultiPoint::registerNative(env);
diff --git a/platform/android/src/style/layers/layer.cpp b/platform/android/src/style/layers/layer.cpp
index 29530879a5..a2f4087fce 100644
--- a/platform/android/src/style/layers/layer.cpp
+++ b/platform/android/src/style/layers/layer.cpp
@@ -25,6 +25,8 @@
// C++ -> Java conversion
#include "../conversion/property_value.hpp"
+#include <mbgl/style/filter.hpp>
+#include "../conversion/gson.hpp"
#include <string>
@@ -134,6 +136,39 @@ namespace android {
layer.accept(SetFilterEvaluator {std::move(*converted)});
}
+ struct GetFilterEvaluator {
+ mbgl::style::Filter noop(std::string layerType) {
+ Log::Warning(mbgl::Event::JNI, "%s doesn't support filter", layerType.c_str());
+ return {};
+ }
+
+ mbgl::style::Filter operator()(style::BackgroundLayer&) { return noop("BackgroundLayer"); }
+ mbgl::style::Filter operator()(style::CustomLayer&) { return noop("CustomLayer"); }
+ mbgl::style::Filter operator()(style::RasterLayer&) { return noop("RasterLayer"); }
+ mbgl::style::Filter operator()(style::HillshadeLayer&) { return noop("HillshadeLayer"); }
+
+ template <class LayerType>
+ mbgl::style::Filter operator()(LayerType& layer) {
+ return layer.getFilter();
+ }
+ };
+
+ jni::Object<gson::JsonArray> Layer::getFilter(jni::JNIEnv& env) {
+ using namespace mbgl::style;
+ using namespace mbgl::style::conversion;
+
+ Filter filter = layer.accept(GetFilterEvaluator());
+
+ jni::jobject* converted = nullptr;
+ if (filter.is<ExpressionFilter>()) {
+ ExpressionFilter filterExpression = filter.get<ExpressionFilter>();
+ mbgl::Value expressionValue = filterExpression.expression.get()->serialize();
+ conversion::JsonEvaluator jsonEvaluator{env};
+ converted = apply_visitor(jsonEvaluator, expressionValue);
+ }
+ return jni::Object<gson::JsonArray>(converted);
+ }
+
struct SetSourceLayerEvaluator {
std::string sourceLayer;
@@ -208,6 +243,7 @@ namespace android {
METHOD(&Layer::setLayoutProperty, "nativeSetLayoutProperty"),
METHOD(&Layer::setPaintProperty, "nativeSetPaintProperty"),
METHOD(&Layer::setFilter, "nativeSetFilter"),
+ METHOD(&Layer::getFilter, "nativeGetFilter"),
METHOD(&Layer::setSourceLayer, "nativeSetSourceLayer"),
METHOD(&Layer::getSourceLayer, "nativeGetSourceLayer"),
METHOD(&Layer::getMinZoom, "nativeGetMinZoom"),
diff --git a/platform/android/src/style/layers/layer.hpp b/platform/android/src/style/layers/layer.hpp
index 78c3f80b48..19713a1baa 100644
--- a/platform/android/src/style/layers/layer.hpp
+++ b/platform/android/src/style/layers/layer.hpp
@@ -3,7 +3,7 @@
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/map/map.hpp>
#include <mbgl/style/layer.hpp>
-
+#include "../../gson/json_array.hpp"
#include "../value.hpp"
#include <jni/jni.hpp>
@@ -68,6 +68,8 @@ public:
void setFilter(jni::JNIEnv&, jni::Array<jni::Object<>>);
+ jni::Object<gson::JsonArray> getFilter(jni::JNIEnv&);
+
void setSourceLayer(jni::JNIEnv&, jni::String);
jni::String getSourceLayer(jni::JNIEnv&);
diff --git a/platform/darwin/src/MGLPointAnnotation.h b/platform/darwin/src/MGLPointAnnotation.h
index 1ef0962f99..78b92f45ac 100644
--- a/platform/darwin/src/MGLPointAnnotation.h
+++ b/platform/darwin/src/MGLPointAnnotation.h
@@ -28,7 +28,8 @@ NS_ASSUME_NONNULL_BEGIN
default content of the annotation’s callout (on iOS) or popover (on macOS).
To group multiple related points together in one shape, use an
- `MGLPointCollection` or `MGLShapeCollection` object.
+ `MGLPointCollection` or `MGLShapeCollection` object. To access
+ a point’s attributes, use an `MGLPointFeature` object.
A point shape is known as a
<a href="https://tools.ietf.org/html/rfc7946#section-3.1.2">Point</a> geometry
diff --git a/platform/darwin/src/MGLPointCollection.h b/platform/darwin/src/MGLPointCollection.h
index 74b30385a0..5aab580b4d 100644
--- a/platform/darwin/src/MGLPointCollection.h
+++ b/platform/darwin/src/MGLPointCollection.h
@@ -15,7 +15,8 @@
You can add point collections to the map by adding them to an `MGLShapeSource`
object. Configure the appearance of an `MGLShapeSource`’s or
`MGLVectorSource`’s point collections collectively using an
- `MGLCircleStyleLayer` or `MGLSymbolStyleLayer` object.
+ `MGLCircleStyleLayer` or `MGLSymbolStyleLayer` object. To access a point
+ collection’s attributes, use an `MGLPointCollectionFeature` object.
You cannot add an `MGLPointCollection` object directly to a map view as an
annotation. However, you can create individual `MGLPointAnnotation` objects
diff --git a/platform/darwin/src/MGLPolygon.h b/platform/darwin/src/MGLPolygon.h
index 3fcc1be76d..bdd2e1eeab 100644
--- a/platform/darwin/src/MGLPolygon.h
+++ b/platform/darwin/src/MGLPolygon.h
@@ -18,7 +18,8 @@ NS_ASSUME_NONNULL_BEGIN
You can add polygon shapes to the map by adding them to an `MGLShapeSource`
object. Configure the appearance of an `MGLShapeSource`’s or
`MGLVectorSource`’s polygons collectively using an `MGLFillStyleLayer` or
- `MGLSymbolStyleLayer` object.
+ `MGLSymbolStyleLayer` object. To access a polygon’s attributes, use an
+ `MGLPolygonFeature` object.
Alternatively, you can add a polygon overlay directly to a map view using the
`-[MGLMapView addAnnotation:]` or `-[MGLMapView addOverlay:]` method. Configure
diff --git a/platform/darwin/src/MGLPolyline.h b/platform/darwin/src/MGLPolyline.h
index e46baa91cc..1ed3dd0b9f 100644
--- a/platform/darwin/src/MGLPolyline.h
+++ b/platform/darwin/src/MGLPolyline.h
@@ -18,7 +18,8 @@ NS_ASSUME_NONNULL_BEGIN
You can add polyline shapes to the map by adding them to an `MGLShapeSource`
object. Configure the appearance of an `MGLShapeSource`’s or
`MGLVectorSource`’s polylines collectively using an `MGLLineStyleLayer` or
- `MGLSymbolStyleLayer` object.
+ `MGLSymbolStyleLayer` object. To access a polyline’s attributes, use an
+ `MGLPolylineFeature` object.
Alternatively, you can add a polyline overlay directly to a map view using the
`-[MGLMapView addAnnotation:]` or `-[MGLMapView addOverlay:]` method. Configure
diff --git a/platform/darwin/src/MGLShape.h b/platform/darwin/src/MGLShape.h
index e965710552..4bab2b81c1 100644
--- a/platform/darwin/src/MGLShape.h
+++ b/platform/darwin/src/MGLShape.h
@@ -20,7 +20,8 @@ NS_ASSUME_NONNULL_BEGIN
Although you do not create instances of this class directly, you can use its
`+[MGLShape shapeWithData:encoding:error:]` factory method to create one of the
- concrete subclasses of `MGLShape` noted above from GeoJSON data.
+ concrete subclasses of `MGLShape` noted above from GeoJSON data. To access a
+ shape’s attributes, use the corresponding `MGLFeature` class instead.
You can add shapes to the map by adding them to an `MGLShapeSource` object.
Configure the appearance of an `MGLShapeSource`’s or `MGLVectorSource`’s shapes
diff --git a/platform/darwin/src/MGLShapeCollection.h b/platform/darwin/src/MGLShapeCollection.h
index bb107ee7f0..094c630167 100644
--- a/platform/darwin/src/MGLShapeCollection.h
+++ b/platform/darwin/src/MGLShapeCollection.h
@@ -27,7 +27,8 @@ NS_ASSUME_NONNULL_BEGIN
To represent a collection of point, polyline, or polygon shapes, it may be more
convenient to use an `MGLPointCollection`, `MGLMultiPolyline`, or
- `MGLMultiPolygon` object, respectively.
+ `MGLMultiPolygon` object, respectively. To access a shape collection’s attributes,
+ use the corresponding `MGLFeature` object.
A shape collection is known as a
<a href="https://tools.ietf.org/html/rfc7946#section-3.1.8">GeometryCollection</a>
diff --git a/platform/darwin/src/NSExpression+MGLAdditions.mm b/platform/darwin/src/NSExpression+MGLAdditions.mm
index 5ad565c398..9797709b7b 100644
--- a/platform/darwin/src/NSExpression+MGLAdditions.mm
+++ b/platform/darwin/src/NSExpression+MGLAdditions.mm
@@ -258,6 +258,12 @@
return [self valueForKeyPath:@"mgl_jsonExpressionObject"];
}
+- (id)mgl_coalesce {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Coalesce expressions lack underlying Objective-C implementations."];
+ return nil;
+}
+
@end
@implementation NSDictionary (MGLExpressionAdditions)
@@ -464,18 +470,43 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) {
} else if ([op isEqualToString:@"var"]) {
return [NSExpression expressionForVariable:argumentObjects.firstObject];
} else if ([op isEqualToString:@"case"]) {
- NSPredicate *conditional = [NSPredicate mgl_predicateWithJSONObject:argumentObjects.firstObject];
- NSExpression *trueExpression = [NSExpression mgl_expressionWithJSONObject:argumentObjects[1]];
- NSExpression *falseExpression;
- if (argumentObjects.count > 3) {
- NSArray *falseObjects = [@[@"case"] arrayByAddingObjectsFromArray:
- [argumentObjects subarrayWithRange:NSMakeRange(2, argumentObjects.count - 2)]];
- falseExpression = [NSExpression mgl_expressionWithJSONObject:falseObjects];
- } else {
- falseExpression = [NSExpression mgl_expressionWithJSONObject:argumentObjects[2]];
+ NSArray *caseExpressions = argumentObjects;
+ NSExpression *firstConditional = [NSExpression expressionWithFormat:@"%@", [NSPredicate mgl_predicateWithJSONObject:caseExpressions[0]]];
+ NSMutableArray *arguments = [NSMutableArray array];
+
+ for (NSUInteger index = 1; index < caseExpressions.count; index++) {
+ if ([caseExpressions[index] isKindOfClass:[NSArray class]]) {
+ NSPredicate *conditional = [NSPredicate mgl_predicateWithJSONObject:caseExpressions[index]];
+ NSExpression *argument = [NSExpression expressionWithFormat:@"%@", conditional];
+ [arguments addObject:argument];
+ } else {
+ [arguments addObject:[NSExpression mgl_expressionWithJSONObject:caseExpressions[index]]];
+ }
+ }
+
+ return [NSExpression expressionForFunction:firstConditional selectorName:@"mgl_case:" arguments:arguments];
+ } else if ([op isEqualToString:@"match"]) {
+ NSExpression *operand = [NSExpression mgl_expressionWithJSONObject:argumentObjects[0]];
+ NSArray *matchOptions = [argumentObjects subarrayWithRange:NSMakeRange(1, argumentObjects.count - 1)];
+
+ NSMutableArray *optionsArray = [NSMutableArray array];
+ NSEnumerator *optionsEnumerator = matchOptions.objectEnumerator;
+ while (id object = optionsEnumerator.nextObject) {
+ NSExpression *option = [NSExpression mgl_expressionWithJSONObject:object];
+ [optionsArray addObject:option];
+ }
+
+ return [NSExpression expressionForFunction:operand
+ selectorName:@"mgl_match:"
+ arguments:optionsArray];
+ } else if ([op isEqualToString:@"coalesce"]) {
+ NSMutableArray *expressions = [NSMutableArray array];
+ for (id operand in argumentObjects) {
+ [expressions addObject:[NSExpression mgl_expressionWithJSONObject:operand]];
}
- return [NSExpression expressionForConditional:conditional trueExpression:trueExpression falseExpression:falseExpression];
- } else {
+
+ return [NSExpression expressionWithFormat:@"FUNCTION(%@, 'mgl_coalesce')", expressions];
+ }else {
[NSException raise:NSInvalidArgumentException
format:@"Expression operator %@ not yet implemented.", op];
}
@@ -666,6 +697,37 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) {
}];
[expressionObject addObject:self.operand.mgl_jsonExpressionObject];
return expressionObject;
+ } else if ([function isEqualToString:@"mgl_case:"]) {
+ NSPredicate *firstConditional = (NSPredicate *)self.operand.constantValue;
+ NSMutableArray *expressionObject = [NSMutableArray arrayWithObjects:@"case", firstConditional.mgl_jsonExpressionObject, nil];
+
+ for (NSExpression *option in self.arguments) {
+ if ([option respondsToSelector:@selector(constantValue)] && [option.constantValue isKindOfClass:[NSComparisonPredicate class]]) {
+ NSPredicate *predicate = (NSPredicate *)option.constantValue;
+ [expressionObject addObject:predicate.mgl_jsonExpressionObject];
+ } else {
+ [expressionObject addObject:option.mgl_jsonExpressionObject];
+ }
+ }
+
+ return expressionObject;
+ } else if ([function isEqualToString:@"mgl_match:"]) {
+ NSMutableArray *expressionObject = [NSMutableArray arrayWithObjects:@"match", self.operand.mgl_jsonExpressionObject, nil];
+
+
+ for (NSExpression *option in self.arguments) {
+ [expressionObject addObject:option.mgl_jsonExpressionObject];
+ }
+
+ return expressionObject;
+ } else if ([function isEqualToString:@"mgl_coalesce"]) {
+ NSMutableArray *expressionObject = [NSMutableArray arrayWithObjects:@"coalesce", nil];
+
+ for (NSExpression *expression in self.operand.constantValue) {
+ [expressionObject addObject:[expression mgl_jsonExpressionObject]];
+ }
+
+ return expressionObject;
} else if ([function isEqualToString:@"median:"] ||
[function isEqualToString:@"mode:"] ||
[function isEqualToString:@"stddev:"] ||
@@ -690,7 +752,17 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) {
}
case NSConditionalExpressionType: {
- NSMutableArray *arguments = [NSMutableArray arrayWithObjects:self.predicate.mgl_jsonExpressionObject, self.trueExpression.mgl_jsonExpressionObject, nil];
+ NSMutableArray *arguments = [NSMutableArray arrayWithObjects:self.predicate.mgl_jsonExpressionObject, nil];
+
+ if (self.trueExpression.expressionType == NSConditionalExpressionType) {
+ // Fold nested conditionals into a single case expression.
+ NSArray *trueArguments = self.trueExpression.mgl_jsonExpressionObject;
+ trueArguments = [trueArguments subarrayWithRange:NSMakeRange(1, trueArguments.count - 1)];
+ [arguments addObjectsFromArray:trueArguments];
+ } else {
+ [arguments addObject:self.trueExpression.mgl_jsonExpressionObject];
+ }
+
if (self.falseExpression.expressionType == NSConditionalExpressionType) {
// Fold nested conditionals into a single case expression.
NSArray *falseArguments = self.falseExpression.mgl_jsonExpressionObject;
diff --git a/platform/darwin/src/NSExpression+MGLPrivateAdditions.h b/platform/darwin/src/NSExpression+MGLPrivateAdditions.h
index 8d1b4d6af5..b16e2625a0 100644
--- a/platform/darwin/src/NSExpression+MGLPrivateAdditions.h
+++ b/platform/darwin/src/NSExpression+MGLPrivateAdditions.h
@@ -54,6 +54,8 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, readonly) id mgl_jsonExpressionObject;
+@property (nonatomic, readonly) id mgl_coalesce;
+
@end
@interface NSDictionary (MGLExpressionAdditions)
diff --git a/platform/darwin/src/NSPredicate+MGLAdditions.h b/platform/darwin/src/NSPredicate+MGLAdditions.h
index 89e9e65c64..a67c6ca005 100644
--- a/platform/darwin/src/NSPredicate+MGLAdditions.h
+++ b/platform/darwin/src/NSPredicate+MGLAdditions.h
@@ -16,4 +16,8 @@
@property (nonatomic, readonly) id mgl_jsonExpressionObject;
+- (id)mgl_case:(id)firstValue, ...;
+
+- (id)mgl_match:(NSExpression *)firstCase, ...;
+
@end
diff --git a/platform/darwin/src/NSPredicate+MGLAdditions.mm b/platform/darwin/src/NSPredicate+MGLAdditions.mm
index 63c8307803..1a7bb30a92 100644
--- a/platform/darwin/src/NSPredicate+MGLAdditions.mm
+++ b/platform/darwin/src/NSPredicate+MGLAdditions.mm
@@ -324,4 +324,35 @@ NSArray *MGLSubpredicatesWithJSONObjects(NSArray *objects) {
return nil;
}
+- (id)mgl_case:(id)firstValue, ... {
+
+ if ([self evaluateWithObject:nil]) {
+ return firstValue;
+ }
+
+ id eachExpression;
+ va_list argumentList;
+ va_start(argumentList, firstValue);
+
+ while ((eachExpression = va_arg(argumentList, id))) {
+ if ([eachExpression isKindOfClass:[NSComparisonPredicate class]]) {
+ id valueExpression = va_arg(argumentList, id);
+ if ([eachExpression evaluateWithObject:nil]) {
+ return valueExpression;
+ }
+ } else {
+ return eachExpression;
+ }
+ }
+ va_end(argumentList);
+
+ return nil;
+}
+
+- (id)mgl_match:(NSExpression *)firstCase, ... {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Match expressions lack underlying Objective-C implementations."];
+ return nil;
+}
+
@end
diff --git a/platform/darwin/test/MGLExpressionTests.mm b/platform/darwin/test/MGLExpressionTests.mm
index a5ed2f7bf5..ea5a1cd41c 100644
--- a/platform/darwin/test/MGLExpressionTests.mm
+++ b/platform/darwin/test/MGLExpressionTests.mm
@@ -572,29 +572,61 @@ using namespace std::string_literals;
}
}
+- (void)testMatchExpressionObject {
+ {
+ NSExpression *expression = [NSExpression expressionWithFormat:@"FUNCTION(2 - 1, 'mgl_match:', %@, %@, %@, %@, 'default')", MGLConstantExpression(@1),
+ MGLConstantExpression(@"one"),
+ MGLConstantExpression(@0),
+ MGLConstantExpression(@"zero")];
+ NSArray *jsonExpression = @[@"match", @[@"-", @2, @1], @1, @"one", @0, @"zero", @"default"];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionWithFormat:@"FUNCTION(2 * 1, 'mgl_match:', %@, %@, 'default')", MGLConstantExpression(@1), MGLConstantExpression(@"one")];
+ NSArray *jsonExpression = @[@"match", @[@"*", @2, @1], @1, @"one", @"default"];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ }
+}
+
+- (void)testCoalesceExpressionObject {
+ {
+ NSExpression *expression = [NSExpression expressionWithFormat:@"FUNCTION(%@, 'mgl_coalesce')", @[[NSExpression expressionForKeyPath:@"x"],
+ [NSExpression expressionForKeyPath:@"y"],
+ [NSExpression expressionForKeyPath:@"z"],
+ [NSExpression expressionForConstantValue:@0]]];
+ NSArray *jsonExpression = @[@"coalesce", @[@"get", @"x"], @[@"get", @"y"], @[@"get", @"z"], @0];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ }
+
+}
+
- (void)testConditionalExpressionObject {
- // FIXME: This test crashes because iOS 8 doesn't have `+[NSExpression expressionForConditional:trueExpression:falseExpression:]`.
- // https://github.com/mapbox/mapbox-gl-native/issues/11007
- if (@available(iOS 9.0, *)) {
- {
- NSPredicate *conditional = [NSPredicate predicateWithFormat:@"1 = 2"];
- NSExpression *trueExpression = [NSExpression expressionForConstantValue:@YES];
- NSExpression *falseExpression = [NSExpression expressionForConstantValue:@NO];
- NSExpression *expression = [NSExpression expressionForConditional:conditional trueExpression:trueExpression falseExpression:falseExpression];
- NSArray *jsonExpression = @[@"case", @[@"==", @1, @2], @YES, @NO];
- XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
- XCTAssertEqualObjects([NSExpression expressionWithFormat:@"TERNARY(1 = 2, TRUE, FALSE)"].mgl_jsonExpressionObject, jsonExpression);
- XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @NO);
- XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
- }
- {
- NSExpression *expression = [NSExpression expressionWithFormat:@"TERNARY(0 = 1, TRUE, TERNARY(1 = 2, TRUE, FALSE))"];
- NSArray *jsonExpression = @[@"case", @[@"==", @0, @1], @YES, @[@"==", @1, @2], @YES, @NO];
- XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
- XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @NO);
- XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
- }
+ {
+ NSExpression *expression = [NSExpression expressionWithFormat:@"FUNCTION(%@, 'mgl_case:', %@, %@)",
+ [NSExpression expressionWithFormat:@"%@", [NSPredicate predicateWithFormat:@"1 = 2"]],
+ MGLConstantExpression(@YES),
+ MGLConstantExpression(@NO)];
+ NSArray *jsonExpression = @[@"case", @[@"==", @1, @2], @YES, @NO];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @NO);
}
+ {
+ NSExpression *expression = [NSExpression expressionWithFormat:@"FUNCTION(%@, 'mgl_case:', %@, %@, %@, %@)",
+ [NSExpression expressionWithFormat:@"%@", [NSPredicate predicateWithFormat:@"1 = 2"]],
+ MGLConstantExpression(@YES),
+ [NSExpression expressionWithFormat:@"%@", [NSPredicate predicateWithFormat:@"1 = 1"]],
+ MGLConstantExpression(@YES),
+ MGLConstantExpression(@NO)];
+ NSArray *jsonExpression = @[@"case", @[@"==", @1, @2], @YES, @[@"==", @1, @1], @YES, @NO];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @YES);
+ }
+
}
@end
diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md
index 5ef1039d00..0279653451 100644
--- a/platform/ios/CHANGELOG.md
+++ b/platform/ios/CHANGELOG.md
@@ -37,6 +37,8 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT
* Improved performance of `MGLAnnotationView`-backed annotations that have `scalesWithViewingDistance` enabled. ([#10951](https://github.com/mapbox/mapbox-gl-native/pull/10951))
* Fixed an issue may potentially caused `NSInvalidArgumentException`. ([#11366](https://github.com/mapbox/mapbox-gl-native/pull/11366))
* Fix an issue where a wrong annotation may selected if annotations were set close together. ([#11438](https://github.com/mapbox/mapbox-gl-native/pull/11438))
+* The `MGLMapView.selectedAnnotations` property (backed by `-[MGLMapView setSelectedAnnotations:]`) now selects annotations that are off-screen. ([#9790](https://github.com/mapbox/mapbox-gl-native/issues/9790))
+* The `animated` parameter to `-[MGLMapView selectAnnotation:animated:]` now controls whether the annotation and its callout are brought on-screen. If `animated` is `NO` then the annotation is selected if offscreen, but the map is not panned. Currently only point annotations are supported. Setting the `MGLMapView.selectedAnnotations` property now animates. ([#3249](https://github.com/mapbox/mapbox-gl-native/issues/3249))
### Map snapshots
diff --git a/platform/ios/app/MBXCustomCalloutView.m b/platform/ios/app/MBXCustomCalloutView.m
index 13564c5cbf..0626b0997a 100644
--- a/platform/ios/app/MBXCustomCalloutView.m
+++ b/platform/ios/app/MBXCustomCalloutView.m
@@ -37,11 +37,15 @@ static CGFloat const tipWidth = 10.0;
return self;
}
-
#pragma mark - API
- (void)presentCalloutFromRect:(CGRect)rect inView:(UIView *)view constrainedToView:(UIView *)constrainedView animated:(BOOL)animated
{
+ [self presentCalloutFromRect:rect inView:view constrainedToRect:CGRectNull animated:animated];
+}
+
+- (void)presentCalloutFromRect:(CGRect)rect inView:(nonnull UIView *)view constrainedToRect:(__unused CGRect)constrainedRect animated:(BOOL)animated
+{
if ([self.delegate respondsToSelector:@selector(calloutViewWillAppear:)])
{
[self.delegate performSelector:@selector(calloutViewWillAppear:) withObject:self];
@@ -108,5 +112,4 @@ static CGFloat const tipWidth = 10.0;
CGPathRelease(tipPath);
}
-
@end
diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m
index a472e3a221..5282469223 100644
--- a/platform/ios/app/MBXViewController.m
+++ b/platform/ios/app/MBXViewController.m
@@ -54,6 +54,9 @@ typedef NS_ENUM(NSInteger, MBXSettingsAnnotationsRows) {
MBXSettingsAnnotationsQueryAnnotations,
MBXSettingsAnnotationsCustomUserDot,
MBXSettingsAnnotationsRemoveAnnotations,
+ MBXSettingsAnnotationSelectRandomOffscreenPointAnnotation,
+ MBXSettingsAnnotationCenterSelectedAnnotation,
+ MBXSettingsAnnotationAddVisibleAreaPolyline
};
typedef NS_ENUM(NSInteger, MBXSettingsRuntimeStylingRows) {
@@ -340,6 +343,9 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
@"Query Annotations",
[NSString stringWithFormat:@"%@ Custom User Dot", (_customUserLocationAnnnotationEnabled ? @"Disable" : @"Enable")],
@"Remove Annotations",
+ @"Select an offscreen point annotation",
+ @"Center selected annotation",
+ @"Add visible area polyline"
]];
break;
case MBXSettingsRuntimeStyling:
@@ -468,6 +474,18 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
case MBXSettingsAnnotationsRemoveAnnotations:
[self.mapView removeAnnotations:self.mapView.annotations];
break;
+ case MBXSettingsAnnotationSelectRandomOffscreenPointAnnotation:
+ [self selectAnOffscreenPointAnnotation];
+ break;
+
+ case MBXSettingsAnnotationCenterSelectedAnnotation:
+ [self centerSelectedAnnotation];
+ break;
+
+ case MBXSettingsAnnotationAddVisibleAreaPolyline:
+ [self addVisibleAreaPolyline];
+ break;
+
default:
NSAssert(NO, @"All annotations setting rows should be implemented");
break;
@@ -1551,6 +1569,73 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
[self presentViewController:alertController animated:YES completion:nil];
}
+- (id<MGLAnnotation>)randomOffscreenPointAnnotation {
+
+ NSPredicate *pointAnnotationPredicate = [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
+ return [evaluatedObject isKindOfClass:[MGLPointAnnotation class]];
+ }];
+
+ NSArray *annotations = [self.mapView.annotations filteredArrayUsingPredicate:pointAnnotationPredicate];
+
+ if (annotations.count == 0) {
+ return nil;
+ }
+
+ NSArray *visibleAnnotations = [self.mapView.visibleAnnotations filteredArrayUsingPredicate:pointAnnotationPredicate];
+
+ if (visibleAnnotations.count == annotations.count) {
+ return nil;
+ }
+
+ NSMutableArray *invisibleAnnotations = [annotations mutableCopy];
+
+ if (visibleAnnotations.count > 0) {
+ [invisibleAnnotations removeObjectsInArray:visibleAnnotations];
+ }
+
+ // Now pick a random offscreen annotation.
+ uint32_t index = arc4random_uniform((uint32_t)invisibleAnnotations.count);
+ return invisibleAnnotations[index];
+}
+
+- (void)selectAnOffscreenPointAnnotation {
+ id<MGLAnnotation> annotation = [self randomOffscreenPointAnnotation];
+ if (annotation) {
+ [self.mapView selectAnnotation:annotation animated:YES];
+
+ NSAssert(self.mapView.selectedAnnotations.firstObject, @"The annotation was not selected");
+ }
+}
+
+- (void)centerSelectedAnnotation {
+ id<MGLAnnotation> annotation = self.mapView.selectedAnnotations.firstObject;
+
+ if (!annotation)
+ return;
+
+ CGPoint point = [self.mapView convertCoordinate:annotation.coordinate toPointToView:self.mapView];
+
+ // Animate, so that point becomes the the center
+ CLLocationCoordinate2D center = [self.mapView convertPoint:point toCoordinateFromView:self.mapView];
+ [self.mapView setCenterCoordinate:center animated:YES];
+}
+
+- (void)addVisibleAreaPolyline {
+ CGRect constrainedRect = UIEdgeInsetsInsetRect(self.mapView.bounds, self.mapView.contentInset);
+
+ CLLocationCoordinate2D lineCoords[5];
+
+ lineCoords[0] = [self.mapView convertPoint: CGPointMake(CGRectGetMinX(constrainedRect), CGRectGetMinY(constrainedRect)) toCoordinateFromView:self.mapView];
+ lineCoords[1] = [self.mapView convertPoint: CGPointMake(CGRectGetMaxX(constrainedRect), CGRectGetMinY(constrainedRect)) toCoordinateFromView:self.mapView];
+ lineCoords[2] = [self.mapView convertPoint: CGPointMake(CGRectGetMaxX(constrainedRect), CGRectGetMaxY(constrainedRect)) toCoordinateFromView:self.mapView];
+ lineCoords[3] = [self.mapView convertPoint: CGPointMake(CGRectGetMinX(constrainedRect), CGRectGetMaxY(constrainedRect)) toCoordinateFromView:self.mapView];
+ lineCoords[4] = lineCoords[0];
+
+ MGLPolyline *line = [MGLPolyline polylineWithCoordinates:lineCoords
+ count:sizeof(lineCoords)/sizeof(lineCoords[0])];
+ [self.mapView addAnnotation:line];
+}
+
- (void)printTelemetryLogFile
{
NSString *fileContents = [NSString stringWithContentsOfFile:[self telemetryDebugLogFilePath] encoding:NSUTF8StringEncoding error:nil];
@@ -1602,7 +1687,13 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
toCoordinateFromView:self.mapView];
pin.title = title ?: @"Dropped Pin";
pin.subtitle = [[[MGLCoordinateFormatter alloc] init] stringFromCoordinate:pin.coordinate];
- // Calling `addAnnotation:` on mapView is not required since `selectAnnotation:animated` has the side effect of adding the annotation if required
+
+
+ // Calling `addAnnotation:` on mapView is required here (since `selectAnnotation:animated` has
+ // the side effect of adding the annotation if required, but returning an incorrect callout
+ // positioning rect)
+
+ [self.mapView addAnnotation:pin];
[self.mapView selectAnnotation:pin animated:YES];
}
}
diff --git a/platform/ios/src/MGLCalloutView.h b/platform/ios/src/MGLCalloutView.h
index 0481a39680..7e7cf2d02e 100644
--- a/platform/ios/src/MGLCalloutView.h
+++ b/platform/ios/src/MGLCalloutView.h
@@ -41,6 +41,13 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (void)presentCalloutFromRect:(CGRect)rect inView:(UIView *)view constrainedToView:(UIView *)constrainedView animated:(BOOL)animated;
+
+/**
+ Presents a callout view by adding it to `view` and pointing at the given rect
+ of `view`’s bounds. Constrains the callout to the rect in the space of `view`.
+ */
+- (void)presentCalloutFromRect:(CGRect)rect inView:(UIView *)view constrainedToRect:(CGRect)constrainedRect animated:(BOOL)animated;
+
/**
Dismisses the callout view.
*/
@@ -49,6 +56,24 @@ NS_ASSUME_NONNULL_BEGIN
@optional
/**
+ If implemented, should provide margins to expand the rect the callout is presented from.
+
+ These are used to determine positioning. Currently only the top and bottom properties of the return
+ value are used. For example, `{ .top = -50.0, .left = -10.0, .bottom = 0.0, .right = -10.0 }` indicates
+ a 50 point margin above the presentation origin rect (and 10 point margins to the left and the right)
+ in which the callout is assumed to be displayed.
+
+ There are no assumed defaults for these margins, as they should be calculated from the callout that
+ is to be presented. For example, `SMCalloutView` generates the top margin from the callout height, but
+ the left and right margins from a minimum width that the callout should have.
+
+ @param rect Rect that the callout is presented from. This should be the same as the one passed in
+ `-[MGLCalloutView presentCalloutFromRect:inView:constrainedToRect:animated:]`
+ @return `UIEdgeInsets` representing the margins. Values should be negative.
+ */
+- (UIEdgeInsets)marginInsetsHintForPresentationFromRect:(CGRect)rect NS_SWIFT_NAME(marginInsetsHintForPresentation(from:));
+
+/**
A Boolean value indicating whether the callout view should be anchored to
the corresponding annotation. You can adjust the callout view’s precise location by
overriding -[UIView setCenter:]. The callout view will not be anchored to the
diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h
index 52d28d871c..2d566f26a0 100644
--- a/platform/ios/src/MGLMapView.h
+++ b/platform/ios/src/MGLMapView.h
@@ -1200,17 +1200,32 @@ MGL_EXPORT IB_DESIGNABLE
Assigning a new array to this property selects only the first annotation in
the array.
+
+ If the annotation is of type `MGLPointAnnotation` and is offscreen, the camera
+ will animate to bring the annotation and its callout just on screen. If you
+ need finer control, consider using `-selectAnnotation:animated:`.
+
+ @note In versions prior to `4.0.0` if the annotation was offscreen it was not
+ selected.
*/
@property (nonatomic, copy) NS_ARRAY_OF(id <MGLAnnotation>) *selectedAnnotations;
/**
- Selects an annotation and displays a callout view for it.
+ Selects an annotation and displays its callout view.
- If the given annotation is not visible within the current viewport, this
- method has no effect.
+ The `animated` parameter determines whether the map is panned to bring the
+ annotation on-screen, specifically:
+
+ | `animated` parameter | Effect |
+ |------------------|--------|
+ | `NO` | The annotation is selected, and the callout is presented. However the map is not panned to bring the annotation or callout onscreen. The presentation of the callout is animated. |
+ | `YES` | The annotation is selected, and the callout is presented. If the annotation is offscreen *and* is of type `MGLPointAnnotation`, the map is panned so that the annotation and its callout are brought just onscreen. The annotation is *not* centered within the viewport. |
@param annotation The annotation object to select.
- @param animated If `YES`, the callout view is animated into position.
+ @param animated If `YES`, the annotation and callout view are animated on-screen.
+
+ @note In versions prior to `4.0.0` selecting an offscreen annotation did not
+ change the camera.
*/
- (void)selectAnnotation:(id <MGLAnnotation>)annotation animated:(BOOL)animated;
diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm
index 72198ba637..482fd07c00 100644
--- a/platform/ios/src/MGLMapView.mm
+++ b/platform/ios/src/MGLMapView.mm
@@ -144,6 +144,9 @@ const CGFloat MGLAnnotationImagePaddingForCallout = 1;
const CGSize MGLAnnotationAccessibilityElementMinimumSize = CGSizeMake(10, 10);
+/// Padding to edge of view that an offscreen annotation must have when being brought onscreen (by being selected)
+const UIEdgeInsets MGLMapViewOffscreenAnnotationPadding = UIEdgeInsetsMake(-20.0f, -20.0f, -20.0f, -20.0f);
+
/// An indication that the requested annotation was not found or is nonexistent.
enum { MGLAnnotationTagNotFound = UINT32_MAX };
@@ -272,7 +275,7 @@ public:
CADisplayLink *_displayLink;
BOOL _needsDisplayRefresh;
- NSUInteger _changeDelimiterSuppressionDepth;
+ NSInteger _changeDelimiterSuppressionDepth;
/// Center coordinate of the pinch gesture on the previous iteration of the gesture.
CLLocationCoordinate2D _previousPinchCenterCoordinate;
@@ -1625,7 +1628,7 @@ public:
{
CGPoint calloutPoint = [singleTap locationInView:self];
CGRect positionRect = [self positioningRectForAnnotation:annotation defaultCalloutPoint:calloutPoint];
- [self selectAnnotation:annotation animated:YES calloutPositioningRect:positionRect];
+ [self selectAnnotation:annotation moveOnscreen:YES animateSelection:YES calloutPositioningRect:positionRect];
}
else if (self.selectedAnnotation)
{
@@ -4234,6 +4237,12 @@ public:
});
}
+
+- (BOOL)isBringingAnnotationOnscreenSupportedForAnnotation:(id<MGLAnnotation>)annotation animated:(BOOL)animated {
+ // Consider delegating
+ return animated && [annotation isKindOfClass:[MGLPointAnnotation class]];
+}
+
- (id <MGLAnnotation>)selectedAnnotation
{
if (_userLocationAnnotationIsSelected)
@@ -4274,20 +4283,16 @@ public:
if ([firstAnnotation isKindOfClass:[MGLMultiPoint class]]) return;
- // Select the annotation if it’s visible.
- if (MGLCoordinateInCoordinateBounds(firstAnnotation.coordinate, self.visibleCoordinateBounds))
- {
- [self selectAnnotation:firstAnnotation animated:NO];
- }
+ [self selectAnnotation:firstAnnotation animated:YES];
}
- (void)selectAnnotation:(id <MGLAnnotation>)annotation animated:(BOOL)animated
{
CGRect positioningRect = [self positioningRectForAnnotation:annotation defaultCalloutPoint:CGPointZero];
- [self selectAnnotation:annotation animated:animated calloutPositioningRect:positioningRect];
+ [self selectAnnotation:annotation moveOnscreen:animated animateSelection:YES calloutPositioningRect:positioningRect];
}
-- (void)selectAnnotation:(id <MGLAnnotation>)annotation animated:(BOOL)animated calloutPositioningRect:(CGRect)calloutPositioningRect
+- (void)selectAnnotation:(id <MGLAnnotation>)annotation moveOnscreen:(BOOL)moveOnscreen animateSelection:(BOOL)animateSelection calloutPositioningRect:(CGRect)calloutPositioningRect
{
if ( ! annotation) return;
@@ -4311,22 +4316,34 @@ public:
MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(annotationTag);
annotationView = annotationContext.annotationView;
if (annotationView && annotationView.enabled) {
- // Annotations represented by views use the view frame as the positioning rect.
- calloutPositioningRect = annotationView.frame;
- [annotationView.superview bringSubviewToFront:annotationView];
- [annotationView setSelected:YES animated:animated];
+ // Annotations represented by views use the view frame as the positioning rect.
+ calloutPositioningRect = annotationView.frame;
+ [annotationView.superview bringSubviewToFront:annotationView];
+
+ [annotationView setSelected:YES animated:animateSelection];
}
}
self.selectedAnnotation = annotation;
+ // Determine if we're allowed to move this offscreen annotation on screen, even though we've asked it to
+ if (moveOnscreen) {
+ moveOnscreen = [self isBringingAnnotationOnscreenSupportedForAnnotation:annotation animated:animateSelection];
+ }
+
+ CGRect expandedPositioningRect = UIEdgeInsetsInsetRect(calloutPositioningRect, MGLMapViewOffscreenAnnotationPadding);
+
+ // Used for callout positioning, and moving offscreen annotations onscreen.
+ CGRect constrainedRect = UIEdgeInsetsInsetRect(self.bounds, self.contentInset);
+
+ UIView <MGLCalloutView> *calloutView = nil;
+
if ([annotation respondsToSelector:@selector(title)] &&
annotation.title &&
[self.delegate respondsToSelector:@selector(mapView:annotationCanShowCallout:)] &&
[self.delegate mapView:self annotationCanShowCallout:annotation])
{
// build the callout
- UIView <MGLCalloutView> *calloutView;
if ([self.delegate respondsToSelector:@selector(mapView:calloutViewForAnnotation:)])
{
id providedCalloutView = [self.delegate mapView:self calloutViewForAnnotation:annotation];
@@ -4384,13 +4401,51 @@ public:
// set annotation delegate to handle taps on the callout view
calloutView.delegate = self;
- // present popup
- [calloutView presentCalloutFromRect:calloutPositioningRect
- inView:self.glView
- constrainedToView:self.glView
- animated:animated];
+ // If the callout view provides inset (outset) information, we can use it to expand our positioning
+ // rect, which we then use to help move the annotation on-screen if want need to.
+ if (moveOnscreen && [calloutView respondsToSelector:@selector(marginInsetsHintForPresentationFromRect:)]) {
+ UIEdgeInsets margins = [calloutView marginInsetsHintForPresentationFromRect:calloutPositioningRect];
+ expandedPositioningRect = UIEdgeInsetsInsetRect(expandedPositioningRect, margins);
+ }
+ }
+
+ if (moveOnscreen)
+ {
+ moveOnscreen = NO;
+
+ // Need to consider the content insets.
+ CGRect bounds = UIEdgeInsetsInsetRect(self.bounds, self.contentInset);
+
+ // Any one of these cases should trigger a move onscreen
+ if (CGRectGetMinX(calloutPositioningRect) < CGRectGetMinX(bounds))
+ {
+ constrainedRect.origin.x = expandedPositioningRect.origin.x;
+ moveOnscreen = YES;
+ }
+ else if (CGRectGetMaxX(calloutPositioningRect) > CGRectGetMaxX(bounds))
+ {
+ constrainedRect.origin.x = CGRectGetMaxX(expandedPositioningRect) - constrainedRect.size.width;
+ moveOnscreen = YES;
+ }
+
+ if (CGRectGetMinY(calloutPositioningRect) < CGRectGetMinY(bounds))
+ {
+ constrainedRect.origin.y = expandedPositioningRect.origin.y;
+ moveOnscreen = YES;
+ }
+ else if (CGRectGetMaxY(calloutPositioningRect) > CGRectGetMaxY(bounds))
+ {
+ constrainedRect.origin.y = CGRectGetMaxY(expandedPositioningRect) - constrainedRect.size.height;
+ moveOnscreen = YES;
+ }
}
+ // Remember, calloutView can be nil here.
+ [calloutView presentCalloutFromRect:calloutPositioningRect
+ inView:self.glView
+ constrainedToRect:constrainedRect
+ animated:animateSelection];
+
// notify delegate
if ([self.delegate respondsToSelector:@selector(mapView:didSelectAnnotation:)])
{
@@ -4401,6 +4456,13 @@ public:
{
[self.delegate mapView:self didSelectAnnotationView:annotationView];
}
+
+ if (moveOnscreen)
+ {
+ CGPoint center = CGPointMake(CGRectGetMidX(constrainedRect), CGRectGetMidY(constrainedRect));
+ CLLocationCoordinate2D centerCoord = [self convertPoint:center toCoordinateFromView:self];
+ [self setCenterCoordinate:centerCoord animated:animateSelection];
+ }
}
- (MGLCompactCalloutView *)calloutViewForAnnotation:(id <MGLAnnotation>)annotation
@@ -4591,6 +4653,7 @@ public:
animated:animated];
}
+
#pragma mark Annotation Image Delegate
- (void)annotationImageNeedsRedisplay:(MGLAnnotationImage *)annotationImage
diff --git a/platform/ios/test/MGLAnnotationViewTests.m b/platform/ios/test/MGLAnnotationViewTests.m
index fc4f35a9e1..c8b140b274 100644
--- a/platform/ios/test/MGLAnnotationViewTests.m
+++ b/platform/ios/test/MGLAnnotationViewTests.m
@@ -51,12 +51,15 @@ static NSString * const MGLTestAnnotationReuseIdentifer = @"MGLTestAnnotationReu
- (void)presentCalloutFromRect:(CGRect)rect inView:(UIView *)view constrainedToView:(UIView *)constrainedView animated:(BOOL)animated { }
+- (void)presentCalloutFromRect:(CGRect)rect inView:(nonnull UIView *)view constrainedToRect:(CGRect)constrainedRect animated:(BOOL)animated {}
+
@end
@interface MGLAnnotationViewTests : XCTestCase <MGLMapViewDelegate>
@property (nonatomic) XCTestExpectation *expectation;
@property (nonatomic) MGLMapView *mapView;
@property (nonatomic, weak) MGLAnnotationView *annotationView;
+@property (nonatomic) NSInteger annotationSelectedCount;
@end
@implementation MGLAnnotationViewTests
@@ -98,6 +101,61 @@ static NSString * const MGLTestAnnotationReuseIdentifer = @"MGLTestAnnotationReu
XCTAssertNotNil(customAnnotationView);
}
+- (void)testSelectingOffscreenAnnotation
+{
+ // Partial test for https://github.com/mapbox/mapbox-gl-native/issues/9790
+
+ // This isn't quite the same as in updateAnnotationViews, but should be sufficient for this test.
+ MGLCoordinateBounds coordinateBounds = [_mapView convertRect:_mapView.bounds toCoordinateBoundsFromView:_mapView];
+
+ // -90 latitude is invalid. TBD.
+ BOOL anyOffscreen = NO;
+ NSInteger selectionCount = 0;
+
+ for (NSInteger latitude = -89; latitude <= 90; latitude += 10)
+ {
+ for (NSInteger longitude = -180; longitude <= 180; longitude += 10)
+ {
+ MGLTestAnnotation *annotation = [[MGLTestAnnotation alloc] init];
+
+ annotation.coordinate = CLLocationCoordinate2DMake(latitude, longitude);
+ [_mapView addAnnotation:annotation];
+
+ if (!(MGLCoordinateInCoordinateBounds(annotation.coordinate, coordinateBounds)))
+ anyOffscreen = YES;
+
+ XCTAssertNil(_mapView.selectedAnnotations.firstObject, @"There should be no selected annotation");
+
+ // First selection
+ [_mapView selectAnnotation:annotation animated:NO];
+ selectionCount++;
+
+ XCTAssert(_mapView.selectedAnnotations.count == 1, @"There should only be 1 selected annotation");
+ XCTAssertEqualObjects(_mapView.selectedAnnotations.firstObject, annotation, @"The annotation should be selected");
+
+ // Deselect
+ [_mapView deselectAnnotation:annotation animated:NO];
+ XCTAssert(_mapView.selectedAnnotations.count == 0, @"There should be no selected annotations");
+
+ // Second selection
+ _mapView.selectedAnnotations = @[annotation];
+ selectionCount++;
+
+ XCTAssert(_mapView.selectedAnnotations.count == 1, @"There should be 1 selected annotation");
+ XCTAssertEqualObjects(_mapView.selectedAnnotations.firstObject, annotation, @"The annotation should be selected");
+
+ // Deselect
+ [_mapView deselectAnnotation:annotation animated:NO];
+ XCTAssert(_mapView.selectedAnnotations.count == 0, @"There should be no selected annotations");
+ }
+ }
+
+ XCTAssert(anyOffscreen, @"At least one of these annotations should be offscreen");
+ XCTAssertEqual(selectionCount, self.annotationSelectedCount, @"-mapView:didSelectAnnotation: should be called for each selection");
+}
+
+#pragma mark - MGLMapViewDelegate -
+
- (MGLAnnotationView *)mapView:(MGLMapView *)mapView viewForAnnotation:(id<MGLAnnotation>)annotation
{
MGLAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:MGLTestAnnotationReuseIdentifer];
@@ -117,4 +175,9 @@ static NSString * const MGLTestAnnotationReuseIdentifer = @"MGLTestAnnotationReu
[_expectation fulfill];
}
+- (void)mapView:(MGLMapView *)mapView didSelectAnnotation:(id<MGLAnnotation>)annotation
+{
+ self.annotationSelectedCount++;
+}
+
@end
diff --git a/platform/ios/vendor/SMCalloutView/SMCalloutView.h b/platform/ios/vendor/SMCalloutView/SMCalloutView.h
index 0b14913626..5bb73d4c84 100755
--- a/platform/ios/vendor/SMCalloutView/SMCalloutView.h
+++ b/platform/ios/vendor/SMCalloutView/SMCalloutView.h
@@ -114,6 +114,18 @@ extern NSTimeInterval const kMGLSMCalloutViewRepositionDelayForUIScrollView;
- (void)presentCalloutFromRect:(CGRect)rect inView:(UIView *)view constrainedToView:(UIView *)constrainedView animated:(BOOL)animated;
/**
+ @brief Presents a callout view by adding it to "inView" and pointing at the given rect of inView's bounds.
+
+ @discussion Constrains the callout to the rect (in the space of the given view).
+
+ @param rect @c CGRect to present the view from
+ @param view view to 'constrain' the @c constrainedView to
+ @param constrainedRect Rect to constrain the callout to
+ @param animated @c BOOL if presentation should be animated
+ */
+- (void)presentCalloutFromRect:(CGRect)rect inView:(UIView *)view constrainedToRect:(CGRect)constrainedRect animated:(BOOL)animated;
+
+/**
@brief Present a callout layer in the `layer` and pointing at the given rect of the `layer` bounds
@discussion Same as the view-based presentation, but inserts the callout into a CALayer hierarchy instead.
diff --git a/platform/ios/vendor/SMCalloutView/SMCalloutView.m b/platform/ios/vendor/SMCalloutView/SMCalloutView.m
index 9631ca0367..a0049a3e2d 100755
--- a/platform/ios/vendor/SMCalloutView/SMCalloutView.m
+++ b/platform/ios/vendor/SMCalloutView/SMCalloutView.m
@@ -247,6 +247,35 @@ NSTimeInterval const kMGLSMCalloutViewRepositionDelayForUIScrollView = 1.0/3.0;
return CGSizeMake(nudgeLeft ? nudgeLeft : nudgeRight, nudgeTop ? nudgeTop : nudgeBottom);
}
+- (UIEdgeInsets)marginInsetsHintForPresentationFromRect:(CGRect)rect {
+
+ // form our subviews based on our content set so far
+ [self rebuildSubviews];
+
+ // size the callout to fit the width constraint as best as possible
+ CGFloat height = self.calloutHeight;
+ CGSize size = [self sizeThatFits:CGSizeMake(0.0f, height)];
+
+ // Without re-jigging presentCalloutFromRect, let's just make a best-guess with what we have
+ // right now.
+ CGFloat horizontalMargin = fmaxf(0, ceilf((CALLOUT_MIN_WIDTH-rect.size.width)/2));
+
+ UIEdgeInsets insets = {
+ .top = 0.0f,
+ .right = -horizontalMargin,
+ .bottom = 0.0f,
+ .left = -horizontalMargin
+ };
+
+ if (self.permittedArrowDirection == MGLSMCalloutArrowDirectionUp)
+ insets.bottom -= size.height;
+ else
+ insets.top -= size.height;
+
+ return insets;
+}
+
+
- (void)presentCalloutFromRect:(CGRect)rect inView:(UIView *)view constrainedToView:(UIView *)constrainedView animated:(BOOL)animated {
[self presentCalloutFromRect:rect inLayer:view.layer ofView:view constrainedToLayer:constrainedView.layer animated:animated];
}
@@ -255,8 +284,18 @@ NSTimeInterval const kMGLSMCalloutViewRepositionDelayForUIScrollView = 1.0/3.0;
[self presentCalloutFromRect:rect inLayer:layer ofView:nil constrainedToLayer:constrainedLayer animated:animated];
}
-// this private method handles both CALayer and UIView parents depending on what's passed.
- (void)presentCalloutFromRect:(CGRect)rect inLayer:(CALayer *)layer ofView:(UIView *)view constrainedToLayer:(CALayer *)constrainedLayer animated:(BOOL)animated {
+ // figure out the constrained view's rect in our popup view's coordinate system
+ CGRect constrainedRect = [constrainedLayer convertRect:constrainedLayer.bounds toLayer:layer];
+ [self presentCalloutFromRect:rect inLayer:layer ofView:view constrainedToRect:constrainedRect animated:animated];
+}
+
+- (void)presentCalloutFromRect:(CGRect)rect inView:(UIView *)view constrainedToRect:(CGRect)constrainedRect animated:(BOOL)animated {
+ [self presentCalloutFromRect:rect inLayer:view.layer ofView:view constrainedToRect:constrainedRect animated:animated];
+}
+
+// this private method handles both CALayer and UIView parents depending on what's passed.
+- (void)presentCalloutFromRect:(CGRect)rect inLayer:(CALayer *)layer ofView:(UIView *)view constrainedToRect:(CGRect)constrainedRect animated:(BOOL)animated {
// Sanity check: dismiss this callout immediately if it's displayed somewhere
if (self.layer.superlayer) [self dismissCalloutAnimated:NO];
@@ -265,8 +304,6 @@ NSTimeInterval const kMGLSMCalloutViewRepositionDelayForUIScrollView = 1.0/3.0;
[self.layer removeAnimationForKey:@"present"];
[self.layer removeAnimationForKey:@"dismiss"];
- // figure out the constrained view's rect in our popup view's coordinate system
- CGRect constrainedRect = [constrainedLayer convertRect:constrainedLayer.bounds toLayer:layer];
// apply our edge constraints
constrainedRect = UIEdgeInsetsInsetRect(constrainedRect, self.constrainedInsets);
diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md
index 6de2a4e87e..fd22589473 100644
--- a/platform/macos/CHANGELOG.md
+++ b/platform/macos/CHANGELOG.md
@@ -25,6 +25,8 @@
### Annotations
* Fix an issue where a wrong annotation may selected if annotations were set close together. ([#11438](https://github.com/mapbox/mapbox-gl-native/pull/11438))
+* The `MGLMapView.selectedAnnotations` property (backed by `-[MGLMapView setSelectedAnnotations:]`) now selects annotations that are off-screen. ([#9790](https://github.com/mapbox/mapbox-gl-native/issues/9790))
+* The `animated` parameter to `-[MGLMapView selectAnnotation:animated:]` now controls whether the annotation and its callout are brought on-screen. If `animated` is `NO` then the annotation is selected if offscreen, but the map is not panned. Currently only point annotations are supported.([#3249](https://github.com/mapbox/mapbox-gl-native/issues/3249))
### Map snapshots
diff --git a/platform/macos/app/Base.lproj/MainMenu.xib b/platform/macos/app/Base.lproj/MainMenu.xib
index 4cf8d87653..8f0aeaf69c 100644
--- a/platform/macos/app/Base.lproj/MainMenu.xib
+++ b/platform/macos/app/Base.lproj/MainMenu.xib
@@ -545,7 +545,13 @@
<action selector="drawAnimatedAnnotation:" target="-1" id="CYM-WB-s97"/>
</connections>
</menuItem>
- <menuItem title="Show All Annnotations" keyEquivalent="A" id="yMj-uM-8SN">
+ <menuItem title="Select an Offscreen Point Annotation" id="Xy2-Cc-RUB">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="selectOffscreenPointAnnotation:" target="-1" id="Fhm-l3-G6h"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Show All Annotations" keyEquivalent="A" id="yMj-uM-8SN">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="showAllAnnotations:" target="-1" id="ahr-OR-Em2"/>
@@ -664,7 +670,7 @@ CA
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="109" y="131" width="350" height="84"/>
- <rect key="screenRect" x="0.0" y="0.0" width="1280" height="777"/>
+ <rect key="screenRect" x="0.0" y="0.0" width="1440" height="877"/>
<view key="contentView" id="eA4-n3-qPe">
<rect key="frame" x="0.0" y="0.0" width="350" height="84"/>
<autoresizingMask key="autoresizingMask"/>
@@ -740,7 +746,7 @@ CA
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES" utility="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="830" y="430" width="400" height="300"/>
- <rect key="screenRect" x="0.0" y="0.0" width="1280" height="777"/>
+ <rect key="screenRect" x="0.0" y="0.0" width="1440" height="877"/>
<view key="contentView" id="8ha-hw-zOD">
<rect key="frame" x="0.0" y="0.0" width="400" height="300"/>
<autoresizingMask key="autoresizingMask"/>
@@ -748,11 +754,11 @@ CA
<scrollView autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Q8b-0e-dLv">
<rect key="frame" x="-1" y="20" width="402" height="281"/>
<clipView key="contentView" id="J9U-Yx-o2S">
- <rect key="frame" x="1" y="0.0" width="400" height="280"/>
+ <rect key="frame" x="1" y="0.0" width="400" height="265"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" autosaveColumns="NO" headerView="MAZ-Iq-hBi" id="Ato-Vu-HYT">
- <rect key="frame" x="0.0" y="0.0" width="423" height="257"/>
+ <rect key="frame" x="0.0" y="0.0" width="423" height="242"/>
<autoresizingMask key="autoresizingMask"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
@@ -891,7 +897,7 @@ CA
</subviews>
</clipView>
<scroller key="horizontalScroller" verticalHuggingPriority="750" horizontal="YES" id="QLr-6P-Ogs">
- <rect key="frame" x="1" y="264" width="400" height="16"/>
+ <rect key="frame" x="1" y="265" width="400" height="15"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="q0K-eE-mzL">
diff --git a/platform/macos/app/MapDocument.m b/platform/macos/app/MapDocument.m
index 03557caca2..23bc652229 100644
--- a/platform/macos/app/MapDocument.m
+++ b/platform/macos/app/MapDocument.m
@@ -641,6 +641,51 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
repeats:YES];
}
+
+- (id<MGLAnnotation>)randomOffscreenPointAnnotation {
+
+ NSPredicate *pointAnnotationPredicate = [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
+ return [evaluatedObject isKindOfClass:[MGLPointAnnotation class]];
+ }];
+
+ NSArray *annotations = [self.mapView.annotations filteredArrayUsingPredicate:pointAnnotationPredicate];
+
+ if (annotations.count == 0) {
+ return nil;
+ }
+
+ // NOTE: self.mapView.visibleAnnotations occasionally returns nil - see
+ // https://github.com/mapbox/mapbox-gl-native/issues/11296
+ NSArray *visibleAnnotations = [self.mapView.visibleAnnotations filteredArrayUsingPredicate:pointAnnotationPredicate];
+
+ NSLog(@"Number of visible point annotations = %ld", visibleAnnotations.count);
+
+ if (visibleAnnotations.count == annotations.count) {
+ return nil;
+ }
+
+ NSMutableArray *invisibleAnnotations = [annotations mutableCopy];
+
+ if (visibleAnnotations.count > 0) {
+ [invisibleAnnotations removeObjectsInArray:visibleAnnotations];
+ }
+
+ // Now pick a random offscreen annotation.
+ uint32_t index = arc4random_uniform((uint32_t)invisibleAnnotations.count);
+ return invisibleAnnotations[index];
+}
+
+- (IBAction)selectOffscreenPointAnnotation:(id)sender {
+ id<MGLAnnotation> annotation = [self randomOffscreenPointAnnotation];
+ if (annotation) {
+ [self.mapView selectAnnotation:annotation];
+
+ // Alternative method to select the annotation. These two should do the same thing.
+ // self.mapView.selectedAnnotations = @[annotation];
+ NSAssert(self.mapView.selectedAnnotations.firstObject, @"The annotation was not selected");
+ }
+}
+
- (void)updateAnimatedAnnotation:(NSTimer *)timer {
DroppedPinAnnotation *annotation = timer.userInfo;
double angle = timer.fireDate.timeIntervalSinceReferenceDate;
@@ -1111,6 +1156,9 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
if (menuItem.action == @selector(insertGraticuleLayer:)) {
return ![self.mapView.style sourceWithIdentifier:@"graticule"];
}
+ if (menuItem.action == @selector(selectOffscreenPointAnnotation:)) {
+ return YES;
+ }
if (menuItem.action == @selector(showAllAnnotations:) || menuItem.action == @selector(removeAllAnnotations:)) {
return self.mapView.annotations.count > 0;
}
diff --git a/platform/macos/src/MGLMapView.h b/platform/macos/src/MGLMapView.h
index 050145b80b..74224622d4 100644
--- a/platform/macos/src/MGLMapView.h
+++ b/platform/macos/src/MGLMapView.h
@@ -721,16 +721,27 @@ MGL_EXPORT IB_DESIGNABLE
Assigning a new array to this property selects only the first annotation in the
array.
+
+ If the annotation is of type `MGLPointAnnotation` and is offscreen, the map is
+ panned so that the annotation and its callout are brought just onscreen. The
+ annotation is *not* centered within the viewport.
+
+ @note In versions prior to `4.0.0` if the annotation was offscreen it was not
+ selected.
*/
@property (nonatomic, copy) NS_ARRAY_OF(id <MGLAnnotation>) *selectedAnnotations;
/**
Selects an annotation and displays a callout popover for it.
- If the given annotation is not visible within the current viewport, this method
- has no effect.
+ If the annotation is of type `MGLPointAnnotation` and is offscreen, the map is
+ panned so that the annotation and its callout are brought just onscreen. The
+ annotation is *not* centered within the viewport.
@param annotation The annotation object to select.
+
+ @note In versions prior to `4.0.0` selecting an offscreen annotation did not
+ change the camera.
*/
- (void)selectAnnotation:(id <MGLAnnotation>)annotation;
diff --git a/platform/macos/src/MGLMapView.mm b/platform/macos/src/MGLMapView.mm
index 353b2bf2f1..9cab9a76da 100644
--- a/platform/macos/src/MGLMapView.mm
+++ b/platform/macos/src/MGLMapView.mm
@@ -97,6 +97,9 @@ const CGFloat MGLAnnotationImagePaddingForHitTest = 4;
/// Distance from the callout’s anchor point to the annotation it points to.
const CGFloat MGLAnnotationImagePaddingForCallout = 4;
+/// Padding to edge of view that an offscreen annotation must have when being brought onscreen (by being selected)
+const NSEdgeInsets MGLMapViewOffscreenAnnotationPadding = NSEdgeInsetsMake(-30.0f, -30.0f, -30.0f, -30.0f);
+
/// Unique identifier representing a single annotation in mbgl.
typedef uint32_t MGLAnnotationTag;
@@ -2205,10 +2208,12 @@ public:
return;
}
- // Select the annotation if it’s visible.
- if (MGLCoordinateInCoordinateBounds(firstAnnotation.coordinate, self.visibleCoordinateBounds)) {
- [self selectAnnotation:firstAnnotation];
- }
+ [self selectAnnotation:firstAnnotation];
+}
+
+- (BOOL)isBringingAnnotationOnscreenSupportedForAnnotation:(id<MGLAnnotation>)annotation animated:(BOOL)animated {
+ // Consider delegating
+ return animated && [annotation isKindOfClass:[MGLPointAnnotation class]];
}
- (void)selectAnnotation:(id <MGLAnnotation>)annotation
@@ -2218,6 +2223,11 @@ public:
- (void)selectAnnotation:(id <MGLAnnotation>)annotation atPoint:(NSPoint)gesturePoint
{
+ [self selectAnnotation:annotation atPoint:gesturePoint moveOnscreen:YES animateSelection:YES];
+}
+
+- (void)selectAnnotation:(id <MGLAnnotation>)annotation atPoint:(NSPoint)gesturePoint moveOnscreen:(BOOL)moveOnscreen animateSelection:(BOOL)animateSelection
+{
id <MGLAnnotation> selectedAnnotation = self.selectedAnnotation;
if (annotation == selectedAnnotation) {
return;
@@ -2232,9 +2242,14 @@ public:
[self addAnnotation:annotation];
}
+ if (moveOnscreen) {
+ moveOnscreen = [self isBringingAnnotationOnscreenSupportedForAnnotation:annotation animated:animateSelection];
+ }
+
// The annotation's anchor will bounce to the current click.
NSRect positioningRect = [self positioningRectForCalloutForAnnotationWithTag:annotationTag];
- if (NSIsEmptyRect(NSIntersectionRect(positioningRect, self.bounds))) {
+
+ if (!moveOnscreen && NSIsEmptyRect(NSIntersectionRect(positioningRect, self.bounds))) {
positioningRect = CGRectMake(gesturePoint.x, gesturePoint.y, positioningRect.size.width, positioningRect.size.height);
}
@@ -2254,11 +2269,65 @@ public:
// alignment rect, or off the left edge in a right-to-left UI.
callout.delegate = self;
self.calloutForSelectedAnnotation = callout;
+
NSRectEdge edge = (self.userInterfaceLayoutDirection == NSUserInterfaceLayoutDirectionRightToLeft
? NSMinXEdge
: NSMaxXEdge);
+
+ // The following will do nothing if the positioning rect is not on-screen. See
+ // `-[MGLMapView updateAnnotationCallouts]` for presenting the callout when the selected
+ // annotation comes back on-screen.
[callout showRelativeToRect:positioningRect ofView:self preferredEdge:edge];
}
+
+ if (moveOnscreen)
+ {
+ moveOnscreen = NO;
+
+ NSRect (^edgeInsetsInsetRect)(NSRect, NSEdgeInsets) = ^(NSRect rect, NSEdgeInsets insets) {
+ return NSMakeRect(rect.origin.x + insets.left,
+ rect.origin.y + insets.top,
+ rect.size.width - insets.left - insets.right,
+ rect.size.height - insets.top - insets.bottom);
+ };
+
+ // Add padding around the positioning rect (in essence an inset from the edge of the viewport
+ NSRect expandedPositioningRect = edgeInsetsInsetRect(positioningRect, MGLMapViewOffscreenAnnotationPadding);
+
+ // Used for callout positioning, and moving offscreen annotations onscreen.
+ CGRect constrainedRect = edgeInsetsInsetRect(self.bounds, self.contentInsets);
+ CGRect bounds = constrainedRect;
+
+ // Any one of these cases should trigger a move onscreen
+ if (CGRectGetMinX(positioningRect) < CGRectGetMinX(bounds))
+ {
+ constrainedRect.origin.x = expandedPositioningRect.origin.x;
+ moveOnscreen = YES;
+ }
+ else if (CGRectGetMaxX(positioningRect) > CGRectGetMaxX(bounds))
+ {
+ constrainedRect.origin.x = CGRectGetMaxX(expandedPositioningRect) - constrainedRect.size.width;
+ moveOnscreen = YES;
+ }
+
+ if (CGRectGetMinY(positioningRect) < CGRectGetMinY(bounds))
+ {
+ constrainedRect.origin.y = expandedPositioningRect.origin.y;
+ moveOnscreen = YES;
+ }
+ else if (CGRectGetMaxY(positioningRect) > CGRectGetMaxY(bounds))
+ {
+ constrainedRect.origin.y = CGRectGetMaxY(expandedPositioningRect) - constrainedRect.size.height;
+ moveOnscreen = YES;
+ }
+
+ if (moveOnscreen)
+ {
+ CGPoint center = CGPointMake(CGRectGetMidX(constrainedRect), CGRectGetMidY(constrainedRect));
+ CLLocationCoordinate2D centerCoord = [self convertPoint:center toCoordinateFromView:self];
+ [self setCenterCoordinate:centerCoord animated:animateSelection];
+ }
+ }
}
- (void)showAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations animated:(BOOL)animated {
@@ -2393,7 +2462,25 @@ public:
- (void)updateAnnotationCallouts {
NSPopover *callout = self.calloutForSelectedAnnotation;
if (callout) {
- callout.positioningRect = [self positioningRectForCalloutForAnnotationWithTag:_selectedAnnotationTag];
+ NSRect rect = [self positioningRectForCalloutForAnnotationWithTag:_selectedAnnotationTag];
+
+ if (!NSIsEmptyRect(NSIntersectionRect(rect, self.bounds))) {
+
+ // It's possible that the current callout hasn't been presented (since the original
+ // positioningRect was offscreen). We can check that the callout has a valid window
+ // This results in the callout being presented just as the annotation comes on screen
+ // which matches MapKit, but (currently) not iOS.
+ if (!callout.contentViewController.view.window) {
+ NSRectEdge edge = (self.userInterfaceLayoutDirection == NSUserInterfaceLayoutDirectionRightToLeft
+ ? NSMinXEdge
+ : NSMaxXEdge);
+ // Re-present the callout
+ [callout showRelativeToRect:rect ofView:self preferredEdge:edge];
+ }
+ else {
+ callout.positioningRect = rect;
+ }
+ }
}
}
diff --git a/platform/node/test/ignores.json b/platform/node/test/ignores.json
index 840daa2049..79debab587 100644
--- a/platform/node/test/ignores.json
+++ b/platform/node/test/ignores.json
@@ -36,10 +36,8 @@
"render-tests/regressions/mapbox-gl-js#2769": "https://github.com/mapbox/mapbox-gl-native/issues/10619",
"render-tests/regressions/mapbox-gl-js#3682": "https://github.com/mapbox/mapbox-gl-js/issues/3682",
"render-tests/regressions/mapbox-gl-js#5370": "skip - https://github.com/mapbox/mapbox-gl-native/pull/9439",
- "render-tests/regressions/mapbox-gl-js#5599": "https://github.com/mapbox/mapbox-gl-native/issues/10399",
"render-tests/regressions/mapbox-gl-js#5740": "https://github.com/mapbox/mapbox-gl-native/issues/10619",
"render-tests/regressions/mapbox-gl-js#5982": "https://github.com/mapbox/mapbox-gl-native/issues/10619",
- "render-tests/regressions/mapbox-gl-js#6160": "https://github.com/mapbox/mapbox-gl-native/pull/11206",
"render-tests/regressions/mapbox-gl-native#7357": "https://github.com/mapbox/mapbox-gl-native/issues/7357",
"render-tests/runtime-styling/image-add-sdf": "https://github.com/mapbox/mapbox-gl-native/issues/9847",
"render-tests/runtime-styling/paint-property-fill-flat-to-extrude": "https://github.com/mapbox/mapbox-gl-native/issues/6745",
diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp
index 3bf85407c6..82a9255824 100644
--- a/src/mbgl/layout/symbol_layout.cpp
+++ b/src/mbgl/layout/symbol_layout.cpp
@@ -126,7 +126,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters,
if (hasText) {
std::string u8string = layout.evaluate<TextField>(zoom, ft);
- if (layout.get<TextField>().isConstant()) {
+ if (layout.get<TextField>().isConstant() && !leader.layout.get<TextField>().isExpression()) {
u8string = util::replaceTokens(u8string, getValue);
}
@@ -159,7 +159,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters,
if (hasIcon) {
std::string icon = layout.evaluate<IconImage>(zoom, ft);
- if (layout.get<IconImage>().isConstant()) {
+ if (layout.get<IconImage>().isConstant() && !leader.layout.get<IconImage>().isExpression()) {
icon = util::replaceTokens(icon, getValue);
}
ft.icon = icon;
diff --git a/src/mbgl/programs/symbol_program.hpp b/src/mbgl/programs/symbol_program.hpp
index a14afac702..9b5037ed9f 100644
--- a/src/mbgl/programs/symbol_program.hpp
+++ b/src/mbgl/programs/symbol_program.hpp
@@ -61,8 +61,8 @@ struct SymbolLayoutAttributes : gl::Attributes<
{{
static_cast<int16_t>(labelAnchor.x),
static_cast<int16_t>(labelAnchor.y),
- static_cast<int16_t>(::round(o.x * 64)), // use 1/64 pixels for placement
- static_cast<int16_t>(::round((o.y + glyphOffsetY) * 64))
+ static_cast<int16_t>(::round(o.x * 32)), // use 1/32 pixels for placement
+ static_cast<int16_t>(::round((o.y + glyphOffsetY) * 32))
}},
{{
tx,
diff --git a/src/mbgl/shaders/collision_box.cpp b/src/mbgl/shaders/collision_box.cpp
index 9d11640bf4..bc5d9bc6f9 100644
--- a/src/mbgl/shaders/collision_box.cpp
+++ b/src/mbgl/shaders/collision_box.cpp
@@ -22,7 +22,10 @@ varying float v_notUsed;
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 boxes in pitched/overzoomed tiles
+ 4.0);
gl_Position = u_matrix * vec4(a_pos, 0.0, 1.0);
gl_Position.xy += a_extrude * u_extrude_scale * gl_Position.w * collision_perspective_ratio;
diff --git a/src/mbgl/shaders/symbol_icon.cpp b/src/mbgl/shaders/symbol_icon.cpp
index f5c2bbe22d..c037c81005 100644
--- a/src/mbgl/shaders/symbol_icon.cpp
+++ b/src/mbgl/shaders/symbol_icon.cpp
@@ -80,7 +80,10 @@ void main() {
highp float distance_ratio = u_pitch_with_map ?
camera_to_anchor_distance / u_camera_to_center_distance :
u_camera_to_center_distance / camera_to_anchor_distance;
- highp float perspective_ratio = 0.5 + 0.5 * distance_ratio;
+ highp float perspective_ratio = clamp(
+ 0.5 + 0.5 * distance_ratio,
+ 0.0, // Prevents oversized near-field symbols in pitched/overzoomed tiles
+ 4.0);
size *= perspective_ratio;
@@ -102,7 +105,7 @@ void main() {
mat2 rotation_matrix = mat2(angle_cos, -1.0 * angle_sin, angle_sin, angle_cos);
vec4 projected_pos = u_label_plane_matrix * vec4(a_projected_pos.xy, 0.0, 1.0);
- gl_Position = u_gl_coord_matrix * vec4(projected_pos.xy / projected_pos.w + rotation_matrix * (a_offset / 64.0 * fontScale), 0.0, 1.0);
+ gl_Position = u_gl_coord_matrix * vec4(projected_pos.xy / projected_pos.w + rotation_matrix * (a_offset / 32.0 * fontScale), 0.0, 1.0);
v_tex = a_tex / u_texsize;
vec2 fade_opacity = unpack_opacity(a_fade_opacity);
diff --git a/src/mbgl/shaders/symbol_sdf.cpp b/src/mbgl/shaders/symbol_sdf.cpp
index 441eaf7aac..b584c00315 100644
--- a/src/mbgl/shaders/symbol_sdf.cpp
+++ b/src/mbgl/shaders/symbol_sdf.cpp
@@ -156,7 +156,10 @@ void main() {
highp float distance_ratio = u_pitch_with_map ?
camera_to_anchor_distance / u_camera_to_center_distance :
u_camera_to_center_distance / camera_to_anchor_distance;
- highp float perspective_ratio = 0.5 + 0.5 * distance_ratio;
+ highp float perspective_ratio = clamp(
+ 0.5 + 0.5 * distance_ratio,
+ 0.0, // Prevents oversized near-field symbols in pitched/overzoomed tiles
+ 4.0);
size *= perspective_ratio;
@@ -180,7 +183,7 @@ void main() {
mat2 rotation_matrix = mat2(angle_cos, -1.0 * angle_sin, angle_sin, angle_cos);
vec4 projected_pos = u_label_plane_matrix * vec4(a_projected_pos.xy, 0.0, 1.0);
- gl_Position = u_gl_coord_matrix * vec4(projected_pos.xy / projected_pos.w + rotation_matrix * (a_offset / 64.0 * fontScale), 0.0, 1.0);
+ gl_Position = u_gl_coord_matrix * vec4(projected_pos.xy / projected_pos.w + rotation_matrix * (a_offset / 32.0 * fontScale), 0.0, 1.0);
float gamma_scale = gl_Position.w;
vec2 tex = a_tex / u_texsize;
diff --git a/src/mbgl/text/placement.cpp b/src/mbgl/text/placement.cpp
index fc24c78902..54b2b7539b 100644
--- a/src/mbgl/text/placement.cpp
+++ b/src/mbgl/text/placement.cpp
@@ -95,9 +95,6 @@ void Placement::placeLayerBucket(
auto partiallyEvaluatedTextSize = bucket.textSizeBinder->evaluateForZoom(state.getZoom());
auto partiallyEvaluatedIconSize = bucket.iconSizeBinder->evaluateForZoom(state.getZoom());
- const bool iconWithoutText = !bucket.hasTextData() || bucket.layout.get<style::TextOptional>();
- const bool textWithoutIcon = !bucket.hasIconData() || bucket.layout.get<style::IconOptional>();
-
for (auto& symbolInstance : bucket.symbolInstances) {
if (seenCrossTileIDs.count(symbolInstance.crossTileID) == 0) {
@@ -140,6 +137,9 @@ void Placement::placeLayerBucket(
offscreen &= placed.second;
}
+ const bool iconWithoutText = !symbolInstance.hasText || bucket.layout.get<style::TextOptional>();
+ const bool textWithoutIcon = !symbolInstance.hasIcon || bucket.layout.get<style::IconOptional>();
+
// combine placements for icon and text
if (!iconWithoutText && !textWithoutIcon) {
placeText = placeIcon = placeText && placeIcon;