summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore5
-rw-r--r--.ycm_extra_conf.py2
-rw-r--r--CMakeLists.txt29
-rw-r--r--INSTALL.md58
-rw-r--r--LICENSE.md2
-rw-r--r--Makefile61
-rw-r--r--README.md2
-rw-r--r--appveyor.yml56
-rw-r--r--benchmark/api/query.benchmark.cpp2
-rw-r--r--benchmark/api/render.benchmark.cpp6
-rw-r--r--benchmark/function/camera_function.benchmark.cpp18
-rw-r--r--benchmark/function/composite_function.benchmark.cpp17
-rw-r--r--benchmark/function/source_function.benchmark.cpp17
-rw-r--r--benchmark/parse/filter.benchmark.cpp8
-rw-r--r--bin/offline.cpp76
-rw-r--r--bin/render.cpp115
-rw-r--r--circle.yml163
-rw-r--r--cmake/benchmark-files.cmake19
-rw-r--r--cmake/benchmark.cmake5
-rw-r--r--cmake/core-files.cmake157
-rw-r--r--cmake/core.cmake6
-rw-r--r--cmake/files.cmake.ejs13
-rw-r--r--cmake/filesource.cmake8
-rw-r--r--cmake/glfw.cmake5
-rw-r--r--cmake/loop-darwin.cmake7
-rw-r--r--cmake/loop-uv.cmake5
-rw-r--r--cmake/mbgl.cmake127
-rw-r--r--cmake/node.cmake19
-rw-r--r--cmake/offline.cmake6
-rw-r--r--cmake/render.cmake7
-rw-r--r--cmake/test-files.cmake76
-rw-r--r--cmake/test.cmake5
-rwxr-xr-xdeps/ninja/ninja-linuxbin166960 -> 0 bytes
-rwxr-xr-xdeps/ninja/ninja-macosbin171356 -> 0 bytes
-rwxr-xr-xdeps/run_gyp8
-rw-r--r--docker/Dockerfile16
-rw-r--r--docker/bitrise/android/Dockerfile21
-rwxr-xr-xdocker/build.sh8
-rw-r--r--docker/clang-tidy/Dockerfile9
-rwxr-xr-xdocker/clang-tidy/run.sh14
-rwxr-xr-xdocker/clang-tidy/tidy.sh18
-rw-r--r--docker/george-edison55-precise-backports.gpg.key13
-rw-r--r--docker/george-edison55-precise-backports.list2
-rw-r--r--docker/linux/Dockerfile14
-rwxr-xr-xdocker/linux/run.sh14
-rwxr-xr-xdocker/linux/test.sh24
-rw-r--r--docker/ubuntu-toolchain-r.gpg.key13
-rw-r--r--docker/ubuntu-toolchain-r.list2
-rw-r--r--docs/TROUBLESHOOTING.md5
-rw-r--r--include/mbgl/map/map.hpp8
-rw-r--r--include/mbgl/map/mode.hpp12
-rw-r--r--include/mbgl/renderer/mode.hpp18
-rw-r--r--include/mbgl/renderer/renderer.hpp4
-rw-r--r--include/mbgl/renderer/renderer_backend.hpp2
-rw-r--r--include/mbgl/renderer/renderer_observer.hpp (renamed from src/mbgl/renderer/renderer_observer.hpp)0
-rw-r--r--include/mbgl/storage/offline.hpp6
-rw-r--r--include/mbgl/style/conversion.hpp259
-rw-r--r--include/mbgl/style/conversion/constant.hpp98
-rw-r--r--include/mbgl/style/conversion/coordinate.hpp21
-rw-r--r--include/mbgl/style/conversion/custom_geometry_source_options.hpp84
-rw-r--r--include/mbgl/style/conversion/data_driven_property_value.hpp28
-rw-r--r--include/mbgl/style/conversion/expression.hpp39
-rw-r--r--include/mbgl/style/conversion/filter.hpp243
-rw-r--r--include/mbgl/style/conversion/function.hpp47
-rw-r--r--include/mbgl/style/conversion/geojson.hpp10
-rw-r--r--include/mbgl/style/conversion/geojson_options.hpp79
-rw-r--r--include/mbgl/style/conversion/get_json_type.hpp14
-rw-r--r--include/mbgl/style/conversion/heatmap_color_property_value.hpp46
-rw-r--r--include/mbgl/style/conversion/layer.hpp213
-rw-r--r--include/mbgl/style/conversion/light.hpp106
-rw-r--r--include/mbgl/style/conversion/make_property_setters.hpp211
-rw-r--r--include/mbgl/style/conversion/make_property_setters.hpp.ejs48
-rw-r--r--include/mbgl/style/conversion/position.hpp14
-rw-r--r--include/mbgl/style/conversion/property_value.hpp19
-rw-r--r--include/mbgl/style/conversion/source.hpp176
-rw-r--r--include/mbgl/style/conversion/tileset.hpp96
-rw-r--r--include/mbgl/style/conversion/transition_options.hpp32
-rw-r--r--include/mbgl/style/expression/array_assertion.hpp46
-rw-r--r--include/mbgl/style/expression/assertion.hpp38
-rw-r--r--include/mbgl/style/expression/at.hpp44
-rw-r--r--include/mbgl/style/expression/boolean_operator.hpp52
-rw-r--r--include/mbgl/style/expression/case.hpp39
-rw-r--r--include/mbgl/style/expression/check_subtype.hpp17
-rw-r--r--include/mbgl/style/expression/coalesce.hpp48
-rw-r--r--include/mbgl/style/expression/coercion.hpp40
-rw-r--r--include/mbgl/style/expression/compound_expression.hpp147
-rw-r--r--include/mbgl/style/expression/equals.hpp33
-rw-r--r--include/mbgl/style/expression/expression.hpp190
-rw-r--r--include/mbgl/style/expression/find_zoom_curve.hpp20
-rw-r--r--include/mbgl/style/expression/get_covering_stops.hpp18
-rw-r--r--include/mbgl/style/expression/interpolate.hpp195
-rw-r--r--include/mbgl/style/expression/is_constant.hpp35
-rw-r--r--include/mbgl/style/expression/is_expression.hpp13
-rw-r--r--include/mbgl/style/expression/let.hpp80
-rw-r--r--include/mbgl/style/expression/literal.hpp52
-rw-r--r--include/mbgl/style/expression/match.hpp48
-rw-r--r--include/mbgl/style/expression/parsing_context.hpp156
-rw-r--r--include/mbgl/style/expression/step.hpp50
-rw-r--r--include/mbgl/style/expression/type.hpp112
-rw-r--r--include/mbgl/style/expression/value.hpp163
-rw-r--r--include/mbgl/style/function/camera_function.hpp63
-rw-r--r--include/mbgl/style/function/composite_function.hpp140
-rw-r--r--include/mbgl/style/function/convert.hpp356
-rw-r--r--include/mbgl/style/function/source_function.hpp45
-rw-r--r--include/mbgl/style/heatmap_color_property_value.hpp49
-rw-r--r--include/mbgl/style/layer.hpp10
-rw-r--r--include/mbgl/style/layer_type.hpp4
-rw-r--r--include/mbgl/style/layers/heatmap_layer.hpp86
-rw-r--r--include/mbgl/style/layers/hillshade_layer.hpp86
-rw-r--r--include/mbgl/style/layers/layer.hpp.ejs5
-rw-r--r--include/mbgl/style/layers/symbol_layer.hpp6
-rw-r--r--include/mbgl/style/source.hpp5
-rw-r--r--include/mbgl/style/sources/custom_geometry_source.hpp58
-rw-r--r--include/mbgl/style/sources/geojson_source.hpp2
-rw-r--r--include/mbgl/style/sources/raster_dem_source.hpp25
-rw-r--r--include/mbgl/style/sources/raster_source.hpp4
-rw-r--r--include/mbgl/style/types.hpp15
-rw-r--r--include/mbgl/tile/tile_id.hpp6
-rw-r--r--include/mbgl/util/any.hpp10
-rw-r--r--include/mbgl/util/enum.hpp1
-rw-r--r--include/mbgl/util/indexed_tuple.hpp10
-rw-r--r--include/mbgl/util/interpolate.hpp33
-rw-r--r--include/mbgl/util/optional.hpp3
-rw-r--r--include/mbgl/util/projection.hpp4
-rw-r--r--include/mbgl/util/run_loop.hpp54
-rw-r--r--include/mbgl/util/string.hpp8
-rw-r--r--include/mbgl/util/thread.hpp21
-rw-r--r--include/mbgl/util/tileset.hpp14
-rw-r--r--include/mbgl/util/tuple.hpp19
-rw-r--r--include/mbgl/util/unique_any.hpp275
-rw-r--r--include/mbgl/util/unitbezier.hpp17
m---------mapbox-gl-js0
-rw-r--r--misc/bench-icon.svg (renamed from common/bench-icon.svg)0
-rw-r--r--misc/ca-bundle.crt (renamed from common/ca-bundle.crt)1048
-rw-r--r--misc/cloudformation.template (renamed from cloudformation/travis.template)0
-rw-r--r--misc/mb-icon-blue-circle.svg (renamed from common/mb-icon-blue-circle.svg)0
-rw-r--r--misc/mb-icon-blue-square.png (renamed from common/mb-icon-blue-square.png)bin35662 -> 35662 bytes
-rw-r--r--misc/mb-icon-blue-square.svg (renamed from common/mb-icon-blue-square.svg)0
-rw-r--r--misc/proto/binary_program.proto (renamed from proto/binary_program.proto)0
-rw-r--r--misc/proto/glyphs.proto (renamed from proto/glyphs.proto)0
-rw-r--r--misc/proto/style.proto (renamed from proto/style.proto)0
-rw-r--r--misc/proto/vector_tile.proto (renamed from proto/vector_tile.proto)0
-rw-r--r--package.json10
-rw-r--r--platform/android/.gitignore4
-rw-r--r--platform/android/CHANGELOG.md64
-rw-r--r--platform/android/DISTRIBUTE.md2
-rw-r--r--platform/android/MapboxGLAndroidSDK/.gitignore2
-rw-r--r--platform/android/MapboxGLAndroidSDK/build.gradle74
-rw-r--r--platform/android/MapboxGLAndroidSDK/gradle.properties2
-rw-r--r--platform/android/MapboxGLAndroidSDK/lint-baseline-local.xml37
-rw-r--r--platform/android/MapboxGLAndroidSDK/lint/lint-baseline-ci.xml44
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/EmptyLocationSource.java107
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/LibraryLoader.java34
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java31
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/GeometryConstants.java77
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLng.java73
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java208
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationSource.java199
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/package-info.java4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java18
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Events.java36
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java111
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java64
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxEventWrapper.java57
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java24
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java17
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java11
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java6
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java23
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java6
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java83
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java1797
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HeatmapLayer.java221
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HillshadeLayer.java286
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Layer.java13
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java45
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java1417
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/RasterLayer.java22
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/layer.java.ejs2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/property_factory.java.ejs32
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySource.java203
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySourceOptions.java31
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonOptions.java11
-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/GeometryTileProvider.java22
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/RasterDemSource.java93
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/VectorSource.java2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MathUtils.java49
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values-fr/strings.xml17
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values-iw/strings.xml16
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values-sv/strings.xml5
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values-uk/strings.xml5
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/MapboxTest.java2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java224
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngTest.java14
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/expressions/ExpressionTest.java1033
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/build.gradle63
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/gradle-checkstyle.gradle19
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java698
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/OrientationTest.java41
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/action/OrientationChangeAction.java74
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/BaseActivityTest.java10
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/MarkerTest.java51
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/MarkerViewTest.java65
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/PolygonTest.java50
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/PolylineTest.java42
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraAnimateTest.java248
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraEaseTest.java248
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraMoveTest.java249
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/feature/QueryRenderedFeaturesPropertiesTest.java17
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/geometry/LatLngBoundsTest.java18
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/AttributionTest.java15
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/CompassViewTest.java53
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/offline/OfflineUtilsTest.java44
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/GeoJsonSourceTests.java8
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/HeatmapLayerTest.java626
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/HillshadeLayerTest.java505
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/ImageTest.java23
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RasterLayerTest.java18
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RuntimeStyleTests.java98
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java57
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/layer.junit.ejs2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml38
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java37
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java57
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/BulkMarkerActivity.java80
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/DynamicMarkerChangeActivity.java35
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewActivity.java215
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolygonActivity.java16
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolylineActivity.java57
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PressForMarkerActivity.java35
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimationTypeActivity.java146
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimatorActivity.java112
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraPositionActivity.java8
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/LatLngBoundsActivity.java10
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/ManualZoomActivity.java15
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/MaxMinZoomActivity.java14
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/ScrollByActivity.java14
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/customlayer/CustomLayerActivity.java20
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxCountActivity.java58
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxHighlightActivity.java69
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxSymbolCountActivity.java80
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesPropertiesActivity.java79
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QuerySourceFeaturesActivity.java56
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/MultiMapActivity.java2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/ViewPagerActivity.java1
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/imagegenerator/PrintActivity.java18
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/imagegenerator/SnapshotActivity.java22
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/DynamicInfoWindowAdapterActivity.java59
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/InfoWindowAdapterActivity.java12
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/BottomSheetActivity.java41
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DebugModeActivity.java73
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DoubleMapActivity.java62
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/LocalGlyphActivity.java4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapChangeActivity.java19
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapInDialogActivity.java11
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapPaddingActivity.java21
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/VisibilityChangeActivity.java12
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/DeleteRegionActivity.java15
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/OfflineActivity.java43
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/UpdateMetadataActivity.java17
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterActivity.java20
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterMarkerActivity.java9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterReuseActivity.java31
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/storage/UrlTransformActivity.java10
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/AnimatedImageSourceActivity.java1
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/BuildingFillExtrusionActivity.java38
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CircleLayerActivity.java27
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CustomSpriteActivity.java103
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/DataDrivenStyleActivity.java294
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionActivity.java103
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionStyleTestActivity.java8
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GeoJsonClusteringActivity.java14
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GridSourceActivity.java152
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/HeatmapLayerActivity.java216
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/HillshadeLayerActivity.java84
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java222
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleTestActivity.java8
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleTimingTestActivity.java32
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/StyleFileActivity.java36
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java138
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolLayerActivity.java79
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/ZoomFunctionSymbolLayerActivity.java57
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewAnimationActivity.java46
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewDebugModeActivity.java70
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewResizeActivity.java23
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/BaseLocationActivity.java4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/CustomLocationEngineActivity.java20
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MockLocationEngine.java13
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationDrawableActivity.java12
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTintActivity.java136
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationToggleActivity.java18
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTrackingModeActivity.java58
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/adapter/FeatureSectionAdapter.java12
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/other/OfflineDownloadRegionDialog.java22
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/other/OfflineListRegionsDialog.java19
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/FontCache.java4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/GeoParseUtil.java40
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/OfflineUtils.java19
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_data_driven_style.xml12
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_fill_extrusion_layer.xml8
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_grid_source.xml17
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_heatmaplayer.xml14
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_hillshade_layer.xml17
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_local_glyph.xml2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_generator_symbol.xml4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/actions.xml1
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml2
-rw-r--r--platform/android/README.md85
-rw-r--r--platform/android/build.gradle17
-rw-r--r--platform/android/config.cmake65
-rw-r--r--platform/android/dependencies.gradle49
-rw-r--r--platform/android/gradle/dependencies.gradle55
-rw-r--r--platform/android/gradle/gradle-checkstyle.gradle (renamed from platform/android/MapboxGLAndroidSDK/gradle-checkstyle.gradle)3
-rw-r--r--platform/android/gradle/gradle-config.gradle (renamed from platform/android/MapboxGLAndroidSDKTestApp/gradle-config.gradle)0
-rw-r--r--platform/android/gradle/gradle-javadoc.gradle (renamed from platform/android/MapboxGLAndroidSDK/gradle-javadoc.gradle)2
-rw-r--r--platform/android/gradle/gradle-lint.gradle (renamed from platform/android/gradle-lint.gradle)0
-rw-r--r--platform/android/gradle/gradle-make.gradle (renamed from platform/android/MapboxGLAndroidSDKTestApp/gradle-make.gradle)0
-rw-r--r--platform/android/gradle/gradle-publish.gradle (renamed from platform/android/MapboxGLAndroidSDK/gradle-publish.gradle)0
-rw-r--r--platform/android/gradle/gradle-tests-staticblockremover.gradle (renamed from platform/android/MapboxGLAndroidSDK/gradle-tests-staticblockremover.gradle)0
-rw-r--r--platform/android/gradle/wrapper/gradle-wrapper.properties3
-rw-r--r--platform/android/mbgl/gl/gl_impl.hpp5
-rw-r--r--platform/android/scripts/exclude-activity-gen.json7
-rwxr-xr-x[-rw-r--r--]platform/android/scripts/generate-style-code.js6
-rwxr-xr-xplatform/android/src/android_renderer_backend.cpp2
-rwxr-xr-xplatform/android/src/android_renderer_backend.hpp2
-rw-r--r--platform/android/src/android_renderer_frontend.cpp4
-rw-r--r--platform/android/src/android_renderer_frontend.hpp2
-rw-r--r--platform/android/src/example_custom_layer.cpp4
-rw-r--r--platform/android/src/geojson/conversion/geometry.hpp130
-rw-r--r--platform/android/src/geojson/feature.cpp18
-rw-r--r--platform/android/src/geojson/feature.hpp8
-rw-r--r--platform/android/src/geojson/feature_collection.cpp29
-rw-r--r--platform/android/src/geojson/feature_collection.hpp4
-rw-r--r--platform/android/src/geojson/geometry.cpp2
-rw-r--r--platform/android/src/geojson/geometry.hpp2
-rw-r--r--platform/android/src/geojson/line_string.cpp29
-rw-r--r--platform/android/src/geojson/line_string.hpp6
-rw-r--r--platform/android/src/geojson/multi_line_string.cpp22
-rw-r--r--platform/android/src/geojson/multi_line_string.hpp6
-rw-r--r--platform/android/src/geojson/multi_point.cpp10
-rw-r--r--platform/android/src/geojson/multi_point.hpp4
-rw-r--r--platform/android/src/geojson/multi_polygon.cpp16
-rw-r--r--platform/android/src/geojson/multi_polygon.hpp4
-rw-r--r--platform/android/src/geojson/point.cpp42
-rw-r--r--platform/android/src/geojson/point.hpp10
-rw-r--r--platform/android/src/geojson/polygon.cpp12
-rw-r--r--platform/android/src/geojson/polygon.hpp6
-rw-r--r--platform/android/src/geojson/position.cpp27
-rw-r--r--platform/android/src/geojson/position.hpp27
-rwxr-xr-xplatform/android/src/jni.cpp6
-rwxr-xr-xplatform/android/src/native_map_view.cpp30
-rwxr-xr-xplatform/android/src/native_map_view.hpp6
-rw-r--r--platform/android/src/run_loop.cpp7
-rw-r--r--platform/android/src/style/android_conversion.hpp147
-rw-r--r--platform/android/src/style/conversion/filter.hpp3
-rw-r--r--platform/android/src/style/conversion/geojson.hpp24
-rw-r--r--platform/android/src/style/conversion/types.hpp7
-rw-r--r--platform/android/src/style/conversion/types_string_values.hpp14
-rw-r--r--platform/android/src/style/conversion/url_or_tileset.hpp9
-rw-r--r--platform/android/src/style/layers/heatmap_layer.cpp134
-rw-r--r--platform/android/src/style/layers/heatmap_layer.hpp50
-rw-r--r--platform/android/src/style/layers/hillshade_layer.cpp163
-rw-r--r--platform/android/src/style/layers/hillshade_layer.hpp56
-rw-r--r--platform/android/src/style/layers/layer.cpp26
-rw-r--r--platform/android/src/style/layers/layers.cpp8
-rw-r--r--platform/android/src/style/layers/raster_layer.cpp15
-rw-r--r--platform/android/src/style/layers/raster_layer.hpp2
-rw-r--r--platform/android/src/style/sources/custom_geometry_source.cpp143
-rw-r--r--platform/android/src/style/sources/custom_geometry_source.hpp47
-rw-r--r--platform/android/src/style/sources/geojson_source.cpp20
-rw-r--r--platform/android/src/style/sources/geojson_source.hpp5
-rw-r--r--platform/android/src/style/sources/image_source.cpp10
-rw-r--r--platform/android/src/style/sources/image_source.hpp5
-rw-r--r--platform/android/src/style/sources/raster_dem_source.cpp63
-rw-r--r--platform/android/src/style/sources/raster_dem_source.hpp33
-rw-r--r--platform/android/src/style/sources/raster_source.cpp10
-rw-r--r--platform/android/src/style/sources/raster_source.hpp5
-rw-r--r--platform/android/src/style/sources/source.cpp119
-rw-r--r--platform/android/src/style/sources/source.hpp36
-rw-r--r--platform/android/src/style/sources/sources.cpp60
-rw-r--r--platform/android/src/style/sources/sources.hpp18
-rw-r--r--platform/android/src/style/sources/unknown_source.cpp10
-rw-r--r--platform/android/src/style/sources/unknown_source.hpp5
-rw-r--r--platform/android/src/style/sources/vector_source.cpp10
-rw-r--r--platform/android/src/style/sources/vector_source.hpp5
-rw-r--r--platform/android/src/style/value.cpp2
-rw-r--r--platform/android/src/style/value.hpp8
-rw-r--r--platform/darwin/.gitignore3
-rw-r--r--platform/darwin/docs/guides/For Style Authors.md.ejs127
-rw-r--r--platform/darwin/docs/guides/Predicates and Expressions.md414
-rw-r--r--platform/darwin/docs/guides/Using Style Functions at Runtime.md.ejs2
-rw-r--r--platform/darwin/docs/theme/assets/css/jazzy.css.scss1
-rw-r--r--platform/darwin/mbgl/gl/gl_impl.hpp16
-rw-r--r--platform/darwin/mbgl/util/image+MGLAdditions.hpp2
-rw-r--r--platform/darwin/resources/ca.lproj/Foundation.stringsbin13170 -> 6598 bytes
-rw-r--r--platform/darwin/resources/de.lproj/Foundation.stringsbin13082 -> 6575 bytes
-rw-r--r--platform/darwin/resources/fr.lproj/Foundation.stringsbin13222 -> 6625 bytes
-rw-r--r--platform/darwin/resources/he.lproj/Foundation.strings297
-rw-r--r--platform/darwin/resources/ja.lproj/Foundation.stringsbin12488 -> 6691 bytes
-rw-r--r--platform/darwin/resources/lt.lproj/Foundation.stringsbin13652 -> 6954 bytes
-rw-r--r--platform/darwin/resources/pt-BR.lproj/Foundation.stringsbin13274 -> 6656 bytes
-rw-r--r--platform/darwin/resources/ru.lproj/Foundation.stringsbin13398 -> 7308 bytes
-rw-r--r--platform/darwin/resources/sv.lproj/Foundation.stringsbin13138 -> 6598 bytes
-rw-r--r--platform/darwin/resources/uk.lproj/Foundation.stringsbin13904 -> 7762 bytes
-rw-r--r--platform/darwin/resources/zh-Hans.lproj/Foundation.stringsbin12528 -> 6751 bytes
-rw-r--r--platform/darwin/resources/zh-Hant.lproj/Foundation.stringsbin12496 -> 6703 bytes
-rwxr-xr-x[-rw-r--r--]platform/darwin/scripts/generate-style-code.js198
-rw-r--r--platform/darwin/scripts/style-spec-overrides-v8.json21
-rwxr-xr-x[-rw-r--r--]platform/darwin/scripts/update-examples.js12
-rw-r--r--platform/darwin/src/MGLAbstractShapeSource.h124
-rw-r--r--platform/darwin/src/MGLAbstractShapeSource.mm136
-rw-r--r--platform/darwin/src/MGLAbstractShapeSource_Private.h22
-rw-r--r--platform/darwin/src/MGLAccountManager.m2
-rw-r--r--platform/darwin/src/MGLAttributionInfo.mm1
-rw-r--r--platform/darwin/src/MGLBackgroundStyleLayer.h75
-rw-r--r--platform/darwin/src/MGLBackgroundStyleLayer.mm30
-rw-r--r--platform/darwin/src/MGLCircleStyleLayer.h397
-rw-r--r--platform/darwin/src/MGLCircleStyleLayer.mm122
-rw-r--r--platform/darwin/src/MGLComputedShapeSource.h113
-rw-r--r--platform/darwin/src/MGLComputedShapeSource.mm169
-rw-r--r--platform/darwin/src/MGLConversion.h227
-rw-r--r--platform/darwin/src/MGLFeature.mm8
-rw-r--r--platform/darwin/src/MGLFillExtrusionStyleLayer.h231
-rw-r--r--platform/darwin/src/MGLFillExtrusionStyleLayer.mm78
-rw-r--r--platform/darwin/src/MGLFillStyleLayer.h253
-rw-r--r--platform/darwin/src/MGLFillStyleLayer.mm82
-rw-r--r--platform/darwin/src/MGLForegroundStyleLayer.h9
-rw-r--r--platform/darwin/src/MGLHeatmapStyleLayer.h189
-rw-r--r--platform/darwin/src/MGLHeatmapStyleLayer.mm210
-rw-r--r--platform/darwin/src/MGLHillshadeStyleLayer.h257
-rw-r--r--platform/darwin/src/MGLHillshadeStyleLayer.mm239
-rw-r--r--platform/darwin/src/MGLLight.h102
-rw-r--r--platform/darwin/src/MGLLight.h.ejs6
-rw-r--r--platform/darwin/src/MGLLight.mm28
-rw-r--r--platform/darwin/src/MGLLight.mm.ejs16
-rw-r--r--platform/darwin/src/MGLLineStyleLayer.h450
-rw-r--r--platform/darwin/src/MGLLineStyleLayer.mm152
-rw-r--r--platform/darwin/src/MGLMapSnapshotter.mm1
-rw-r--r--platform/darwin/src/MGLNetworkConfiguration.m2
-rw-r--r--platform/darwin/src/MGLPolygon+MGLAdditions.h7
-rw-r--r--platform/darwin/src/MGLPolygon+MGLAdditions.m27
-rw-r--r--platform/darwin/src/MGLPolygon.mm26
-rw-r--r--platform/darwin/src/MGLPolygon_Private.h11
-rw-r--r--platform/darwin/src/MGLPolyline+MGLAdditions.h7
-rw-r--r--platform/darwin/src/MGLPolyline+MGLAdditions.m14
-rw-r--r--platform/darwin/src/MGLPolyline.mm13
-rw-r--r--platform/darwin/src/MGLPolyline_Private.h12
-rw-r--r--platform/darwin/src/MGLRasterDEMSource.h37
-rw-r--r--platform/darwin/src/MGLRasterDEMSource.mm17
-rw-r--r--platform/darwin/src/MGLRasterSource.mm11
-rw-r--r--platform/darwin/src/MGLRasterSource_Private.h13
-rw-r--r--platform/darwin/src/MGLRasterStyleLayer.h178
-rw-r--r--platform/darwin/src/MGLRasterStyleLayer.mm100
-rw-r--r--platform/darwin/src/MGLRendererConfiguration.h2
-rw-r--r--platform/darwin/src/MGLRendererFrontend.h4
-rw-r--r--platform/darwin/src/MGLShapeSource.h75
-rw-r--r--platform/darwin/src/MGLShapeSource.mm62
-rw-r--r--platform/darwin/src/MGLShapeSource_Private.h3
-rw-r--r--platform/darwin/src/MGLStyle.mm69
-rw-r--r--platform/darwin/src/MGLStyleLayer.h.ejs13
-rw-r--r--platform/darwin/src/MGLStyleLayer.mm.ejs78
-rw-r--r--platform/darwin/src/MGLStyleValue.h470
-rw-r--r--platform/darwin/src/MGLStyleValue.mm393
-rw-r--r--platform/darwin/src/MGLStyleValue_Private.h640
-rw-r--r--platform/darwin/src/MGLSymbolStyleLayer.h1650
-rw-r--r--platform/darwin/src/MGLSymbolStyleLayer.mm584
-rw-r--r--platform/darwin/src/MGLVectorSource+MGLAdditions.h15
-rw-r--r--platform/darwin/src/MGLVectorSource+MGLAdditions.m53
-rw-r--r--platform/darwin/src/MGLVectorSource.mm53
-rw-r--r--platform/darwin/src/MGLVectorSource_Private.h9
-rw-r--r--platform/darwin/src/MGLVectorStyleLayer.h113
-rw-r--r--platform/darwin/src/NSArray+MGLAdditions.mm2
-rw-r--r--platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm44
-rw-r--r--platform/darwin/src/NSCompoundPredicate+MGLAdditions.mm30
-rw-r--r--platform/darwin/src/NSDictionary+MGLAdditions.mm2
-rw-r--r--platform/darwin/src/NSExpression+MGLAdditions.h43
-rw-r--r--platform/darwin/src/NSExpression+MGLAdditions.mm585
-rw-r--r--platform/darwin/src/NSExpression+MGLPrivateAdditions.h79
-rw-r--r--platform/darwin/src/NSPredicate+MGLAdditions.h10
-rw-r--r--platform/darwin/src/NSPredicate+MGLAdditions.mm91
-rw-r--r--platform/darwin/src/NSString+MGLAdditions.h14
-rw-r--r--platform/darwin/src/NSString+MGLAdditions.m19
-rw-r--r--platform/darwin/src/headless_backend_cgl.cpp132
-rw-r--r--platform/darwin/src/headless_backend_eagl.mm61
-rw-r--r--platform/darwin/src/headless_display_cgl.cpp60
-rw-r--r--platform/darwin/src/image.mm2
-rw-r--r--platform/darwin/src/run_loop.cpp7
-rw-r--r--platform/darwin/test/MGLAttributionInfoTests.m4
-rw-r--r--platform/darwin/test/MGLBackgroundStyleLayerTests.mm129
-rw-r--r--platform/darwin/test/MGLCircleStyleLayerTests.mm550
-rw-r--r--platform/darwin/test/MGLComputedShapeSourceTests.m24
-rw-r--r--platform/darwin/test/MGLCoordinateFormatterTests.m21
-rw-r--r--platform/darwin/test/MGLDocumentationExampleTests.swift123
-rw-r--r--platform/darwin/test/MGLDocumentationGuideTests.swift150
-rw-r--r--platform/darwin/test/MGLExpressionTests.mm463
-rw-r--r--platform/darwin/test/MGLFeatureTests.mm27
-rw-r--r--platform/darwin/test/MGLFillExtrusionStyleLayerTests.mm334
-rw-r--r--platform/darwin/test/MGLFillStyleLayerTests.mm334
-rw-r--r--platform/darwin/test/MGLHeatmapColorTests.mm61
-rw-r--r--platform/darwin/test/MGLHeatmapStyleLayerTests.mm296
-rw-r--r--platform/darwin/test/MGLHillshadeStyleLayerTests.mm348
-rw-r--r--platform/darwin/test/MGLLightTest.mm49
-rw-r--r--platform/darwin/test/MGLLightTest.mm.ejs17
-rw-r--r--platform/darwin/test/MGLLineStyleLayerTests.mm659
-rw-r--r--platform/darwin/test/MGLNSStringAdditionsTests.m34
-rw-r--r--platform/darwin/test/MGLPredicateTests.mm7
-rw-r--r--platform/darwin/test/MGLRasterStyleLayerTests.mm310
-rw-r--r--platform/darwin/test/MGLSDKTestHelpers.swift1
-rw-r--r--platform/darwin/test/MGLShapeSourceTests.mm4
-rw-r--r--platform/darwin/test/MGLStyleLayerTests.h3
-rw-r--r--platform/darwin/test/MGLStyleLayerTests.mm.ejs67
-rw-r--r--platform/darwin/test/MGLStyleValueTests.h4
-rw-r--r--platform/darwin/test/MGLStyleValueTests.m113
-rw-r--r--platform/darwin/test/MGLStyleValueTests.swift362
-rw-r--r--platform/darwin/test/MGLSymbolStyleLayerTests.mm2292
-rw-r--r--platform/darwin/test/test-Bridging-Header.h2
-rw-r--r--platform/default/asset_file_source.cpp27
-rw-r--r--platform/default/default_file_source.cpp12
-rw-r--r--platform/default/headless_backend_osmesa.cpp44
-rw-r--r--platform/default/local_file_source.cpp22
-rw-r--r--platform/default/mbgl/gl/headless_backend.cpp17
-rw-r--r--platform/default/mbgl/gl/headless_backend.hpp15
-rw-r--r--platform/default/mbgl/gl/headless_display.cpp15
-rw-r--r--platform/default/mbgl/gl/headless_display.hpp34
-rw-r--r--platform/default/mbgl/gl/headless_frontend.cpp8
-rw-r--r--platform/default/mbgl/gl/headless_frontend.hpp5
-rw-r--r--platform/default/mbgl/map/map_snapshotter.cpp2
-rw-r--r--platform/default/mbgl/storage/offline.cpp6
-rw-r--r--platform/default/mbgl/storage/offline_download.cpp15
-rw-r--r--platform/default/mbgl/storage/offline_download.hpp2
-rw-r--r--platform/default/run_loop.cpp7
-rw-r--r--platform/glfw/glfw_view.cpp29
-rw-r--r--platform/glfw/glfw_view.hpp7
-rw-r--r--platform/glfw/main.cpp100
-rw-r--r--platform/glfw/settings_json.hpp3
-rw-r--r--platform/ios/CHANGELOG.md31
-rw-r--r--platform/ios/DEVELOPING.md10
-rw-r--r--platform/ios/INSTALL.md12
-rw-r--r--platform/ios/Integration Test Harness/AppDelegate.h8
-rw-r--r--platform/ios/Integration Test Harness/AppDelegate.m14
-rw-r--r--platform/ios/Integration Test Harness/Assets.xcassets/AppIcon.appiconset/Contents.json93
-rw-r--r--platform/ios/Integration Test Harness/Base.lproj/LaunchScreen.storyboard25
-rw-r--r--platform/ios/Integration Test Harness/Info.plist43
-rw-r--r--platform/ios/Integration Test Harness/main.m8
-rw-r--r--platform/ios/Integration Tests/Info.plist22
-rw-r--r--platform/ios/Integration Tests/MBGLIntegrationTests.m77
-rw-r--r--platform/ios/README.md1
-rw-r--r--platform/ios/app/Assets.xcassets/AppIcon.appiconset/Contents.json3
-rw-r--r--platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-1024.pngbin0 -> 12225 bytes
-rw-r--r--platform/ios/app/Info.plist2
-rw-r--r--platform/ios/app/MBXViewController.m239
-rw-r--r--platform/ios/app/he.lproj/Localizable.strings0
-rw-r--r--platform/ios/benchmark/Info.plist2
-rw-r--r--platform/ios/config.cmake17
-rw-r--r--platform/ios/docs/guides/For Style Authors.md125
-rw-r--r--platform/ios/docs/guides/Using Style Functions at Runtime.md84
-rw-r--r--platform/ios/framework/Settings.bundle/he.lproj/Root.strings3
-rw-r--r--platform/ios/ios.xcodeproj/project.pbxproj568
-rw-r--r--platform/ios/ios.xcodeproj/xcshareddata/xcschemes/CI.xcscheme4
-rw-r--r--platform/ios/ios.xcodeproj/xcshareddata/xcschemes/Integration Test Harness.xcscheme103
-rw-r--r--platform/ios/ios.xcodeproj/xcshareddata/xcschemes/bench.xcscheme4
-rw-r--r--platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic+static.xcscheme4
-rw-r--r--platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme4
-rw-r--r--platform/ios/ios.xcodeproj/xcshareddata/xcschemes/iosapp.xcscheme4
-rw-r--r--platform/ios/ios.xcodeproj/xcshareddata/xcschemes/static.xcscheme4
-rw-r--r--platform/ios/jazzy.yml9
-rw-r--r--platform/ios/resources/es.lproj/Localizable.stringsbin8156 -> 4100 bytes
-rw-r--r--platform/ios/resources/fr.lproj/Localizable.strings66
-rw-r--r--platform/ios/resources/fr.lproj/Localizable.stringsdict38
-rw-r--r--platform/ios/resources/he.lproj/Localizable.strings117
-rw-r--r--platform/ios/resources/sv.lproj/Localizable.strings58
-rw-r--r--platform/ios/resources/uk.lproj/Localizable.strings56
-rw-r--r--platform/ios/resources/uk.lproj/Localizable.stringsdict44
-rw-r--r--platform/ios/resources/vi.lproj/Localizable.stringsbin8122 -> 4450 bytes
-rwxr-xr-xplatform/ios/scripts/package.sh7
-rw-r--r--platform/ios/src/MGLAnnotationView.h4
-rw-r--r--platform/ios/src/MGLAnnotationView.mm13
-rw-r--r--platform/ios/src/MGLMapAccessibilityElement.mm29
-rw-r--r--platform/ios/src/MGLMapView.mm38
-rw-r--r--platform/ios/src/Mapbox.h6
-rw-r--r--platform/ios/src/NSOrthography+MGLAdditions.h18
-rw-r--r--platform/ios/src/NSOrthography+MGLAdditions.m31
-rw-r--r--platform/ios/src/UIImage+MGLAdditions.mm4
-rw-r--r--platform/ios/test/MGLMapAccessibilityElementTests.m8
-rw-r--r--platform/ios/test/MGLMapViewLayoutTests.m2
-rw-r--r--platform/ios/test/MGLNSOrthographyAdditionsTests.m19
-rw-r--r--platform/ios/uitest/LaunchScreen.xib2
-rw-r--r--platform/ios/uitest/ios-tests.xcodeproj/xcshareddata/xcschemes/Mapbox GL Tests.xcscheme4
-rw-r--r--platform/linux/config.cmake14
-rw-r--r--platform/linux/mbgl/gl/gl_impl.hpp11
-rw-r--r--platform/linux/src/headless_backend_egl.cpp155
-rw-r--r--platform/linux/src/headless_backend_glx.cpp147
-rw-r--r--platform/linux/src/headless_display_egl.cpp68
-rw-r--r--platform/linux/src/headless_display_glx.cpp78
-rw-r--r--platform/macos/CHANGELOG.md23
-rw-r--r--platform/macos/app/Assets.xcassets/Layers/background.imageset/Contents.json3
-rw-r--r--platform/macos/app/Assets.xcassets/Layers/heatmap.imageset/Contents.json16
-rw-r--r--platform/macos/app/Assets.xcassets/Layers/heatmap.imageset/heatmap.pdfbin0 -> 1262 bytes
-rw-r--r--platform/macos/app/Assets.xcassets/Layers/hillshade.imageset/Contents.json16
-rw-r--r--platform/macos/app/Assets.xcassets/Layers/hillshade.imageset/hillshade.pdf70
-rw-r--r--platform/macos/app/Base.lproj/MainMenu.xib48
-rw-r--r--platform/macos/app/MapDocument.m198
-rw-r--r--platform/macos/app/StyleLayerIconTransformer.m8
-rw-r--r--platform/macos/app/he.lproj/Localizable.strings0
-rw-r--r--platform/macos/app/heatmap.json809
-rw-r--r--platform/macos/app/resources/heatmap.svg72
-rw-r--r--platform/macos/app/resources/hillshade.svg87
-rw-r--r--platform/macos/config.cmake33
-rw-r--r--platform/macos/docs/guides/For Style Authors.md124
-rw-r--r--platform/macos/docs/guides/Using Style Functions at Runtime.md84
-rw-r--r--platform/macos/jazzy.yml9
-rw-r--r--platform/macos/macos.xcodeproj/project.pbxproj140
-rw-r--r--platform/macos/macos.xcodeproj/xcshareddata/xcschemes/CI.xcscheme8
-rw-r--r--platform/macos/macos.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme4
-rw-r--r--platform/macos/macos.xcodeproj/xcshareddata/xcschemes/macosapp.xcscheme4
-rw-r--r--platform/macos/sdk/Info.plist6
-rw-r--r--platform/macos/sdk/es.lproj/Localizable.stringsbin1716 -> 862 bytes
-rw-r--r--platform/macos/sdk/fr.lproj/Localizable.strings10
-rw-r--r--platform/macos/sdk/he.lproj/Localizable.strings27
-rw-r--r--platform/macos/sdk/sv.lproj/Localizable.strings14
-rw-r--r--platform/macos/sdk/uk.lproj/Localizable.strings2
-rw-r--r--platform/macos/sdk/vi.lproj/Localizable.stringsbin1654 -> 926 bytes
-rw-r--r--platform/macos/src/MGLMapView.mm2
-rw-r--r--platform/macos/src/Mapbox.h6
-rw-r--r--platform/macos/src/NSImage+MGLAdditions.mm4
-rw-r--r--platform/node/CHANGELOG.md11
-rw-r--r--platform/node/DEVELOPING.md2
-rw-r--r--platform/node/src/node_conversion.hpp183
-rw-r--r--platform/node/src/node_expression.cpp241
-rw-r--r--platform/node/src/node_expression.hpp43
-rw-r--r--platform/node/src/node_geojson.hpp17
-rw-r--r--platform/node/src/node_map.cpp167
-rw-r--r--platform/node/src/node_map.hpp5
-rw-r--r--platform/node/src/node_mapbox_gl_native.cpp2
-rw-r--r--platform/node/test/expression.test.js85
-rw-r--r--platform/node/test/ignores.json96
-rw-r--r--platform/node/test/js/map.test.js4
-rw-r--r--platform/node/test/suite_implementation.js25
-rw-r--r--platform/qt/README.md75
-rw-r--r--platform/qt/app/mapwindow.cpp53
-rw-r--r--platform/qt/config.cmake55
-rw-r--r--platform/qt/config.qdocconf2
-rw-r--r--platform/qt/include/QQuickMapboxGL1
-rw-r--r--platform/qt/include/QQuickMapboxGLMapParameter1
-rw-r--r--platform/qt/include/qmapbox.hpp28
-rw-r--r--platform/qt/include/qmapboxgl.hpp36
-rw-r--r--platform/qt/mbgl/gl/gl_impl.hpp178
-rwxr-xr-xplatform/qt/ninja.exebin0 -> 504320 bytes
-rw-r--r--platform/qt/qt.cmake61
-rw-r--r--platform/qt/qt5.cmake5
-rw-r--r--platform/qt/resources/common.qrc2
-rw-r--r--platform/qt/src/headless_backend_qt.cpp36
-rw-r--r--platform/qt/src/http_file_source.cpp3
-rw-r--r--platform/qt/src/http_request.cpp7
-rw-r--r--platform/qt/src/http_request.hpp2
-rw-r--r--platform/qt/src/qmapbox.cpp6
-rw-r--r--platform/qt/src/qmapboxgl.cpp422
-rw-r--r--platform/qt/src/qmapboxgl_map_observer.cpp95
-rw-r--r--platform/qt/src/qmapboxgl_map_observer.hpp45
-rw-r--r--platform/qt/src/qmapboxgl_map_renderer.cpp86
-rw-r--r--platform/qt/src/qmapboxgl_map_renderer.hpp63
-rw-r--r--platform/qt/src/qmapboxgl_p.hpp80
-rw-r--r--platform/qt/src/qmapboxgl_renderer_backend.cpp49
-rw-r--r--platform/qt/src/qmapboxgl_renderer_backend.hpp34
-rw-r--r--platform/qt/src/qmapboxgl_renderer_frontend_p.cpp37
-rw-r--r--platform/qt/src/qmapboxgl_renderer_frontend_p.hpp35
-rw-r--r--platform/qt/src/qmapboxgl_renderer_observer.hpp51
-rw-r--r--platform/qt/src/qt_conversion.hpp191
-rw-r--r--platform/qt/src/qt_geojson.cpp166
-rw-r--r--platform/qt/src/qt_geojson.hpp194
-rw-r--r--platform/qt/src/qt_image.cpp5
-rw-r--r--platform/qt/src/qt_logging.cpp12
-rw-r--r--platform/qt/src/run_loop.cpp7
-rw-r--r--platform/qt/src/sqlite3.cpp91
-rw-r--r--platform/qt/test/qmapboxgl.test.cpp9
-rw-r--r--scripts/check-sha256.ps17
-rwxr-xr-xscripts/generate-benchmark-files.sh20
-rwxr-xr-xscripts/generate-cmake-files.js32
-rwxr-xr-xscripts/generate-core-files.sh20
-rwxr-xr-xscripts/generate-shaders.js3
-rwxr-xr-x[-rw-r--r--]scripts/generate-style-code.js13
-rwxr-xr-xscripts/generate-test-files.sh20
-rwxr-xr-xscripts/nitpick/generated-code.js50
-rw-r--r--scripts/nitpick/index.js28
-rwxr-xr-xscripts/nitpick/submodule-pin.js13
-rw-r--r--scripts/style-code.js15
-rw-r--r--scripts/style-spec.js3
-rwxr-xr-xscripts/update_ca_bundle.sh2
-rw-r--r--src/mbgl/annotation/render_annotation_source.cpp9
-rw-r--r--src/mbgl/annotation/render_annotation_source.hpp7
-rw-r--r--src/mbgl/geometry/dem_data.cpp106
-rw-r--r--src/mbgl/geometry/dem_data.hpp49
-rw-r--r--src/mbgl/geometry/feature_index.cpp28
-rw-r--r--src/mbgl/geometry/feature_index.hpp33
-rw-r--r--src/mbgl/gl/color_mode.hpp6
-rw-r--r--src/mbgl/gl/context.cpp50
-rw-r--r--src/mbgl/gl/context.hpp47
-rw-r--r--src/mbgl/gl/gl.hpp30
-rw-r--r--src/mbgl/gl/index_buffer.hpp1
-rw-r--r--src/mbgl/gl/types.hpp9
-rw-r--r--src/mbgl/layout/symbol_instance.cpp33
-rw-r--r--src/mbgl/layout/symbol_instance.hpp16
-rw-r--r--src/mbgl/layout/symbol_layout.cpp304
-rw-r--r--src/mbgl/layout/symbol_layout.hpp22
-rw-r--r--src/mbgl/layout/symbol_projection.cpp156
-rw-r--r--src/mbgl/layout/symbol_projection.hpp42
-rw-r--r--src/mbgl/map/map.cpp35
-rw-r--r--src/mbgl/map/transform.cpp26
-rw-r--r--src/mbgl/map/transform.hpp8
-rw-r--r--src/mbgl/map/transform_state.cpp30
-rw-r--r--src/mbgl/map/transform_state.hpp5
-rw-r--r--src/mbgl/programs/attributes.hpp15
-rw-r--r--src/mbgl/programs/background_program.cpp47
-rw-r--r--src/mbgl/programs/background_program.hpp83
-rw-r--r--src/mbgl/programs/clipping_mask_program.hpp26
-rw-r--r--src/mbgl/programs/collision_box_program.hpp157
-rw-r--r--src/mbgl/programs/fill_extrusion_program.hpp10
-rw-r--r--src/mbgl/programs/fill_program.hpp4
-rw-r--r--src/mbgl/programs/heatmap_program.cpp7
-rw-r--r--src/mbgl/programs/heatmap_program.hpp49
-rw-r--r--src/mbgl/programs/heatmap_texture_program.cpp7
-rw-r--r--src/mbgl/programs/heatmap_texture_program.hpp43
-rw-r--r--src/mbgl/programs/hillshade_prepare_program.cpp7
-rw-r--r--src/mbgl/programs/hillshade_prepare_program.hpp49
-rw-r--r--src/mbgl/programs/hillshade_program.cpp7
-rw-r--r--src/mbgl/programs/hillshade_program.hpp55
-rw-r--r--src/mbgl/programs/programs.hpp26
-rw-r--r--src/mbgl/programs/symbol_program.cpp12
-rw-r--r--src/mbgl/programs/symbol_program.hpp85
-rw-r--r--src/mbgl/programs/uniforms.hpp7
-rw-r--r--src/mbgl/renderer/buckets/circle_bucket.cpp5
-rw-r--r--src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp6
-rw-r--r--src/mbgl/renderer/buckets/heatmap_bucket.cpp98
-rw-r--r--src/mbgl/renderer/buckets/heatmap_bucket.hpp40
-rw-r--r--src/mbgl/renderer/buckets/hillshade_bucket.cpp113
-rw-r--r--src/mbgl/renderer/buckets/hillshade_bucket.hpp59
-rw-r--r--src/mbgl/renderer/buckets/symbol_bucket.cpp167
-rw-r--r--src/mbgl/renderer/buckets/symbol_bucket.hpp59
-rw-r--r--src/mbgl/renderer/frame_history.cpp81
-rw-r--r--src/mbgl/renderer/frame_history.hpp41
-rw-r--r--src/mbgl/renderer/layers/render_background_layer.cpp27
-rw-r--r--src/mbgl/renderer/layers/render_circle_layer.cpp11
-rw-r--r--src/mbgl/renderer/layers/render_heatmap_layer.cpp178
-rw-r--r--src/mbgl/renderer/layers/render_heatmap_layer.hpp48
-rw-r--r--src/mbgl/renderer/layers/render_hillshade_layer.cpp164
-rw-r--r--src/mbgl/renderer/layers/render_hillshade_layer.hpp38
-rw-r--r--src/mbgl/renderer/layers/render_raster_layer.cpp4
-rw-r--r--src/mbgl/renderer/layers/render_symbol_layer.cpp83
-rw-r--r--src/mbgl/renderer/paint_parameters.cpp10
-rw-r--r--src/mbgl/renderer/paint_parameters.hpp9
-rw-r--r--src/mbgl/renderer/paint_property_binder.hpp11
-rw-r--r--src/mbgl/renderer/render_layer.cpp6
-rw-r--r--src/mbgl/renderer/render_layer.hpp11
-rw-r--r--src/mbgl/renderer/render_source.cpp6
-rw-r--r--src/mbgl/renderer/render_source.hpp8
-rw-r--r--src/mbgl/renderer/render_static_data.cpp12
-rw-r--r--src/mbgl/renderer/render_static_data.hpp4
-rw-r--r--src/mbgl/renderer/render_tile.cpp8
-rw-r--r--src/mbgl/renderer/renderer.cpp4
-rw-r--r--src/mbgl/renderer/renderer_backend.cpp2
-rw-r--r--src/mbgl/renderer/renderer_impl.cpp177
-rw-r--r--src/mbgl/renderer/renderer_impl.hpp18
-rw-r--r--src/mbgl/renderer/sources/render_custom_geometry_source.cpp87
-rw-r--r--src/mbgl/renderer/sources/render_custom_geometry_source.hpp50
-rw-r--r--src/mbgl/renderer/sources/render_geojson_source.cpp9
-rw-r--r--src/mbgl/renderer/sources/render_geojson_source.hpp7
-rw-r--r--src/mbgl/renderer/sources/render_image_source.cpp63
-rw-r--r--src/mbgl/renderer/sources/render_image_source.hpp7
-rw-r--r--src/mbgl/renderer/sources/render_raster_dem_source.cpp166
-rw-r--r--src/mbgl/renderer/sources/render_raster_dem_source.hpp59
-rw-r--r--src/mbgl/renderer/sources/render_raster_source.cpp7
-rw-r--r--src/mbgl/renderer/sources/render_raster_source.hpp7
-rw-r--r--src/mbgl/renderer/sources/render_vector_source.cpp9
-rw-r--r--src/mbgl/renderer/sources/render_vector_source.hpp7
-rw-r--r--src/mbgl/renderer/tile_pyramid.cpp57
-rw-r--r--src/mbgl/renderer/tile_pyramid.hpp8
-rw-r--r--src/mbgl/shaders/background.cpp34
-rw-r--r--src/mbgl/shaders/background.hpp16
-rw-r--r--src/mbgl/shaders/background_pattern.cpp65
-rw-r--r--src/mbgl/shaders/background_pattern.hpp16
-rw-r--r--src/mbgl/shaders/clipping_mask.cpp27
-rw-r--r--src/mbgl/shaders/clipping_mask.hpp16
-rw-r--r--src/mbgl/shaders/collision_box.cpp67
-rw-r--r--src/mbgl/shaders/collision_circle.cpp87
-rw-r--r--src/mbgl/shaders/collision_circle.hpp16
-rw-r--r--src/mbgl/shaders/debug.cpp7
-rw-r--r--src/mbgl/shaders/fill_extrusion.cpp12
-rw-r--r--src/mbgl/shaders/fill_extrusion_pattern.cpp16
-rw-r--r--src/mbgl/shaders/heatmap.cpp128
-rw-r--r--src/mbgl/shaders/heatmap.hpp16
-rw-r--r--src/mbgl/shaders/heatmap_texture.cpp42
-rw-r--r--src/mbgl/shaders/heatmap_texture.hpp16
-rw-r--r--src/mbgl/shaders/hillshade.cpp80
-rw-r--r--src/mbgl/shaders/hillshade.hpp16
-rw-r--r--src/mbgl/shaders/hillshade_prepare.cpp100
-rw-r--r--src/mbgl/shaders/hillshade_prepare.hpp16
-rw-r--r--src/mbgl/shaders/preludes.cpp4
-rw-r--r--src/mbgl/shaders/symbol_icon.cpp24
-rw-r--r--src/mbgl/shaders/symbol_sdf.cpp54
-rw-r--r--src/mbgl/storage/asset_file_source.hpp2
-rw-r--r--src/mbgl/style/conversion/constant.cpp94
-rw-r--r--src/mbgl/style/conversion/coordinate.cpp29
-rw-r--r--src/mbgl/style/conversion/filter.cpp248
-rw-r--r--src/mbgl/style/conversion/geojson.cpp18
-rw-r--r--src/mbgl/style/conversion/geojson_options.cpp85
-rw-r--r--src/mbgl/style/conversion/get_json_type.cpp34
-rw-r--r--src/mbgl/style/conversion/json.hpp2
-rw-r--r--src/mbgl/style/conversion/layer.cpp232
-rw-r--r--src/mbgl/style/conversion/light.cpp115
-rw-r--r--src/mbgl/style/conversion/make_property_setters.hpp237
-rw-r--r--src/mbgl/style/conversion/make_property_setters.hpp.ejs46
-rw-r--r--src/mbgl/style/conversion/position.cpp22
-rw-r--r--src/mbgl/style/conversion/property_setter.hpp (renamed from include/mbgl/style/conversion/property_setter.hpp)15
-rw-r--r--src/mbgl/style/conversion/source.cpp200
-rw-r--r--src/mbgl/style/conversion/tileset.cpp12
-rw-r--r--src/mbgl/style/conversion/transition_options.cpp40
-rw-r--r--src/mbgl/style/custom_tile_loader.cpp116
-rw-r--r--src/mbgl/style/custom_tile_loader.hpp45
-rw-r--r--src/mbgl/style/expression/array_assertion.cpp105
-rw-r--r--src/mbgl/style/expression/assertion.cpp87
-rw-r--r--src/mbgl/style/expression/at.cpp64
-rw-r--r--src/mbgl/style/expression/boolean_operator.cpp95
-rw-r--r--src/mbgl/style/expression/case.cpp104
-rw-r--r--src/mbgl/style/expression/check_subtype.cpp60
-rw-r--r--src/mbgl/style/expression/coalesce.cpp84
-rw-r--r--src/mbgl/style/expression/coercion.cpp161
-rw-r--r--src/mbgl/style/expression/compound_expression.cpp555
-rw-r--r--src/mbgl/style/expression/equals.cpp87
-rw-r--r--src/mbgl/style/expression/find_zoom_curve.cpp76
-rw-r--r--src/mbgl/style/expression/get_covering_stops.cpp26
-rw-r--r--src/mbgl/style/expression/interpolate.cpp245
-rw-r--r--src/mbgl/style/expression/is_constant.cpp40
-rw-r--r--src/mbgl/style/expression/is_expression.cpp29
-rw-r--r--src/mbgl/style/expression/let.cpp115
-rw-r--r--src/mbgl/style/expression/literal.cpp116
-rw-r--r--src/mbgl/style/expression/match.cpp313
-rw-r--r--src/mbgl/style/expression/parsing_context.cpp220
-rw-r--r--src/mbgl/style/expression/step.cpp187
-rw-r--r--src/mbgl/style/expression/util.cpp37
-rw-r--r--src/mbgl/style/expression/util.hpp14
-rw-r--r--src/mbgl/style/expression/value.cpp353
-rw-r--r--src/mbgl/style/function/expression.cpp38
-rw-r--r--src/mbgl/style/layers/heatmap_layer.cpp239
-rw-r--r--src/mbgl/style/layers/heatmap_layer_impl.cpp15
-rw-r--r--src/mbgl/style/layers/heatmap_layer_impl.hpp21
-rw-r--r--src/mbgl/style/layers/heatmap_layer_properties.cpp9
-rw-r--r--src/mbgl/style/layers/heatmap_layer_properties.hpp40
-rw-r--r--src/mbgl/style/layers/hillshade_layer.cpp238
-rw-r--r--src/mbgl/style/layers/hillshade_layer_impl.cpp11
-rw-r--r--src/mbgl/style/layers/hillshade_layer_impl.hpp21
-rw-r--r--src/mbgl/style/layers/hillshade_layer_properties.cpp9
-rw-r--r--src/mbgl/style/layers/hillshade_layer_properties.hpp49
-rw-r--r--src/mbgl/style/layers/layer.cpp.ejs14
-rw-r--r--src/mbgl/style/layers/layer_properties.hpp.ejs1
-rw-r--r--src/mbgl/style/layers/symbol_layer.cpp6
-rw-r--r--src/mbgl/style/layers/symbol_layer_properties.hpp2
-rw-r--r--src/mbgl/style/paint_property.hpp23
-rw-r--r--src/mbgl/style/parser.cpp43
-rw-r--r--src/mbgl/style/rapidjson_conversion.hpp152
-rw-r--r--src/mbgl/style/sources/custom_geometry_source.cpp45
-rw-r--r--src/mbgl/style/sources/custom_geometry_source_impl.cpp40
-rw-r--r--src/mbgl/style/sources/custom_geometry_source_impl.hpp29
-rw-r--r--src/mbgl/style/sources/raster_dem_source.cpp19
-rw-r--r--src/mbgl/style/sources/raster_source.cpp4
-rw-r--r--src/mbgl/style/sources/raster_source_impl.cpp4
-rw-r--r--src/mbgl/style/sources/raster_source_impl.hpp2
-rw-r--r--src/mbgl/style/style_impl.cpp2
-rw-r--r--src/mbgl/style/types.cpp6
-rw-r--r--src/mbgl/text/collision_feature.cpp81
-rw-r--r--src/mbgl/text/collision_feature.hpp48
-rw-r--r--src/mbgl/text/collision_index.cpp364
-rw-r--r--src/mbgl/text/collision_index.hpp70
-rw-r--r--src/mbgl/text/collision_tile.cpp267
-rw-r--r--src/mbgl/text/collision_tile.hpp71
-rw-r--r--src/mbgl/text/cross_tile_symbol_index.cpp189
-rw-r--r--src/mbgl/text/cross_tile_symbol_index.hpp69
-rw-r--r--src/mbgl/text/glyph.hpp9
-rw-r--r--src/mbgl/text/glyph_manager.cpp10
-rw-r--r--src/mbgl/text/glyph_manager.hpp2
-rw-r--r--src/mbgl/text/placement.cpp341
-rw-r--r--src/mbgl/text/placement.hpp90
-rw-r--r--src/mbgl/text/placement_config.hpp33
-rw-r--r--src/mbgl/text/shaping.cpp2
-rw-r--r--src/mbgl/tile/custom_geometry_tile.cpp91
-rw-r--r--src/mbgl/tile/custom_geometry_tile.hpp44
-rw-r--r--src/mbgl/tile/geometry_tile.cpp94
-rw-r--r--src/mbgl/tile/geometry_tile.hpp35
-rw-r--r--src/mbgl/tile/geometry_tile_worker.cpp39
-rw-r--r--src/mbgl/tile/geometry_tile_worker.hpp12
-rw-r--r--src/mbgl/tile/raster_dem_tile.cpp125
-rw-r--r--src/mbgl/tile/raster_dem_tile.hpp105
-rw-r--r--src/mbgl/tile/raster_dem_tile_worker.cpp27
-rw-r--r--src/mbgl/tile/raster_dem_tile_worker.hpp23
-rw-r--r--src/mbgl/tile/raster_tile.cpp3
-rw-r--r--src/mbgl/tile/raster_tile.hpp4
-rw-r--r--src/mbgl/tile/tile.cpp6
-rw-r--r--src/mbgl/tile/tile.hpp33
-rw-r--r--src/mbgl/tile/tile_cache.cpp13
-rw-r--r--src/mbgl/tile/tile_cache.hpp3
-rw-r--r--src/mbgl/util/color.cpp6
-rw-r--r--src/mbgl/util/compression.cpp4
-rw-r--r--src/mbgl/util/grid_index.cpp292
-rw-r--r--src/mbgl/util/grid_index.hpp96
-rw-r--r--src/mbgl/util/http_header.cpp3
-rw-r--r--src/mbgl/util/http_timeout.cpp1
-rw-r--r--src/mbgl/util/mapbox.cpp6
-rw-r--r--src/mbgl/util/mapbox.hpp4
-rw-r--r--src/mbgl/util/offscreen_texture.cpp22
-rw-r--r--src/mbgl/util/offscreen_texture.hpp6
-rw-r--r--src/mbgl/util/tile_cover.cpp8
-rw-r--r--src/mbgl/util/tile_cover.hpp2
-rw-r--r--src/parsedate/parsedate.c18
-rw-r--r--test/api/annotations.test.cpp10
-rw-r--r--test/api/api_misuse.test.cpp2
-rw-r--r--test/api/custom_geometry_source.test.cpp71
-rw-r--r--test/api/custom_layer.test.cpp2
-rw-r--r--test/api/query.test.cpp2
-rw-r--r--test/api/recycle_map.cpp2
-rw-r--r--test/api/zoom_history.cpp2
-rw-r--r--test/fixtures/annotations/debug_sparse/expected.pngbin2933 -> 1387 bytes
-rw-r--r--test/fixtures/api/query_style.json12
-rw-r--r--test/fixtures/custom_geometry_source/grid/expected.pngbin0 -> 8302 bytes
-rw-r--r--test/fixtures/expression_equality/acos.a.json4
-rw-r--r--test/fixtures/expression_equality/acos.b.json4
-rw-r--r--test/fixtures/expression_equality/all.a.json17
-rw-r--r--test/fixtures/expression_equality/all.b.json17
-rw-r--r--test/fixtures/expression_equality/any.a.json17
-rw-r--r--test/fixtures/expression_equality/any.b.json17
-rw-r--r--test/fixtures/expression_equality/array.a.json11
-rw-r--r--test/fixtures/expression_equality/array.b.json11
-rw-r--r--test/fixtures/expression_equality/asin.a.json4
-rw-r--r--test/fixtures/expression_equality/asin.b.json4
-rw-r--r--test/fixtures/expression_equality/at.a.json20
-rw-r--r--test/fixtures/expression_equality/at.b.json20
-rw-r--r--test/fixtures/expression_equality/atan.a.json4
-rw-r--r--test/fixtures/expression_equality/atan.b.json4
-rw-r--r--test/fixtures/expression_equality/boolean.a.json7
-rw-r--r--test/fixtures/expression_equality/boolean.b.json7
-rw-r--r--test/fixtures/expression_equality/case.a.json14
-rw-r--r--test/fixtures/expression_equality/case.b.json14
-rw-r--r--test/fixtures/expression_equality/coalesce.a.json16
-rw-r--r--test/fixtures/expression_equality/coalesce.b.json16
-rw-r--r--test/fixtures/expression_equality/concat.a.json6
-rw-r--r--test/fixtures/expression_equality/concat.b.json6
-rw-r--r--test/fixtures/expression_equality/cos.a.json4
-rw-r--r--test/fixtures/expression_equality/cos.b.json4
-rw-r--r--test/fixtures/expression_equality/divide.a.json5
-rw-r--r--test/fixtures/expression_equality/divide.b.json5
-rw-r--r--test/fixtures/expression_equality/downcase.a.json4
-rw-r--r--test/fixtures/expression_equality/downcase.b.json4
-rw-r--r--test/fixtures/expression_equality/get.a.json7
-rw-r--r--test/fixtures/expression_equality/get.b.json7
-rw-r--r--test/fixtures/expression_equality/has.a.json4
-rw-r--r--test/fixtures/expression_equality/has.b.json4
-rw-r--r--test/fixtures/expression_equality/heatmap-density.a.json23
-rw-r--r--test/fixtures/expression_equality/heatmap-density.b.json23
-rw-r--r--test/fixtures/expression_equality/let.a.json25
-rw-r--r--test/fixtures/expression_equality/let.b.json25
-rw-r--r--test/fixtures/expression_equality/ln.a.json4
-rw-r--r--test/fixtures/expression_equality/ln.b.json6
-rw-r--r--test/fixtures/expression_equality/log10.a.json4
-rw-r--r--test/fixtures/expression_equality/log10.b.json4
-rw-r--r--test/fixtures/expression_equality/log2.a.json4
-rw-r--r--test/fixtures/expression_equality/log2.b.json4
-rw-r--r--test/fixtures/expression_equality/match.a.json12
-rw-r--r--test/fixtures/expression_equality/match.b.json12
-rw-r--r--test/fixtures/expression_equality/max.a.json6
-rw-r--r--test/fixtures/expression_equality/max.b.json6
-rw-r--r--test/fixtures/expression_equality/min.a.json5
-rw-r--r--test/fixtures/expression_equality/min.b.json5
-rw-r--r--test/fixtures/expression_equality/minus.a.json5
-rw-r--r--test/fixtures/expression_equality/minus.b.json5
-rw-r--r--test/fixtures/expression_equality/mod.a.json5
-rw-r--r--test/fixtures/expression_equality/mod.b.json5
-rw-r--r--test/fixtures/expression_equality/not.a.json10
-rw-r--r--test/fixtures/expression_equality/not.b.json10
-rw-r--r--test/fixtures/expression_equality/number.a.json7
-rw-r--r--test/fixtures/expression_equality/number.b.json7
-rw-r--r--test/fixtures/expression_equality/object.a.json7
-rw-r--r--test/fixtures/expression_equality/object.b.json7
-rw-r--r--test/fixtures/expression_equality/plus.a.json7
-rw-r--r--test/fixtures/expression_equality/plus.b.json7
-rw-r--r--test/fixtures/expression_equality/pow.a.json11
-rw-r--r--test/fixtures/expression_equality/pow.b.json11
-rw-r--r--test/fixtures/expression_equality/rgb.a.json6
-rw-r--r--test/fixtures/expression_equality/rgb.b.json6
-rw-r--r--test/fixtures/expression_equality/rgba.a.json7
-rw-r--r--test/fixtures/expression_equality/rgba.b.json7
-rw-r--r--test/fixtures/expression_equality/sin.a.json4
-rw-r--r--test/fixtures/expression_equality/sin.b.json4
-rw-r--r--test/fixtures/expression_equality/sqrt.a.json7
-rw-r--r--test/fixtures/expression_equality/sqrt.b.json7
-rw-r--r--test/fixtures/expression_equality/step.a.json18
-rw-r--r--test/fixtures/expression_equality/step.b.json18
-rw-r--r--test/fixtures/expression_equality/string.a.json7
-rw-r--r--test/fixtures/expression_equality/string.b.json7
-rw-r--r--test/fixtures/expression_equality/tan.a.json4
-rw-r--r--test/fixtures/expression_equality/tan.b.json4
-rw-r--r--test/fixtures/expression_equality/times.a.json7
-rw-r--r--test/fixtures/expression_equality/times.b.json7
-rw-r--r--test/fixtures/expression_equality/to-boolean.a.json7
-rw-r--r--test/fixtures/expression_equality/to-boolean.b.json7
-rw-r--r--test/fixtures/expression_equality/to-color.a.json7
-rw-r--r--test/fixtures/expression_equality/to-color.b.json7
-rw-r--r--test/fixtures/expression_equality/to-number.a.json7
-rw-r--r--test/fixtures/expression_equality/to-number.b.json7
-rw-r--r--test/fixtures/expression_equality/to-string.a.json7
-rw-r--r--test/fixtures/expression_equality/to-string.b.json7
-rw-r--r--test/fixtures/expression_equality/typeof.a.json7
-rw-r--r--test/fixtures/expression_equality/typeof.b.json7
-rw-r--r--test/fixtures/expression_equality/upcase.a.json4
-rw-r--r--test/fixtures/expression_equality/upcase.b.json4
-rw-r--r--test/fixtures/expression_equality/zoom.a.json13
-rw-r--r--test/fixtures/expression_equality/zoom.b.json13
-rw-r--r--test/fixtures/local_glyphs/droid/expected.pngbin0 -> 15747 bytes
-rw-r--r--test/fixtures/local_glyphs/mixed.json196
-rw-r--r--test/fixtures/local_glyphs/no_local/expected.pngbin0 -> 8579 bytes
-rw-r--r--test/fixtures/local_glyphs/ping_fang/expected.pngbin0 -> 15747 bytes
-rw-r--r--test/fixtures/map/offline/expected.pngbin47843 -> 48705 bytes
-rw-r--r--test/fixtures/shared_context/expected.pngbin0 -> 6109 bytes
-rw-r--r--test/fixtures/style_parser/expressions.info.json12
-rw-r--r--test/fixtures/style_parser/expressions.style.json74
-rw-r--r--test/fixtures/style_parser/font_stacks.json3
-rw-r--r--test/fixtures/style_parser/text-font.info.json14
-rw-r--r--test/fixtures/style_parser/text-font.style.json135
-rw-r--r--test/geometry/dem_data.test.cpp149
-rw-r--r--test/gl/bucket.test.cpp27
-rw-r--r--test/gl/context.test.cpp114
-rw-r--r--test/map/map.test.cpp4
-rw-r--r--test/map/prefetch.test.cpp2
-rw-r--r--test/renderer/backend_scope.test.cpp9
-rw-r--r--test/src/app-info.plist2
-rw-r--r--test/src/mbgl/test/conversion_stubs.hpp124
-rw-r--r--test/src/mbgl/test/stub_file_source.cpp16
-rw-r--r--test/src/mbgl/test/stub_file_source.hpp8
-rw-r--r--test/storage/asset_file_source.test.cpp26
-rw-r--r--test/storage/local_file_source.test.cpp26
-rw-r--r--test/storage/offline.test.cpp1
-rw-r--r--test/storage/offline_database.test.cpp46
-rw-r--r--test/style/conversion/function.test.cpp36
-rw-r--r--test/style/conversion/geojson_options.test.cpp39
-rw-r--r--test/style/conversion/layer.test.cpp8
-rw-r--r--test/style/conversion/light.test.cpp7
-rw-r--r--test/style/expression/expression.test.cpp91
-rw-r--r--test/style/expression/util.test.cpp23
-rw-r--r--test/style/filter.test.cpp9
-rw-r--r--test/style/source.test.cpp234
-rw-r--r--test/style/style_parser.test.cpp96
-rw-r--r--test/text/cross_tile_symbol_index.test.cpp203
-rw-r--r--test/text/glyph_loader.test.cpp216
-rw-r--r--test/text/glyph_manager.test.cpp341
-rw-r--r--test/text/local_glyph_rasterizer.test.cpp86
-rw-r--r--test/tile/annotation_tile.test.cpp95
-rw-r--r--test/tile/custom_geometry_tile.test.cpp130
-rw-r--r--test/tile/geojson_tile.test.cpp2
-rw-r--r--test/tile/raster_dem_tile.test.cpp93
-rw-r--r--test/tile/vector_tile.test.cpp5
-rw-r--r--test/util/geo.test.cpp125
-rw-r--r--test/util/grid_index.test.cpp53
-rw-r--r--test/util/mapbox.test.cpp1
-rw-r--r--test/util/memory.test.cpp6
-rw-r--r--test/util/run_loop.test.cpp14
-rw-r--r--test/util/unique_any.test.cpp186
1067 files changed, 47467 insertions, 19129 deletions
diff --git a/.gitignore b/.gitignore
index ce65e4c9b6..0f4fa77858 100644
--- a/.gitignore
+++ b/.gitignore
@@ -34,3 +34,8 @@ xcuserdata
test/fixtures/api/assets.zip
test/fixtures/storage/assets.zip
/.circle-week
+
+# Generated list files from code generation
+/scripts/generate-cmake-files.list
+/scripts/generate-shaders.list
+/scripts/generate-style-code.list \ No newline at end of file
diff --git a/.ycm_extra_conf.py b/.ycm_extra_conf.py
index e395609548..63616a1e98 100644
--- a/.ycm_extra_conf.py
+++ b/.ycm_extra_conf.py
@@ -38,6 +38,8 @@ import ycm_core
compilation_database_folders = [
'build/linux-x86_64/Debug',
+ 'build/qt-linux-x86_64/Debug',
+ 'build/qt4-linux-x86_64/Debug',
'build/macos/compdb/Debug',
]
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4badb29f36..181c91df0f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,20 +2,25 @@ cmake_minimum_required(VERSION 3.4)
project(mbgl LANGUAGES CXX C)
set(CMAKE_CXX_STANDARD 14)
-include(cmake/mbgl.cmake)
-include(cmake/mason.cmake)
-include(cmake/xcode.cmake)
-
option(WITH_CXX11ABI "Use cxx11abi mason packages" OFF)
option(WITH_COVERAGE "Enable coverage reports" OFF)
option(WITH_OSMESA "Use OSMesa headless backend" OFF)
option(WITH_EGL "Use EGL backend" OFF)
+option(WITH_NODEJS "Download test dependencies like NPM and Node.js" ON)
+
+include(cmake/mbgl.cmake)
+include(cmake/mason.cmake)
+include(cmake/xcode.cmake)
if(WITH_CXX11ABI)
set(MASON_CXXABI_SUFFIX -cxx11abi)
- add_definitions(-D_GLIBCXX_USE_CXX11_ABI=1)
+ if(CMAKE_COMPILER_IS_GNUCXX)
+ add_definitions(-D_GLIBCXX_USE_CXX11_ABI=1)
+ endif()
else()
- add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0)
+ if(CMAKE_COMPILER_IS_GNUCXX)
+ add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0)
+ endif()
endif()
if(WITH_OSMESA AND WITH_EGL)
@@ -44,10 +49,9 @@ set_source_files_properties(src/mbgl/util/version.cpp PROPERTIES COMPILE_DEFINIT
mason_use(geometry VERSION 0.9.2 HEADER_ONLY)
mason_use(variant VERSION 1.1.4 HEADER_ONLY)
-mason_use(any VERSION 8fef1e9 HEADER_ONLY)
mason_use(unique_resource VERSION cba309e HEADER_ONLY)
mason_use(rapidjson VERSION 1.1.0 HEADER_ONLY)
-mason_use(boost VERSION 1.62.0 HEADER_ONLY)
+mason_use(boost VERSION 1.65.1 HEADER_ONLY)
mason_use(geojsonvt VERSION 6.3.0 HEADER_ONLY)
mason_use(supercluster VERSION 0.2.2 HEADER_ONLY)
mason_use(kdbush VERSION 0.1.1-1 HEADER_ONLY)
@@ -70,14 +74,15 @@ endif(WITH_COVERAGE)
set(CMAKE_CONFIGURATION_TYPES Debug Release RelWithDebugInfo Sanitize)
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -ftemplate-depth=1024 -Wall -Wextra -Wshadow -Wnon-virtual-dtor -Werror -Wno-variadic-macros -Wno-unknown-pragmas")
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -ftemplate-depth=1024 -fPIC -fvisibility=hidden -Wall -Wextra -Wshadow -Wnon-virtual-dtor -Werror -Wno-variadic-macros -Wno-unknown-pragmas")
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC -fvisibility=hidden -Wall -Wextra -Wshadow -Werror -Wno-variadic-macros -Wno-unknown-pragmas")
if(APPLE AND CMAKE_CXX_COMPILER_ID MATCHES ".*Clang")
# -Wno-error=unused-command-line-argument is required due to https://llvm.org/bugs/show_bug.cgi?id=7798
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=unused-command-line-argument")
endif()
-set(CMAKE_CXX_FLAGS_RELEASE "-Os -DNDEBUG")
-set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -DNDEBUG")
-set(CMAKE_CXX_FLAGS_SANITIZE "-O1 -g -fno-omit-frame-pointer -fno-optimize-sibling-calls")
+set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Os -DNDEBUG")
+set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -O3 -DNDEBUG")
+set(CMAKE_CXX_FLAGS_SANITIZE "${CMAKE_CXX_FLAGS_SANITIZE} -O1 -g -fno-omit-frame-pointer -fno-optimize-sibling-calls")
if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang")
diff --git a/INSTALL.md b/INSTALL.md
index fb619770a1..a28348d15c 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -7,50 +7,38 @@ that you can download instantly and get started with fast](https://www.mapbox.co
Still with us? These are the instructions you'll need to build Mapbox GL Native
from source on a variety of platforms and set up a development environment.
-Your journey will start with getting the source code, then installing the
-dependencies, and then setting up a development environment, which varies
-depending on your operating system and what platform you want to develop for.
+Your journey will start with installing dependencies, then getting the source code, and
+then setting up a development environment, which varies depending on your
+operating system and what platform you want to develop for.
-## 1: Getting the source
+## 1: Installing dependencies
-Clone the git repository:
+### macOS
- git clone https://github.com/mapbox/mapbox-gl-native.git
- cd mapbox-gl-native
+ 1. Install [Xcode](https://developer.apple.com/xcode/)
+ 2. Launch Xcode and install any updates
+ 3. Install [Homebrew](http://brew.sh)
+ 4. Install [Node.js](https://nodejs.org/), [CMake](https://cmake.org/), and [ccache](https://ccache.samba.org) with `brew install nodejs cmake ccache`
+ 5. Install [xcpretty](https://github.com/supermarin/xcpretty) with `[sudo] gem install xcpretty` (optional, used for prettifying command line builds)
-## 2: Installing dependencies
+### Linux
-These dependencies are required for all operating systems and all platform
-targets.
-
- - Modern C++ compiler that supports `-std=c++14`\*
- - clang++ 3.5 or later _or_
- - g++-4.9 or later
- - [CMake](https://cmake.org/) 3.1 or later (for build only)
- - [cURL](https://curl.haxx.se) (for build only)
- - [Node.js](https://nodejs.org/) 4.2.1 or later (for build only)
- - [`pkg-config`](https://wiki.freedesktop.org/www/Software/pkg-config/) (for build only)
-
-**Note**: We partially support C++14 because GCC 4.9 does not fully implement the
-final draft of the C++14 standard. More information in [DEVELOPING.md](DEVELOPING.md).
-
-Depending on your operating system and target, you'll need additional
-dependencies:
-
-### Additional dependencies for Linux
+Install the following:
+ - `clang++` 3.5 or later or `g++` 4.9 or later
+ - [git](https://git-scm.com/)
+ - [CMake](https://cmake.org/) 3.1 or later
+ - [cURL](https://curl.haxx.se)
+ - [Node.js](https://nodejs.org/) 4.2.1 or later
- [`libcurl`](http://curl.haxx.se/libcurl/) (depends on OpenSSL)
+ - [ccache](https://ccache.samba.org) (optional, improves recompilation performance)
-### Additional dependencies for macOS
-
- - Apple Command Line Tools (available at [Apple Developer](https://developer.apple.com/download/more/))
- - [Homebrew](http://brew.sh)
- - [Cask](http://caskroom.io/) (if building for Android)
- - [xcpretty](https://github.com/supermarin/xcpretty) (`gem install xcpretty`)
+## 2: Getting the source
-### Optional dependencies
+ Clone the git repository:
-- [ccache](https://ccache.samba.org) (for build only; improves recompilation performance)
+ git clone https://github.com/mapbox/mapbox-gl-native.git
+ cd mapbox-gl-native
## 3: Setting up a development environment & building
@@ -58,7 +46,7 @@ See the relevant SDK documentation for next steps:
* [Maps SDK for Android](platform/android/)
* [Maps SDK for iOS](platform/ios/)
-* [Maps SDK for iOS](platform/macos/)
+* [Maps SDK for macOS](platform/macos/)
* [Mapbox Qt SDK](platform/qt/)
* [Mapbox GL Native on Linux](platform/linux/)
* [node-mapbox-gl-native](platform/node/)
diff --git a/LICENSE.md b/LICENSE.md
index d12e00b0e5..0b77be538e 100644
--- a/LICENSE.md
+++ b/LICENSE.md
@@ -1,4 +1,4 @@
-mapbox-gl-native copyright (c) 2014-2017 Mapbox.
+mapbox-gl-native copyright (c) 2014-2018 Mapbox.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
diff --git a/Makefile b/Makefile
index fbb4cd74f5..862201d8d1 100644
--- a/Makefile
+++ b/Makefile
@@ -142,6 +142,10 @@ node: $(MACOS_PROJ_PATH)
macos-test: $(MACOS_PROJ_PATH)
set -o pipefail && $(MACOS_XCODEBUILD) -scheme 'CI' test $(XCPRETTY)
+.PHONY: macos-lint
+macos-lint:
+ find platform/macos -type f -name '*.plist' | xargs plutil -lint
+
.PHONY: xpackage
xpackage: $(MACOS_PROJ_PATH)
SYMBOLS=$(SYMBOLS) ./platform/macos/scripts/package.sh
@@ -218,10 +222,19 @@ ios: $(IOS_PROJ_PATH)
iproj: $(IOS_PROJ_PATH)
open $(IOS_WORK_PATH)
+.PHONY: ios-lint
+ios-lint:
+ find platform/ios/framework -type f -name '*.plist' | xargs plutil -lint
+ find platform/ios/app -type f -name '*.plist' | xargs plutil -lint
+
.PHONY: ios-test
ios-test: $(IOS_PROJ_PATH)
set -o pipefail && $(IOS_XCODEBUILD_SIM) -scheme 'CI' test $(XCPRETTY)
+.PHONY: ios-integration-test
+ios-integration-test: $(IOS_PROJ_PATH)
+ set -o pipefail && $(IOS_XCODEBUILD_SIM) -scheme 'Integration Test Harness' test $(XCPRETTY)
+
.PHONY: ios-sanitize-address
ios-sanitize-address: $(IOS_PROJ_PATH)
set -o pipefail && $(IOS_XCODEBUILD_SIM) -scheme 'CI' -enableAddressSanitizer YES test $(XCPRETTY)
@@ -296,7 +309,7 @@ linux: glfw-app render offline
.PHONY: linux-core
linux-core: $(LINUX_BUILD)
- $(NINJA) $(NINJA_ARGS) -j$(JOBS) -C $(LINUX_OUTPUT_PATH) mbgl-core mbgl-loop-uv
+ $(NINJA) $(NINJA_ARGS) -j$(JOBS) -C $(LINUX_OUTPUT_PATH) mbgl-core mbgl-loop-uv mbgl-filesource
.PHONY: test
test: $(LINUX_BUILD)
@@ -481,7 +494,7 @@ android-style-code:
style-code: android-style-code
# Configuration file for running CMake from Gradle within Android Studio.
-platform/android/configuration.gradle:
+platform/android/gradle/configuration.gradle:
@echo "ext {\n node = '`command -v node || command -v nodejs`'\n npm = '`command -v npm`'\n ccache = '`command -v ccache`'\n}" > $@
define ANDROID_RULES
@@ -489,17 +502,17 @@ define ANDROID_RULES
# $2 = armeabi-v7a (internal arch)
.PHONY: android-test-lib-$1
-android-test-lib-$1: platform/android/configuration.gradle
+android-test-lib-$1: platform/android/gradle/configuration.gradle
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=$2 -Pmapbox.with_test=true :MapboxGLAndroidSDKTestApp:assemble$(BUILDTYPE)
# Build SDK for for specified abi
.PHONY: android-lib-$1
-android-lib-$1: platform/android/configuration.gradle
+android-lib-$1: platform/android/gradle/configuration.gradle
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=$2 :MapboxGLAndroidSDK:assemble$(BUILDTYPE)
# Build test app and SDK for for specified abi
.PHONY: android-$1
-android-$1: platform/android/configuration.gradle
+android-$1: platform/android/gradle/configuration.gradle
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=$2 :MapboxGLAndroidSDKTestApp:assemble$(BUILDTYPE)
# Build the core test for specified abi
@@ -541,29 +554,29 @@ run-android-core-test-$1: run-android-core-test-$1-*
# Run the test app on connected android device with specified abi
.PHONY: run-android-$1
-run-android-$1: platform/android/configuration.gradle
+run-android-$1: platform/android/gradle/configuration.gradle
-adb uninstall com.mapbox.mapboxsdk.testapp 2> /dev/null
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=$2 :MapboxGLAndroidSDKTestApp:install$(BUILDTYPE) && adb shell am start -n com.mapbox.mapboxsdk.testapp/.activity.FeatureOverviewActivity
# Build test app instrumentation tests apk and test app apk for specified abi
.PHONY: android-ui-test-$1
-android-ui-test-$1: platform/android/configuration.gradle
+android-ui-test-$1: platform/android/gradle/configuration.gradle
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=$2 :MapboxGLAndroidSDKTestApp:assembleDebug :MapboxGLAndroidSDKTestApp:assembleAndroidTest
# Run test app instrumentation tests on a connected android device or emulator with specified abi
.PHONY: run-android-ui-test-$1
-run-android-ui-test-$1: platform/android/configuration.gradle
+run-android-ui-test-$1: platform/android/gradle/configuration.gradle
-adb uninstall com.mapbox.mapboxsdk.testapp 2> /dev/null
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=$2 :MapboxGLAndroidSDKTestApp:connectedAndroidTest
# Run Java Instrumentation tests on a connected android device or emulator with specified abi and test filter
-run-android-ui-test-$1-%: platform/android/configuration.gradle
+run-android-ui-test-$1-%: platform/android/gradle/configuration.gradle
-adb uninstall com.mapbox.mapboxsdk.testapp 2> /dev/null
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=$2 :MapboxGLAndroidSDKTestApp:connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.class="$$*"
# Symbolicate native stack trace with the specified abi
.PHONY: android-ndk-stack-$1
-android-ndk-stack-$1: platform/android/configuration.gradle
+android-ndk-stack-$1: platform/android/gradle/configuration.gradle
adb logcat | ndk-stack -sym platform/android/MapboxGLAndroidSDK/build/intermediates/cmake/debug/obj/$2/
endef
@@ -594,30 +607,30 @@ run-android-ui-test-%: run-android-ui-test-arm-v7-%
# Run Java Unit tests on the JVM of the development machine executing this
.PHONY: run-android-unit-test
-run-android-unit-test: platform/android/configuration.gradle
+run-android-unit-test: platform/android/gradle/configuration.gradle
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=none :MapboxGLAndroidSDK:testDebugUnitTest
-run-android-unit-test-%: platform/android/configuration.gradle
+run-android-unit-test-%: platform/android/gradle/configuration.gradle
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=none :MapboxGLAndroidSDK:testDebugUnitTest --tests "$*"
# Builds a release package of the Android SDK
.PHONY: apackage
-apackage: platform/android/configuration.gradle
+apackage: platform/android/gradle/configuration.gradle
make android-lib-arm-v5 && make android-lib-arm-v7 && make android-lib-arm-v8 && make android-lib-x86 && make android-lib-x86-64 && make android-lib-mips
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=all assemble$(BUILDTYPE)
# Build test app instrumentation tests apk and test app apk for all abi's
.PHONY: android-ui-test
-android-ui-test: platform/android/configuration.gradle
+android-ui-test: platform/android/gradle/configuration.gradle
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=all :MapboxGLAndroidSDKTestApp:assembleDebug :MapboxGLAndroidSDKTestApp:assembleAndroidTest
# Uploads the compiled Android SDK to Maven
.PHONY: run-android-upload-archives
-run-android-upload-archives: platform/android/configuration.gradle
+run-android-upload-archives: platform/android/gradle/configuration.gradle
cd platform/android && export IS_LOCAL_DEVELOPMENT=false && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=all :MapboxGLAndroidSDK:uploadArchives
# Uploads the compiled Android SDK to ~/.m2/repository/com/mapbox/mapboxsdk
.PHONY: run-android-upload-archives-local
-run-android-upload-archives-local: platform/android/configuration.gradle
+run-android-upload-archives-local: platform/android/gradle/configuration.gradle
cd platform/android && export IS_LOCAL_DEVELOPMENT=true && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=all :MapboxGLAndroidSDK:uploadArchives
# Dump system graphics information for the test app
@@ -636,22 +649,22 @@ android-check : android-checkstyle android-lint-sdk android-lint-test-app
# Runs checkstyle on the Android code
.PHONY: android-checkstyle
-android-checkstyle: platform/android/configuration.gradle
+android-checkstyle: platform/android/gradle/configuration.gradle
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=none checkstyle
# Runs lint on the Android SDK code
.PHONY: android-lint-sdk
-android-lint-sdk: platform/android/configuration.gradle
+android-lint-sdk: platform/android/gradle/configuration.gradle
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=none :MapboxGLAndroidSDK:lint
# Runs lint on the Android test app code
.PHONY: android-lint-test-app
-android-lint-test-app: platform/android/configuration.gradle
+android-lint-test-app: platform/android/gradle/configuration.gradle
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=none :MapboxGLAndroidSDKTestApp:lint
# Generates javadoc from the Android SDK
.PHONY: android-javadoc
-android-javadoc: platform/android/configuration.gradle
+android-javadoc: platform/android/gradle/configuration.gradle
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=none :MapboxGLAndroidSDK:javadocrelease
# Symbolicate ndk stack traces for the arm-v7 abi
@@ -661,14 +674,14 @@ android-ndk-stack: android-ndk-stack-arm-v7
# Open Android Studio if machine is macos
ifeq ($(HOST_PLATFORM), macos)
.PHONY: aproj
-aproj: platform/android/configuration.gradle
+aproj: platform/android/gradle/configuration.gradle
open -b com.google.android.studio platform/android
endif
# Creates the configuration needed to build with Android Studio
.PHONY: android-configuration
-android-configuration: platform/android/configuration.gradle
- cat platform/android/configuration.gradle
+android-configuration: platform/android/gradle/configuration.gradle
+ cat platform/android/gradle/configuration.gradle
#### Miscellaneous targets #####################################################
@@ -684,7 +697,7 @@ codestyle:
.PHONY: clean
clean:
-rm -rf ./build \
- ./platform/android/configuration.gradle \
+ ./platform/android/gradle/configuration.gradle \
./platform/android/MapboxGLAndroidSDK/build \
./platform/android/MapboxGLAndroidSDK/.externalNativeBuild \
./platform/android/MapboxGLAndroidSDKTestApp/build \
diff --git a/README.md b/README.md
index c66d2216a4..32bfdf3b51 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@ This repository hosts the cross-platform Mapbox GL Native library, plus convenie
| [Mapbox Maps SDK for iOS](platform/ios/) | Objective-C or Swift | [![Bitrise](https://www.bitrise.io/app/7514e4cf3da2cc57.svg?token=OwqZE5rSBR9MVWNr_lf4sA&branch=master)](https://www.bitrise.io/app/7514e4cf3da2cc57) |
| [Mapbox Maps SDK for macOS](platform/macos/) | Objective-C, Swift, or AppleScript | [![Bitrise](https://www.bitrise.io/app/155ef7da24b38dcd.svg?token=4KSOw_gd6WxTnvGE2rMttg&branch=master)](https://www.bitrise.io/app/155ef7da24b38dcd) |
| [node-mapbox-gl-native](platform/node/) | Node.js | [![Circle CI build status](https://circleci.com/gh/mapbox/mapbox-gl-native.svg?style=shield)](https://circleci.com/gh/mapbox/workflows/mapbox-gl-native/tree/master) |
-| [Mapbox Qt SDK](platform/qt) | C++03 | [![Circle CI build status](https://circleci.com/gh/mapbox/mapbox-gl-native.svg?style=shield)](https://circleci.com/gh/mapbox/workflows/mapbox-gl-native/tree/master) |
+| [Mapbox Qt SDK](platform/qt) | C++03 | [![Circle CI build status](https://circleci.com/gh/mapbox/mapbox-gl-native.svg?style=shield)](https://circleci.com/gh/mapbox/workflows/mapbox-gl-native/tree/master) [![AppVeyor CI build status](https://ci.appveyor.com/api/projects/status/3q12kbcooc6df8uc?svg=true)](https://ci.appveyor.com/project/Mapbox/mapbox-gl-native) |
Additional Mapbox GL Native–based libraries for **hybrid applications** are developed outside of this repository:
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644
index 0000000000..d31160261f
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,56 @@
+os: Visual Studio 2015
+platform: x64
+
+environment:
+ matrix:
+ - configuration: Release
+
+shallow_clone: true
+
+cache:
+ - '%APPVEYOR_BUILD_FOLDER%\mason_packages'
+ - '%APPVEYOR_BUILD_FOLDER%\LLVM-5.0.1-win64.exe'
+ - '%APPVEYOR_BUILD_FOLDER%\cmake-3.10.1-win64-x64.zip'
+
+install:
+ - ps: |
+ if (!(Test-Path LLVM-5.0.1-win64.exe)) {
+ appveyor DownloadFile https://releases.llvm.org/5.0.1/LLVM-5.0.1-win64.exe
+ }
+ scripts\check-sha256.ps1 LLVM-5.0.1-win64.exe 981543611D719624ACB29A2CFFD6A479CFF36E8AB5EE8A57D8ECA4F9C4C6956F
+ Start-Process -FilePath 'LLVM-5.0.1-win64.exe' -ArgumentList '/S','/D=C:\LLVM-5.0.1' -Wait
+ - ps: |
+ if (!(Test-Path cmake-3.10.1-win64-x64.zip)) {
+ appveyor DownloadFile https://cmake.org/files/v3.10/cmake-3.10.1-win64-x64.zip
+ }
+ scripts\check-sha256.ps1 cmake-3.10.1-win64-x64.zip 8251F70C85B58F3CA1F24E4A3B0637E2D609B5E4A341D00B70E02E89244D5029
+ Start-Process -FilePath '7z' -ArgumentList 'x','cmake-3.10.1-win64-x64.zip','-oC:\' -Wait
+
+before_build:
+ - set PATH=C:\LLVM-5.0.1\bin;%PATH%
+ - set PATH=C:\cmake-3.10.1-win64-x64\bin;%PATH%
+ - set CC=clang-cl
+ - set CXX=clang-cl
+ - mkdir %APPVEYOR_BUILD_FOLDER%\build
+ - cd %APPVEYOR_BUILD_FOLDER%\build
+
+build_script:
+ - call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64
+ - cmake -G "Ninja" -DMBGL_PLATFORM=qt -DWITH_QT_DECODERS=ON -DWITH_QT_I18N=ON -DWITH_NODEJS=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=C:\Qt\5.7\msvc2015_64\lib\cmake -DCMAKE_MAKE_PROGRAM="%APPVEYOR_BUILD_FOLDER%\platform\qt\ninja.exe" ..
+ - cmake --build . -- -j %NUMBER_OF_PROCESSORS%
+
+after_build:
+ - mkdir qmapboxgl
+ - mkdir qmapboxgl\lib
+ - mkdir qmapboxgl\include
+ - copy qmapboxgl.dll qmapboxgl\lib
+ - copy qmapboxgl.exp qmapboxgl\lib
+ - copy qmapboxgl.lib qmapboxgl\lib
+ - copy %APPVEYOR_BUILD_FOLDER%\platform\qt\include\* qmapboxgl\include
+ - 7z a qmapboxgl-%APPVEYOR_REPO_COMMIT%.zip qmapboxgl
+
+artifacts:
+ - path: build\qmapboxgl-%APPVEYOR_REPO_COMMIT%.zip
+ name: QMapboxGL
+
+test: off
diff --git a/benchmark/api/query.benchmark.cpp b/benchmark/api/query.benchmark.cpp
index 75695c1ad7..7694585526 100644
--- a/benchmark/api/query.benchmark.cpp
+++ b/benchmark/api/query.benchmark.cpp
@@ -34,7 +34,7 @@ public:
DefaultFileSource fileSource{ "benchmark/fixtures/api/cache.db", "." };
ThreadPool threadPool{ 4 };
HeadlessFrontend frontend { { 1000, 1000 }, 1, fileSource, threadPool };
- Map map { frontend, MapObserver::nullObserver(), frontend.getSize(), 1, fileSource, threadPool, MapMode::Still };
+ Map map { frontend, MapObserver::nullObserver(), frontend.getSize(), 1, fileSource, threadPool, MapMode::Static};
ScreenBox box{{ 0, 0 }, { 1000, 1000 }};
};
diff --git a/benchmark/api/render.benchmark.cpp b/benchmark/api/render.benchmark.cpp
index 9f1ea68e0b..a1b557777f 100644
--- a/benchmark/api/render.benchmark.cpp
+++ b/benchmark/api/render.benchmark.cpp
@@ -41,7 +41,7 @@ static void prepare(Map& map, optional<std::string> json = {}) {
static void API_renderStill_reuse_map(::benchmark::State& state) {
RenderBenchmark bench;
HeadlessFrontend frontend { { 1000, 1000 }, 1, bench.fileSource, bench.threadPool };
- Map map { frontend, MapObserver::nullObserver(), frontend.getSize(), 1, bench.fileSource, bench.threadPool, MapMode::Still };
+ Map map { frontend, MapObserver::nullObserver(), frontend.getSize(), 1, bench.fileSource, bench.threadPool, MapMode::Static};
prepare(map);
while (state.KeepRunning()) {
@@ -52,7 +52,7 @@ static void API_renderStill_reuse_map(::benchmark::State& state) {
static void API_renderStill_reuse_map_switch_styles(::benchmark::State& state) {
RenderBenchmark bench;
HeadlessFrontend frontend { { 1000, 1000 }, 1, bench.fileSource, bench.threadPool };
- Map map { frontend, MapObserver::nullObserver(), frontend.getSize(), 1, bench.fileSource, bench.threadPool, MapMode::Still };
+ Map map { frontend, MapObserver::nullObserver(), frontend.getSize(), 1, bench.fileSource, bench.threadPool, MapMode::Static};
while (state.KeepRunning()) {
prepare(map, { "{}" });
@@ -67,7 +67,7 @@ static void API_renderStill_recreate_map(::benchmark::State& state) {
while (state.KeepRunning()) {
HeadlessFrontend frontend { { 1000, 1000 }, 1, bench.fileSource, bench.threadPool };
- Map map { frontend, MapObserver::nullObserver(), frontend.getSize(), 1, bench.fileSource, bench.threadPool, MapMode::Still };
+ Map map { frontend, MapObserver::nullObserver(), frontend.getSize(), 1, bench.fileSource, bench.threadPool, MapMode::Static};
prepare(map);
frontend.render(map);
}
diff --git a/benchmark/function/camera_function.benchmark.cpp b/benchmark/function/camera_function.benchmark.cpp
index 1f8fe4579f..26de5701db 100644
--- a/benchmark/function/camera_function.benchmark.cpp
+++ b/benchmark/function/camera_function.benchmark.cpp
@@ -1,20 +1,14 @@
#include <benchmark/benchmark.h>
#include <mbgl/style/function/source_function.hpp>
-
-#include <mbgl/style/rapidjson_conversion.hpp>
#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/json.hpp>
#include <mbgl/style/conversion/function.hpp>
-#include <rapidjson/document.h>
-
-
using namespace mbgl;
using namespace mbgl::style;
-static rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> createFunctionJSON(size_t stopCount) {
- rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> doc;
-
+static std::string createFunctionJSON(size_t stopCount) {
std::string stops = "[";
for (size_t i = 0; i < stopCount; i++) {
std::string value = std::to_string(24.0f / stopCount * i);
@@ -22,9 +16,7 @@ static rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> cr
stops += "[" + value + ", " + value + "]";
}
stops += "]";
-
- doc.Parse<0>(R"({"type": "exponential", "base": 2, "stops": )" + stops + "}");
- return doc;
+ return R"({"type": "exponential", "base": 2, "stops": )" + stops + "}";
}
static void Parse_CameraFunction(benchmark::State& state) {
@@ -35,7 +27,7 @@ static void Parse_CameraFunction(benchmark::State& state) {
state.PauseTiming();
auto doc = createFunctionJSON(stopCount);
state.ResumeTiming();
- optional<CameraFunction<float>> result = conversion::convert<CameraFunction<float>, JSValue>(doc, error);
+ optional<CameraFunction<float>> result = conversion::convertJSON<CameraFunction<float>>(doc, error);
if (!result) {
state.SkipWithError(error.message.c_str());
}
@@ -47,7 +39,7 @@ static void Evaluate_CameraFunction(benchmark::State& state) {
size_t stopCount = state.range(0);
auto doc = createFunctionJSON(stopCount);
conversion::Error error;
- optional<CameraFunction<float>> function = conversion::convert<CameraFunction<float>, JSValue>(doc, error);
+ optional<CameraFunction<float>> function = conversion::convertJSON<CameraFunction<float>>(doc, error);
if (!function) {
state.SkipWithError(error.message.c_str());
}
diff --git a/benchmark/function/composite_function.benchmark.cpp b/benchmark/function/composite_function.benchmark.cpp
index f04b6c7073..e2545e6349 100644
--- a/benchmark/function/composite_function.benchmark.cpp
+++ b/benchmark/function/composite_function.benchmark.cpp
@@ -5,19 +5,14 @@
#include <mbgl/style/function/composite_exponential_stops.hpp>
#include <mbgl/style/function/composite_function.hpp>
-#include <mbgl/style/rapidjson_conversion.hpp>
#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/json.hpp>
#include <mbgl/style/conversion/function.hpp>
-#include <rapidjson/document.h>
-
-
using namespace mbgl;
using namespace mbgl::style;
-static rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> createFunctionJSON(size_t stopCount) {
- rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> doc;
-
+static std::string createFunctionJSON(size_t stopCount) {
std::string stops = "[";
for (size_t outerStop = 0; outerStop < stopCount; outerStop++) {
for (size_t innerStop = 0; innerStop < stopCount; innerStop++) {
@@ -29,9 +24,7 @@ static rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> cr
}
}
stops += "]";
-
- doc.Parse<0>(R"({"type": "exponential", "base": 2, "stops": )" + stops + R"(, "property": "x"})");
- return doc;
+ return R"({"type": "exponential", "base": 2, "stops": )" + stops + R"(, "property": "x"})";
}
static void Parse_CompositeFunction(benchmark::State& state) {
@@ -42,7 +35,7 @@ static void Parse_CompositeFunction(benchmark::State& state) {
state.PauseTiming();
auto doc = createFunctionJSON(stopCount);
state.ResumeTiming();
- optional<CompositeFunction<float>> result = conversion::convert<style::CompositeFunction<float>, JSValue>(doc, error);
+ optional<CompositeFunction<float>> result = conversion::convertJSON<style::CompositeFunction<float>>(doc, error);
if (!result) {
state.SkipWithError(error.message.c_str());
}
@@ -54,7 +47,7 @@ static void Evaluate_CompositeFunction(benchmark::State& state) {
size_t stopCount = state.range(0);
auto doc = createFunctionJSON(stopCount);
conversion::Error error;
- optional<CompositeFunction<float>> function = conversion::convert<CompositeFunction<float>, JSValue>(doc, error);
+ optional<CompositeFunction<float>> function = conversion::convertJSON<CompositeFunction<float>>(doc, error);
if (!function) {
state.SkipWithError(error.message.c_str());
}
diff --git a/benchmark/function/source_function.benchmark.cpp b/benchmark/function/source_function.benchmark.cpp
index 14e729eee2..af361943e3 100644
--- a/benchmark/function/source_function.benchmark.cpp
+++ b/benchmark/function/source_function.benchmark.cpp
@@ -4,19 +4,14 @@
#include <mbgl/style/function/source_function.hpp>
-#include <mbgl/style/rapidjson_conversion.hpp>
#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/json.hpp>
#include <mbgl/style/conversion/function.hpp>
-#include <rapidjson/document.h>
-
-
using namespace mbgl;
using namespace mbgl::style;
-static rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> createFunctionJSON(size_t stopCount) {
- rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> doc;
-
+static std::string createFunctionJSON(size_t stopCount) {
std::string stops = "[";
for (size_t i = 0; i < stopCount; i++) {
std::string value = std::to_string(100.0f / stopCount * i);
@@ -24,9 +19,7 @@ static rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> cr
stops += "[" + value + ", " + value + "]";
}
stops += "]";
-
- doc.Parse<0>(R"({"type": "exponential", "base": 2, "stops": )" + stops + R"(, "property": "x"})");
- return doc;
+ return R"({"type": "exponential", "base": 2, "stops": )" + stops + R"(, "property": "x"})";
}
static void Parse_SourceFunction(benchmark::State& state) {
@@ -37,7 +30,7 @@ static void Parse_SourceFunction(benchmark::State& state) {
state.PauseTiming();
auto doc = createFunctionJSON(stopCount);
state.ResumeTiming();
- optional<SourceFunction<float>> result = conversion::convert<SourceFunction<float>, JSValue>(doc, error);
+ optional<SourceFunction<float>> result = conversion::convertJSON<SourceFunction<float>>(doc, error);
if (!result) {
state.SkipWithError(error.message.c_str());
}
@@ -49,7 +42,7 @@ static void Evaluate_SourceFunction(benchmark::State& state) {
size_t stopCount = state.range(0);
auto doc = createFunctionJSON(stopCount);
conversion::Error error;
- optional<SourceFunction<float>> function = conversion::convert<SourceFunction<float>, JSValue>(doc, error);
+ optional<SourceFunction<float>> function = conversion::convertJSON<SourceFunction<float>>(doc, error);
if (!function) {
state.SkipWithError(error.message.c_str());
}
diff --git a/benchmark/parse/filter.benchmark.cpp b/benchmark/parse/filter.benchmark.cpp
index d650cb72c9..4984668400 100644
--- a/benchmark/parse/filter.benchmark.cpp
+++ b/benchmark/parse/filter.benchmark.cpp
@@ -2,20 +2,16 @@
#include <mbgl/style/filter.hpp>
#include <mbgl/style/filter_evaluator.hpp>
-#include <mbgl/style/rapidjson_conversion.hpp>
#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/json.hpp>
#include <mbgl/style/conversion/filter.hpp>
#include <mbgl/tile/geometry_tile_data.hpp>
-#include <rapidjson/document.h>
-
using namespace mbgl;
style::Filter parse(const char* expression) {
- rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> doc;
- doc.Parse<0>(expression);
style::conversion::Error error;
- return *style::conversion::convert<style::Filter, JSValue>(doc, error);
+ return *style::conversion::convertJSON<style::Filter>(expression, error);
}
static void Parse_Filter(benchmark::State& state) {
diff --git a/bin/offline.cpp b/bin/offline.cpp
index ec2fb09096..5b403a6416 100644
--- a/bin/offline.cpp
+++ b/bin/offline.cpp
@@ -4,53 +4,63 @@
#include <mbgl/storage/default_file_source.hpp>
+#include <args/args.hxx>
+
#include <cstdlib>
#include <iostream>
#include <csignal>
#include <atomic>
+#include <sstream>
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunknown-pragmas"
-#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
-#pragma GCC diagnostic ignored "-Wshadow"
-#include <boost/program_options.hpp>
-#pragma GCC diagnostic pop
-
-namespace po = boost::program_options;
using namespace std::literals::chrono_literals;
int main(int argc, char *argv[]) {
- std::string style = mbgl::util::default_styles::streets.url;
- double north = 37.2, west = -122.8, south = 38.1, east = -121.7; // Bay area
- double minZoom = 0.0, maxZoom = 15.0, pixelRatio = 1.0;
- std::string output = "offline.db";
+ args::ArgumentParser argumentParser("Mapbox GL offline tool");
+ args::HelpFlag helpFlag(argumentParser, "help", "Display this help menu", {'h', "help"});
- const char* tokenEnv = getenv("MAPBOX_ACCESS_TOKEN");
- std::string token = tokenEnv ? tokenEnv : std::string();
-
- po::options_description desc("Allowed options");
- desc.add_options()
- ("style,s", po::value(&style)->value_name("URL"), "Map stylesheet")
- ("north", po::value(&north)->value_name("degrees")->default_value(north), "North latitude")
- ("west", po::value(&west)->value_name("degrees")->default_value(west), "West longitude")
- ("south", po::value(&south)->value_name("degrees")->default_value(south), "South latitude")
- ("east", po::value(&east)->value_name("degrees")->default_value(east), "East longitude")
- ("minZoom", po::value(&minZoom)->value_name("number")->default_value(minZoom), "Min zoom level")
- ("maxZoom", po::value(&maxZoom)->value_name("number")->default_value(maxZoom), "Max zoom level")
- ("pixelRatio", po::value(&pixelRatio)->value_name("number")->default_value(pixelRatio), "Pixel ratio")
- ("token,t", po::value(&token)->value_name("key")->default_value(token), "Mapbox access token")
- ("output,o", po::value(&output)->value_name("file")->default_value(output), "Output database file name")
- ;
+ args::ValueFlag<std::string> tokenValue(argumentParser, "key", "Mapbox access token", {'t', "token"});
+ args::ValueFlag<std::string> styleValue(argumentParser, "URL", "Map stylesheet", {'s', "style"});
+ args::ValueFlag<std::string> outputValue(argumentParser, "file", "Output database file name", {'o', "output"});
+
+ args::ValueFlag<double> northValue(argumentParser, "degrees", "North latitude", {"north"});
+ args::ValueFlag<double> westValue(argumentParser, "degrees", "West longitude", {"west"});
+ args::ValueFlag<double> southValue(argumentParser, "degrees", "South latitude", {"south"});
+ args::ValueFlag<double> eastValue(argumentParser, "degrees", "East longitude", {"east"});
+ args::ValueFlag<double> minZoomValue(argumentParser, "number", "Min zoom level", {"minZoom"});
+ args::ValueFlag<double> maxZoomValue(argumentParser, "number", "Max zoom level", {"maxZoom"});
+ args::ValueFlag<double> pixelRatioValue(argumentParser, "number", "Pixel ratio", {"pixelRatio"});
try {
- po::variables_map vm;
- po::store(po::parse_command_line(argc, argv, desc), vm);
- po::notify(vm);
- } catch(std::exception& e) {
- std::cout << "Error: " << e.what() << std::endl << desc;
+ argumentParser.ParseCLI(argc, argv);
+ } catch (args::Help) {
+ std::cout << argumentParser;
+ exit(0);
+ } catch (args::ParseError e) {
+ std::cerr << e.what() << std::endl;
+ std::cerr << argumentParser;
exit(1);
+ } catch (args::ValidationError e) {
+ std::cerr << e.what() << std::endl;
+ std::cerr << argumentParser;
+ exit(2);
}
+ std::string style = styleValue ? args::get(styleValue) : mbgl::util::default_styles::streets.url;
+
+ // Bay area
+ const double north = northValue ? args::get(northValue) : 37.2;
+ const double west = westValue ? args::get(westValue) : -122.8;
+ const double south = southValue ? args::get(southValue) : 38.1;
+ const double east = eastValue ? args::get(eastValue) : -121.7;
+
+ const double minZoom = minZoomValue ? args::get(minZoomValue) : 0.0;
+ const double maxZoom = maxZoomValue ? args::get(maxZoomValue) : 15.0;
+ const double pixelRatio = pixelRatioValue ? args::get(pixelRatioValue) : 1.0;
+ const std::string output = outputValue ? args::get(outputValue) : "offline.db";
+
+ const char* tokenEnv = getenv("MAPBOX_ACCESS_TOKEN");
+ const std::string token = tokenValue ? args::get(tokenValue) : (tokenEnv ? tokenEnv : std::string());
+
using namespace mbgl;
util::RunLoop loop;
diff --git a/bin/render.cpp b/bin/render.cpp
index 444921ff2b..6ceb32eb00 100644
--- a/bin/render.cpp
+++ b/bin/render.cpp
@@ -1,81 +1,82 @@
#include <mbgl/map/map.hpp>
#include <mbgl/util/image.hpp>
#include <mbgl/util/run_loop.hpp>
+#include <mbgl/util/default_styles.hpp>
#include <mbgl/gl/headless_frontend.hpp>
#include <mbgl/util/default_thread_pool.hpp>
#include <mbgl/storage/default_file_source.hpp>
#include <mbgl/style/style.hpp>
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunknown-pragmas"
-#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
-#pragma GCC diagnostic ignored "-Wshadow"
-#include <boost/program_options.hpp>
-#pragma GCC diagnostic pop
-
-namespace po = boost::program_options;
+#include <args/args.hxx>
#include <cstdlib>
#include <iostream>
#include <fstream>
int main(int argc, char *argv[]) {
- std::string style_path;
- double lat = 0, lon = 0;
- double zoom = 0;
- double bearing = 0;
- double pitch = 0;
- double pixelRatio = 1;
-
- uint32_t width = 512;
- uint32_t height = 512;
- static std::string output = "out.png";
- std::string cache_file = "cache.sqlite";
- std::string asset_root = ".";
- std::string token;
- bool debug = false;
-
- po::options_description desc("Allowed options");
- desc.add_options()
- ("style,s", po::value(&style_path)->required()->value_name("json"), "Map stylesheet")
- ("lon,x", po::value(&lon)->value_name("degrees")->default_value(lon), "Longitude")
- ("lat,y", po::value(&lat)->value_name("degrees")->default_value(lat), "Latitude in degrees")
- ("zoom,z", po::value(&zoom)->value_name("number")->default_value(zoom), "Zoom level")
- ("bearing,b", po::value(&bearing)->value_name("degrees")->default_value(bearing), "Bearing")
- ("pitch,p", po::value(&pitch)->value_name("degrees")->default_value(pitch), "Pitch")
- ("width,w", po::value(&width)->value_name("pixels")->default_value(width), "Image width")
- ("height,h", po::value(&height)->value_name("pixels")->default_value(height), "Image height")
- ("ratio,r", po::value(&pixelRatio)->value_name("number")->default_value(pixelRatio), "Image scale factor")
- ("token,t", po::value(&token)->value_name("key")->default_value(token), "Mapbox access token")
- ("debug", po::bool_switch(&debug)->default_value(debug), "Debug mode")
- ("output,o", po::value(&output)->value_name("file")->default_value(output), "Output file name")
- ("cache,d", po::value(&cache_file)->value_name("file")->default_value(cache_file), "Cache database file name")
- ("assets,d", po::value(&asset_root)->value_name("file")->default_value(asset_root), "Directory to which asset:// URLs will resolve")
- ;
+ args::ArgumentParser argumentParser("Mapbox GL render tool");
+ args::HelpFlag helpFlag(argumentParser, "help", "Display this help menu", {"help"});
+
+ args::ValueFlag<std::string> tokenValue(argumentParser, "key", "Mapbox access token", {'t', "token"});
+ args::ValueFlag<std::string> styleValue(argumentParser, "URL", "Map stylesheet", {'s', "style"});
+ args::ValueFlag<std::string> outputValue(argumentParser, "file", "Output file name", {'o', "output"});
+ args::ValueFlag<std::string> cacheValue(argumentParser, "file", "Cache database file name", {'c', "cache"});
+ args::ValueFlag<std::string> assetsValue(argumentParser, "file", "Directory to which asset:// URLs will resolve", {'a', "assets"});
+
+ args::Flag debugFlag(argumentParser, "debug", "Debug mode", {"debug"});
+
+ args::ValueFlag<double> pixelRatioValue(argumentParser, "number", "Image scale factor", {'r', "ratio"});
+
+ args::ValueFlag<double> zoomValue(argumentParser, "number", "Zoom level", {'z', "zoom"});
+
+ args::ValueFlag<double> lonValue(argumentParser, "degrees", "Longitude", {'x', "lon"});
+ args::ValueFlag<double> latValue(argumentParser, "degrees", "Latitude", {'y', "lat"});
+ args::ValueFlag<double> bearingValue(argumentParser, "degrees", "Bearing", {'b', "bearing"});
+ args::ValueFlag<double> pitchValue(argumentParser, "degrees", "Pitch", {'p', "pitch"});
+ args::ValueFlag<uint32_t> widthValue(argumentParser, "pixels", "Image width", {'w', "width"});
+ args::ValueFlag<uint32_t> heightValue(argumentParser, "pixels", "Image height", {'h', "height"});
try {
- po::variables_map vm;
- po::store(po::parse_command_line(argc, argv, desc), vm);
- po::notify(vm);
- } catch(std::exception& e) {
- std::cout << "Error: " << e.what() << std::endl << desc;
+ argumentParser.ParseCLI(argc, argv);
+ } catch (args::Help) {
+ std::cout << argumentParser;
+ exit(0);
+ } catch (args::ParseError e) {
+ std::cerr << e.what() << std::endl;
+ std::cerr << argumentParser;
exit(1);
+ } catch (args::ValidationError e) {
+ std::cerr << e.what() << std::endl;
+ std::cerr << argumentParser;
+ exit(2);
}
+ std::string style = styleValue ? args::get(styleValue) : mbgl::util::default_styles::streets.url;
+ const double lat = latValue ? args::get(latValue) : 0;
+ const double lon = lonValue ? args::get(lonValue) : 0;
+ const double zoom = zoomValue ? args::get(zoomValue) : 0;
+ const double bearing = bearingValue ? args::get(bearingValue) : 0;
+ const double pitch = pitchValue ? args::get(pitchValue) : 0;
+ const double pixelRatio = pixelRatioValue ? args::get(pixelRatioValue) : 1;
+
+ const uint32_t width = widthValue ? args::get(widthValue) : 512;
+ const uint32_t height = heightValue ? args::get(heightValue) : 512;
+ const std::string output = outputValue ? args::get(outputValue) : "out.png";
+ const std::string cache_file = cacheValue ? args::get(cacheValue) : "cache.sqlite";
+ const std::string asset_root = assetsValue ? args::get(assetsValue) : ".";
+
+ // Try to load the token from the environment.
+ const char* tokenEnv = getenv("MAPBOX_ACCESS_TOKEN");
+ const std::string token = tokenValue ? args::get(tokenValue) : (tokenEnv ? tokenEnv : std::string());
+
+ const bool debug = debugFlag ? args::get(debugFlag) : false;
+
using namespace mbgl;
util::RunLoop loop;
DefaultFileSource fileSource(cache_file, asset_root);
- // Try to load the token from the environment.
- if (!token.size()) {
- const char *token_ptr = getenv("MAPBOX_ACCESS_TOKEN");
- if (token_ptr) {
- token = token_ptr;
- }
- }
-
// Set access token if present
if (token.size()) {
fileSource.setAccessToken(std::string(token));
@@ -83,13 +84,13 @@ int main(int argc, char *argv[]) {
ThreadPool threadPool(4);
HeadlessFrontend frontend({ width, height }, pixelRatio, fileSource, threadPool);
- Map map(frontend, MapObserver::nullObserver(), frontend.getSize(), pixelRatio, fileSource, threadPool, MapMode::Still);
+ Map map(frontend, MapObserver::nullObserver(), frontend.getSize(), pixelRatio, fileSource, threadPool, MapMode::Static);
- if (style_path.find("://") == std::string::npos) {
- style_path = std::string("file://") + style_path;
+ if (style.find("://") == std::string::npos) {
+ style = std::string("file://") + style;
}
- map.getStyle().loadURL(style_path);
+ map.getStyle().loadURL(style);
map.setLatLngZoom({ lat, lon }, zoom);
map.setBearing(bearing);
map.setPitch(pitch);
diff --git a/circle.yml b/circle.yml
index d76ab4a8ab..122d8f3b9a 100644
--- a/circle.yml
+++ b/circle.yml
@@ -4,6 +4,7 @@ workflows:
version: 2
default:
jobs:
+ - nitpick
- clang-tidy:
filters:
branches:
@@ -18,11 +19,11 @@ workflows:
filters:
tags:
only: /node-.*/
- - node6-clang39-debug:
+ - node6-gcc6-debug:
filters:
tags:
only: /node-.*/
- - linux-clang39-debug
+ - linux-clang-3.8-libcxx-debug
- linux-clang4-sanitize-address
- linux-clang4-sanitize-undefined
- linux-clang4-sanitize-thread
@@ -63,6 +64,15 @@ step-library:
paths: [ "node_modules", "/root/.ccache", "~/.ccache", "mason_packages/.binaries" ]
+ - &restore-gradle-cache
+ restore_cache:
+ keys:
+ - 'v3/{{ checksum "platform/android/gradle/dependencies.gradle" }}/{{ checksum "platform/android/build.gradle" }}/{{ checksum "platform/android/gradle/wrapper/gradle-wrapper.properties" }}'
+ - &save-gradle-cache
+ save_cache:
+ key: 'v3/{{ checksum "platform/android/gradle/dependencies.gradle" }}/{{ checksum "platform/android/build.gradle" }}/{{ checksum "platform/android/gradle/wrapper/gradle-wrapper.properties" }}'
+ paths: [ "/root/.gradle" ]
+
- &reset-ccache-stats
run:
name: Clear ccache statistics
@@ -114,6 +124,10 @@ step-library:
run:
name: Build ios-test
command: make ios-test
+ - &build-ios-integration-test
+ run:
+ name: Build ios-integration-test
+ command: make ios-integration-test
- &build-macos-test
run:
name: Build and run macOS tests
@@ -204,10 +218,51 @@ step-library:
destination: render-tests
jobs:
+ nitpick:
+ docker:
+ - image: mbgl/7d2403f42e:base
+ working_directory: /src
+ environment:
+ LIBSYSCONFCPUS: 4
+ JOBS: 4
+ BUILDTYPE: Debug
+ steps:
+ - checkout
+ - *generate-cache-key
+ - *restore-cache
+ - run:
+ name: Initialize submodule
+ command: git submodule update --init mapbox-gl-js
+ - run:
+ name: npm install
+ command: npm install --ignore-scripts
+ - *save-cache
+ - run:
+ name: Verify submodule pin
+ command: scripts/nitpick/submodule-pin.js
+ when: always
+ - run:
+ name: CMake file list generation
+ command: scripts/nitpick/generated-code.js cmake
+ when: always
+ - run:
+ name: Shader code generation
+ command: scripts/nitpick/generated-code.js shader
+ when: always
+ - run:
+ name: Style code generation
+ command: scripts/nitpick/generated-code.js style
+ when: always
+ - run:
+ name: Android code generation
+ command: scripts/nitpick/generated-code.js android
+ when: always
+
+
# ------------------------------------------------------------------------------
clang-tidy:
docker:
- - image: mbgl/ci:r4-linux-clang-3.9
+ - image: mbgl/7d2403f42e:linux-clang-3.9
working_directory: /src
environment:
LIBSYSCONFCPUS: 4
@@ -234,7 +289,7 @@ jobs:
# ------------------------------------------------------------------------------
android-debug-arm-v7:
docker:
- - image: mbgl/ci@sha256:c34e221294d81da80918d3e9a9df5de795b067e88f86d7c9a5e262763332536e
+ - image: mbgl/7d2403f42e:android-ndk-r16b
resource_class: large
working_directory: /src
environment:
@@ -246,6 +301,7 @@ jobs:
- checkout
- *generate-cache-key
- *restore-cache
+ - *restore-gradle-cache
- *reset-ccache-stats
- run:
name: Check code style
@@ -269,6 +325,7 @@ jobs:
make android-ui-test-arm-v7
- *show-ccache-stats
- *save-cache
+ - *save-gradle-cache
- run:
name: Log in to Google Cloud Platform
shell: /bin/bash -euo pipefail
@@ -282,23 +339,12 @@ jobs:
shell: /bin/bash -euo pipefail
command: |
gcloud firebase test android models list
- (gcloud firebase test android run --type instrumentation \
- --app platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/MapboxGLAndroidSDKTestApp-debug.apk \
- --test platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/MapboxGLAndroidSDKTestApp-debug-androidTest.apk \
- --device-ids shamu --os-version-ids 22 --locales en --orientations portrait --timeout 20m \
- 2>&1 | tee firebase.log) || EXIT_CODE=$?
-
- FIREBASE_TEST_BUCKET=$(sed -n 's|^.*\[https://console.developers.google.com/storage/browser/\([^]]*\).*|gs://\1|p' firebase.log)
- echo "Downloading from: ${FIREBASE_TEST_BUCKET}"
- gsutil -m cp -n -R -Z "$FIREBASE_TEST_BUCKET*" platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk
-
- echo "Try running ndk-stack on downloaded logcat to symbolicate the stacktraces:"
- find platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk -type f -name "logcat" -print0 | \
- xargs -0 -I '{}' ${ANDROID_NDK_HOME}/ndk-stack -sym build/android-arm-v7/Debug -dump {}
-
- exit ${EXIT_CODE:-0}
+ gcloud firebase test android run --type instrumentation \
+ --app platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/debug/MapboxGLAndroidSDKTestApp-debug.apk \
+ --test platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/androidTest/debug/MapboxGLAndroidSDKTestApp-debug-androidTest.apk \
+ --device-ids sailfish --os-version-ids 26 --locales en --orientations portrait --timeout 20m
- store_artifacts:
- path: platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk
+ path: platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/debug
destination: .
- store_artifacts:
path: platform/android/MapboxGLAndroidSDK/build/reports/lint-results.html
@@ -316,7 +362,7 @@ jobs:
# ------------------------------------------------------------------------------
android-release-all:
docker:
- - image: mbgl/ci@sha256:c34e221294d81da80918d3e9a9df5de795b067e88f86d7c9a5e262763332536e
+ - image: mbgl/7d2403f42e:android-ndk-r16b
resource_class: large
working_directory: /src
environment:
@@ -328,6 +374,7 @@ jobs:
- checkout
- *generate-cache-key
- *restore-cache
+ - *restore-gradle-cache
- *reset-ccache-stats
- run:
name: Generate Maven credentials
@@ -344,8 +391,9 @@ jobs:
command: make apackage
- *show-ccache-stats
- *save-cache
+ - *save-gradle-cache
- store_artifacts:
- path: platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk
+ path: platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/release
destination: .
- deploy:
name: Show statistics
@@ -355,13 +403,13 @@ jobs:
- deploy:
name: Publish to Maven
command: |
- if [ "${CIRCLE_BRANCH}" == "release-agua" ]; then make run-android-upload-archives ; fi
+ if [ "${CIRCLE_BRANCH}" == "master" ]; then make run-android-upload-archives ; fi
# ------------------------------------------------------------------------------
node4-clang39-release:
docker:
- - image: mbgl/ci:r4-linux-clang-3.9-node-4
+ - image: mbgl/7d2403f42e:linux-clang-3.9-node-4
working_directory: /src
environment:
LIBSYSCONFCPUS: 4
@@ -384,7 +432,7 @@ jobs:
# ------------------------------------------------------------------------------
node6-clang39-release:
docker:
- - image: mbgl/ci:r4-linux-clang-3.9
+ - image: mbgl/7d2403f42e:linux-clang-3.9
working_directory: /src
environment:
LIBSYSCONFCPUS: 4
@@ -405,16 +453,16 @@ jobs:
- *upload-render-tests
# ------------------------------------------------------------------------------
- node6-clang39-debug:
+ node6-gcc6-debug:
docker:
- - image: mbgl/ci:r4-linux-clang-3.9
+ - image: mbgl/7d2403f42e:linux-gcc-6
+ resource_class: large
working_directory: /src
environment:
LIBSYSCONFCPUS: 4
JOBS: 4
BUILDTYPE: Debug
WITH_EGL: 1
- WITH_CXX11ABI: 0
steps:
- checkout
- *generate-cache-key
@@ -428,31 +476,29 @@ jobs:
- *upload-render-tests-recycle-map
# ------------------------------------------------------------------------------
- linux-clang39-debug:
+ linux-clang-3.8-libcxx-debug:
docker:
- - image: mbgl/ci:r4-linux-clang-3.9
+ - image: mbgl/7d2403f42e:linux-clang-3.8-libcxx
working_directory: /src
environment:
LIBSYSCONFCPUS: 4
JOBS: 4
BUILDTYPE: Debug
WITH_EGL: 1
+ WITH_CXX11ABI: 1
steps:
- checkout
- *generate-cache-key
- *restore-cache
- *reset-ccache-stats
- *build-linux
- - *build-benchmark
- - *build-test
- *show-ccache-stats
- *save-cache
- - *run-unit-tests
# ------------------------------------------------------------------------------
linux-clang4-sanitize-address:
docker:
- - image: mbgl/ci:r4-linux-clang-4
+ - image: mbgl/7d2403f42e:linux-clang-4
working_directory: /src
environment:
LIBSYSCONFCPUS: 4
@@ -482,7 +528,7 @@ jobs:
# ------------------------------------------------------------------------------
linux-clang4-sanitize-undefined:
docker:
- - image: mbgl/ci:r4-linux-clang-4
+ - image: mbgl/7d2403f42e:linux-clang-4
working_directory: /src
environment:
LIBSYSCONFCPUS: 4
@@ -512,7 +558,7 @@ jobs:
# ------------------------------------------------------------------------------
linux-clang4-sanitize-thread:
docker:
- - image: mbgl/ci:r4-linux-clang-4
+ - image: mbgl/7d2403f42e:linux-clang-4
working_directory: /src
environment:
LIBSYSCONFCPUS: 4
@@ -542,12 +588,12 @@ jobs:
# ------------------------------------------------------------------------------
linux-gcc4.9-debug:
docker:
- - image: mbgl/ci:r4-linux-gcc-4.9
+ - image: mbgl/7d2403f42e:linux-gcc-4.9
resource_class: large
working_directory: /src
environment:
LIBSYSCONFCPUS: 4
- JOBS: 2
+ JOBS: 4
BUILDTYPE: Debug
WITH_EGL: 1
WITH_CXX11ABI: 0
@@ -567,12 +613,12 @@ jobs:
# ------------------------------------------------------------------------------
linux-gcc5-debug-coverage:
docker:
- - image: mbgl/ci:r4-linux-gcc-5
+ - image: mbgl/7d2403f42e:linux-gcc-5
resource_class: large
working_directory: /src
environment:
LIBSYSCONFCPUS: 4
- JOBS: 2
+ JOBS: 4
BUILDTYPE: Debug
WITH_EGL: 1
WITH_COVERAGE: 1
@@ -595,12 +641,12 @@ jobs:
# ------------------------------------------------------------------------------
linux-gcc5-release-qt4:
docker:
- - image: mbgl/ci:r4-linux-gcc-5-qt-4
+ - image: mbgl/7d2403f42e:linux-gcc-5-qt-4
resource_class: large
working_directory: /src
environment:
LIBSYSCONFCPUS: 4
- JOBS: 2 # OOM, causing the compiler to crash.
+ JOBS: 4
BUILDTYPE: Release
GTEST_OUTPUT: xml
LD_PRELOAD: /usr/lib/x86_64-linux-gnu/libjemalloc.so
@@ -623,12 +669,12 @@ jobs:
# ------------------------------------------------------------------------------
linux-gcc5-release-qt5:
docker:
- - image: mbgl/ci:r4-linux-gcc-5-qt-5
+ - image: mbgl/7d2403f42e:linux-gcc-5-qt-5.9
resource_class: large
working_directory: /src
environment:
LIBSYSCONFCPUS: 4
- JOBS: 2 # OOM, causing the compiler to crash.
+ JOBS: 4
BUILDTYPE: Release
WITH_QT_I18N: 1
steps:
@@ -649,12 +695,12 @@ jobs:
JOBS: 1 # https://github.com/mapbox/mapbox-gl-native/issues/9108
command: |
xvfb-run --server-args="-screen 0 1024x768x24" \
- scripts/valgrind.sh build/qt-linux-x86_64/Release/mbgl-test --gtest_filter=-*.Load --gtest_filter=-Memory.Vector
+ build/qt-linux-x86_64/Release/mbgl-test --gtest_filter=-*.Load --gtest_filter=-Memory.Vector
# ------------------------------------------------------------------------------
ios-debug:
macos:
- xcode: "9.0"
+ xcode: "9.2.0"
environment:
BUILDTYPE: Debug
HOMEBREW_NO_AUTO_UPDATE: 1
@@ -665,14 +711,21 @@ jobs:
- *restore-cache
- *reset-ccache-stats
- *build-ios-test
+ - *build-ios-integration-test
- *check-public-symbols
+ - run:
+ name: Lint plist files
+ command: make ios-lint
+ - run:
+ name: Nitpick Darwin code generation
+ command: scripts/nitpick/generated-code.js darwin
- *show-ccache-stats
- *save-cache
# ------------------------------------------------------------------------------
ios-sanitize-address:
macos:
- xcode: "9.0"
+ xcode: "9.2.0"
environment:
BUILDTYPE: Debug
HOMEBREW_NO_AUTO_UPDATE: 1
@@ -691,7 +744,7 @@ jobs:
# ------------------------------------------------------------------------------
ios-sanitize-thread:
macos:
- xcode: "9.0"
+ xcode: "9.2.0"
environment:
BUILDTYPE: Debug
HOMEBREW_NO_AUTO_UPDATE: 1
@@ -710,7 +763,7 @@ jobs:
# ------------------------------------------------------------------------------
macos-debug:
macos:
- xcode: "9.0"
+ xcode: "9.2.0"
environment:
BUILDTYPE: Debug
HOMEBREW_NO_AUTO_UPDATE: 1
@@ -722,6 +775,12 @@ jobs:
- *reset-ccache-stats
- *build-macos-test
- *check-public-symbols
+ - run:
+ name: Lint plist files
+ command: make macos-lint
+ - run:
+ name: Nitpick Darwin code generation
+ command: scripts/nitpick/generated-code.js darwin
- *show-ccache-stats
- *save-cache
- store_artifacts:
@@ -731,7 +790,7 @@ jobs:
# ------------------------------------------------------------------------------
macos-debug-qt5:
macos:
- xcode: "9.0"
+ xcode: "9.2.0"
environment:
BUILDTYPE: Debug
HOMEBREW_NO_AUTO_UPDATE: 1
@@ -756,7 +815,7 @@ jobs:
# ------------------------------------------------------------------------------
macos-release-node4:
macos:
- xcode: "9.0"
+ xcode: "9.2.0"
environment:
BUILDTYPE: RelWithDebInfo
HOMEBREW_NO_AUTO_UPDATE: 1
@@ -777,7 +836,7 @@ jobs:
# ------------------------------------------------------------------------------
macos-release-node6:
macos:
- xcode: "9.0"
+ xcode: "9.2.0"
environment:
BUILDTYPE: RelWithDebInfo
HOMEBREW_NO_AUTO_UPDATE: 1
diff --git a/cmake/benchmark-files.cmake b/cmake/benchmark-files.cmake
index 9161209128..fdafcf30f9 100644
--- a/cmake/benchmark-files.cmake
+++ b/cmake/benchmark-files.cmake
@@ -1,30 +1,27 @@
-# Do not edit. Regenerate this with ./scripts/generate-benchmark-files.sh
+# This file is generated. Do not edit. Regenerate this with scripts/generate-cmake-files.js
set(MBGL_BENCHMARK_FILES
# api
benchmark/api/query.benchmark.cpp
benchmark/api/render.benchmark.cpp
+ # benchmark
+ benchmark/include/mbgl/benchmark.hpp
+ benchmark/src/main.cpp
+ benchmark/src/mbgl/benchmark/benchmark.cpp
+ benchmark/src/mbgl/benchmark/stub_geometry_tile_feature.hpp
+
# function
benchmark/function/camera_function.benchmark.cpp
benchmark/function/composite_function.benchmark.cpp
benchmark/function/source_function.benchmark.cpp
- # include/mbgl
- benchmark/include/mbgl/benchmark.hpp
-
# parse
benchmark/parse/filter.benchmark.cpp
benchmark/parse/tile_mask.benchmark.cpp
benchmark/parse/vector_tile.benchmark.cpp
- # src
- benchmark/src/main.cpp
-
- # src/mbgl/benchmark
- benchmark/src/mbgl/benchmark/benchmark.cpp
- benchmark/src/mbgl/benchmark/stub_geometry_tile_feature.hpp
-
# util
benchmark/util/dtoa.benchmark.cpp
+
)
diff --git a/cmake/benchmark.cmake b/cmake/benchmark.cmake
index 87351e97b1..ea5b3dfa7a 100644
--- a/cmake/benchmark.cmake
+++ b/cmake/benchmark.cmake
@@ -2,10 +2,6 @@ add_executable(mbgl-benchmark
${MBGL_BENCHMARK_FILES}
)
-target_compile_options(mbgl-benchmark
- PRIVATE -fvisibility-inlines-hidden
-)
-
target_include_directories(mbgl-benchmark
PRIVATE src
PRIVATE benchmark/include
@@ -19,6 +15,7 @@ target_link_libraries(mbgl-benchmark
target_add_mason_package(mbgl-benchmark PRIVATE boost)
target_add_mason_package(mbgl-benchmark PRIVATE benchmark)
+target_add_mason_package(mbgl-benchmark PRIVATE geojson)
target_add_mason_package(mbgl-benchmark PRIVATE rapidjson)
target_add_mason_package(mbgl-benchmark PRIVATE protozero)
target_add_mason_package(mbgl-benchmark PRIVATE vector-tile)
diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake
index 0c109d080d..f24482e301 100644
--- a/cmake/core-files.cmake
+++ b/cmake/core-files.cmake
@@ -1,4 +1,4 @@
-# Do not edit. Regenerate this with ./scripts/generate-core-files.sh
+# This file is generated. Do not edit. Regenerate this with scripts/generate-cmake-files.js
set(MBGL_CORE_FILES
# actor
@@ -44,6 +44,8 @@ set(MBGL_CORE_FILES
# geometry
src/mbgl/geometry/anchor.hpp
src/mbgl/geometry/debug_font_data.hpp
+ src/mbgl/geometry/dem_data.cpp
+ src/mbgl/geometry/dem_data.hpp
src/mbgl/geometry/feature_index.cpp
src/mbgl/geometry/feature_index.hpp
src/mbgl/geometry/line_atlas.cpp
@@ -128,10 +130,13 @@ set(MBGL_CORE_FILES
# programs
src/mbgl/programs/attributes.hpp
+ src/mbgl/programs/background_program.cpp
+ src/mbgl/programs/background_program.hpp
src/mbgl/programs/binary_program.cpp
src/mbgl/programs/binary_program.hpp
src/mbgl/programs/circle_program.cpp
src/mbgl/programs/circle_program.hpp
+ src/mbgl/programs/clipping_mask_program.hpp
src/mbgl/programs/collision_box_program.cpp
src/mbgl/programs/collision_box_program.hpp
src/mbgl/programs/debug_program.hpp
@@ -141,6 +146,14 @@ set(MBGL_CORE_FILES
src/mbgl/programs/fill_extrusion_program.hpp
src/mbgl/programs/fill_program.cpp
src/mbgl/programs/fill_program.hpp
+ src/mbgl/programs/heatmap_program.cpp
+ src/mbgl/programs/heatmap_program.hpp
+ src/mbgl/programs/heatmap_texture_program.cpp
+ src/mbgl/programs/heatmap_texture_program.hpp
+ src/mbgl/programs/hillshade_prepare_program.cpp
+ src/mbgl/programs/hillshade_prepare_program.hpp
+ src/mbgl/programs/hillshade_program.cpp
+ src/mbgl/programs/hillshade_program.hpp
src/mbgl/programs/line_program.cpp
src/mbgl/programs/line_program.hpp
src/mbgl/programs/program.hpp
@@ -156,10 +169,12 @@ set(MBGL_CORE_FILES
# renderer
include/mbgl/renderer/backend_scope.hpp
+ include/mbgl/renderer/mode.hpp
include/mbgl/renderer/query.hpp
include/mbgl/renderer/renderer.hpp
include/mbgl/renderer/renderer_backend.hpp
include/mbgl/renderer/renderer_frontend.hpp
+ include/mbgl/renderer/renderer_observer.hpp
src/mbgl/renderer/backend_scope.cpp
src/mbgl/renderer/bucket.hpp
src/mbgl/renderer/bucket_parameters.cpp
@@ -167,8 +182,6 @@ set(MBGL_CORE_FILES
src/mbgl/renderer/cross_faded_property_evaluator.cpp
src/mbgl/renderer/cross_faded_property_evaluator.hpp
src/mbgl/renderer/data_driven_property_evaluator.hpp
- src/mbgl/renderer/frame_history.cpp
- src/mbgl/renderer/frame_history.hpp
src/mbgl/renderer/group_by_layout.cpp
src/mbgl/renderer/group_by_layout.hpp
src/mbgl/renderer/image_atlas.cpp
@@ -198,7 +211,6 @@ set(MBGL_CORE_FILES
src/mbgl/renderer/renderer_backend.cpp
src/mbgl/renderer/renderer_impl.cpp
src/mbgl/renderer/renderer_impl.hpp
- src/mbgl/renderer/renderer_observer.hpp
src/mbgl/renderer/style_diff.cpp
src/mbgl/renderer/style_diff.hpp
src/mbgl/renderer/tile_mask.hpp
@@ -217,6 +229,10 @@ set(MBGL_CORE_FILES
src/mbgl/renderer/buckets/fill_bucket.hpp
src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp
src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp
+ src/mbgl/renderer/buckets/heatmap_bucket.cpp
+ src/mbgl/renderer/buckets/heatmap_bucket.hpp
+ src/mbgl/renderer/buckets/hillshade_bucket.cpp
+ src/mbgl/renderer/buckets/hillshade_bucket.hpp
src/mbgl/renderer/buckets/line_bucket.cpp
src/mbgl/renderer/buckets/line_bucket.hpp
src/mbgl/renderer/buckets/raster_bucket.cpp
@@ -235,6 +251,10 @@ set(MBGL_CORE_FILES
src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp
src/mbgl/renderer/layers/render_fill_layer.cpp
src/mbgl/renderer/layers/render_fill_layer.hpp
+ src/mbgl/renderer/layers/render_heatmap_layer.cpp
+ src/mbgl/renderer/layers/render_heatmap_layer.hpp
+ src/mbgl/renderer/layers/render_hillshade_layer.cpp
+ src/mbgl/renderer/layers/render_hillshade_layer.hpp
src/mbgl/renderer/layers/render_line_layer.cpp
src/mbgl/renderer/layers/render_line_layer.hpp
src/mbgl/renderer/layers/render_raster_layer.cpp
@@ -243,20 +263,32 @@ set(MBGL_CORE_FILES
src/mbgl/renderer/layers/render_symbol_layer.hpp
# renderer/sources
+ src/mbgl/renderer/sources/render_custom_geometry_source.cpp
+ src/mbgl/renderer/sources/render_custom_geometry_source.hpp
src/mbgl/renderer/sources/render_geojson_source.cpp
src/mbgl/renderer/sources/render_geojson_source.hpp
src/mbgl/renderer/sources/render_image_source.cpp
src/mbgl/renderer/sources/render_image_source.hpp
+ src/mbgl/renderer/sources/render_raster_dem_source.cpp
+ src/mbgl/renderer/sources/render_raster_dem_source.hpp
src/mbgl/renderer/sources/render_raster_source.cpp
src/mbgl/renderer/sources/render_raster_source.hpp
src/mbgl/renderer/sources/render_vector_source.cpp
src/mbgl/renderer/sources/render_vector_source.hpp
# shaders
+ src/mbgl/shaders/background.cpp
+ src/mbgl/shaders/background.hpp
+ src/mbgl/shaders/background_pattern.cpp
+ src/mbgl/shaders/background_pattern.hpp
src/mbgl/shaders/circle.cpp
src/mbgl/shaders/circle.hpp
+ src/mbgl/shaders/clipping_mask.cpp
+ src/mbgl/shaders/clipping_mask.hpp
src/mbgl/shaders/collision_box.cpp
src/mbgl/shaders/collision_box.hpp
+ src/mbgl/shaders/collision_circle.cpp
+ src/mbgl/shaders/collision_circle.hpp
src/mbgl/shaders/debug.cpp
src/mbgl/shaders/debug.hpp
src/mbgl/shaders/extrusion_texture.cpp
@@ -273,6 +305,14 @@ set(MBGL_CORE_FILES
src/mbgl/shaders/fill_outline_pattern.hpp
src/mbgl/shaders/fill_pattern.cpp
src/mbgl/shaders/fill_pattern.hpp
+ src/mbgl/shaders/heatmap.cpp
+ src/mbgl/shaders/heatmap.hpp
+ src/mbgl/shaders/heatmap_texture.cpp
+ src/mbgl/shaders/heatmap_texture.hpp
+ src/mbgl/shaders/hillshade.cpp
+ src/mbgl/shaders/hillshade.hpp
+ src/mbgl/shaders/hillshade_prepare.cpp
+ src/mbgl/shaders/hillshade_prepare.hpp
src/mbgl/shaders/line.cpp
src/mbgl/shaders/line.hpp
src/mbgl/shaders/line_pattern.cpp
@@ -321,6 +361,7 @@ set(MBGL_CORE_FILES
include/mbgl/style/data_driven_property_value.hpp
include/mbgl/style/filter.hpp
include/mbgl/style/filter_evaluator.hpp
+ include/mbgl/style/heatmap_color_property_value.hpp
include/mbgl/style/image.hpp
include/mbgl/style/layer.hpp
include/mbgl/style/layer_type.hpp
@@ -333,6 +374,8 @@ set(MBGL_CORE_FILES
include/mbgl/style/types.hpp
include/mbgl/style/undefined.hpp
src/mbgl/style/collection.hpp
+ src/mbgl/style/custom_tile_loader.cpp
+ src/mbgl/style/custom_tile_loader.hpp
src/mbgl/style/image.cpp
src/mbgl/style/image_impl.cpp
src/mbgl/style/image_impl.hpp
@@ -363,23 +406,86 @@ set(MBGL_CORE_FILES
# style/conversion
include/mbgl/style/conversion/constant.hpp
include/mbgl/style/conversion/coordinate.hpp
+ include/mbgl/style/conversion/custom_geometry_source_options.hpp
include/mbgl/style/conversion/data_driven_property_value.hpp
+ include/mbgl/style/conversion/expression.hpp
include/mbgl/style/conversion/filter.hpp
include/mbgl/style/conversion/function.hpp
include/mbgl/style/conversion/geojson.hpp
include/mbgl/style/conversion/geojson_options.hpp
+ include/mbgl/style/conversion/get_json_type.hpp
+ include/mbgl/style/conversion/heatmap_color_property_value.hpp
include/mbgl/style/conversion/layer.hpp
include/mbgl/style/conversion/light.hpp
- include/mbgl/style/conversion/make_property_setters.hpp
include/mbgl/style/conversion/position.hpp
- include/mbgl/style/conversion/property_setter.hpp
include/mbgl/style/conversion/property_value.hpp
include/mbgl/style/conversion/source.hpp
include/mbgl/style/conversion/tileset.hpp
include/mbgl/style/conversion/transition_options.hpp
+ src/mbgl/style/conversion/constant.cpp
+ src/mbgl/style/conversion/coordinate.cpp
+ src/mbgl/style/conversion/filter.cpp
src/mbgl/style/conversion/geojson.cpp
+ src/mbgl/style/conversion/geojson_options.cpp
+ src/mbgl/style/conversion/get_json_type.cpp
src/mbgl/style/conversion/json.hpp
+ src/mbgl/style/conversion/layer.cpp
+ src/mbgl/style/conversion/light.cpp
+ src/mbgl/style/conversion/make_property_setters.hpp
+ src/mbgl/style/conversion/position.cpp
+ src/mbgl/style/conversion/property_setter.hpp
+ src/mbgl/style/conversion/source.cpp
src/mbgl/style/conversion/stringify.hpp
+ src/mbgl/style/conversion/tileset.cpp
+ src/mbgl/style/conversion/transition_options.cpp
+
+ # style/expression
+ include/mbgl/style/expression/array_assertion.hpp
+ include/mbgl/style/expression/assertion.hpp
+ include/mbgl/style/expression/at.hpp
+ include/mbgl/style/expression/boolean_operator.hpp
+ include/mbgl/style/expression/case.hpp
+ include/mbgl/style/expression/check_subtype.hpp
+ include/mbgl/style/expression/coalesce.hpp
+ include/mbgl/style/expression/coercion.hpp
+ include/mbgl/style/expression/compound_expression.hpp
+ include/mbgl/style/expression/equals.hpp
+ include/mbgl/style/expression/expression.hpp
+ include/mbgl/style/expression/find_zoom_curve.hpp
+ include/mbgl/style/expression/get_covering_stops.hpp
+ include/mbgl/style/expression/interpolate.hpp
+ include/mbgl/style/expression/is_constant.hpp
+ include/mbgl/style/expression/is_expression.hpp
+ include/mbgl/style/expression/let.hpp
+ include/mbgl/style/expression/literal.hpp
+ include/mbgl/style/expression/match.hpp
+ include/mbgl/style/expression/parsing_context.hpp
+ include/mbgl/style/expression/step.hpp
+ include/mbgl/style/expression/type.hpp
+ include/mbgl/style/expression/value.hpp
+ src/mbgl/style/expression/array_assertion.cpp
+ src/mbgl/style/expression/assertion.cpp
+ src/mbgl/style/expression/at.cpp
+ src/mbgl/style/expression/boolean_operator.cpp
+ src/mbgl/style/expression/case.cpp
+ src/mbgl/style/expression/check_subtype.cpp
+ src/mbgl/style/expression/coalesce.cpp
+ src/mbgl/style/expression/coercion.cpp
+ src/mbgl/style/expression/compound_expression.cpp
+ src/mbgl/style/expression/equals.cpp
+ src/mbgl/style/expression/find_zoom_curve.cpp
+ src/mbgl/style/expression/get_covering_stops.cpp
+ src/mbgl/style/expression/interpolate.cpp
+ src/mbgl/style/expression/is_constant.cpp
+ src/mbgl/style/expression/is_expression.cpp
+ src/mbgl/style/expression/let.cpp
+ src/mbgl/style/expression/literal.cpp
+ src/mbgl/style/expression/match.cpp
+ src/mbgl/style/expression/parsing_context.cpp
+ src/mbgl/style/expression/step.cpp
+ src/mbgl/style/expression/util.cpp
+ src/mbgl/style/expression/util.hpp
+ src/mbgl/style/expression/value.cpp
# style/function
include/mbgl/style/function/camera_function.hpp
@@ -388,11 +494,13 @@ set(MBGL_CORE_FILES
include/mbgl/style/function/composite_exponential_stops.hpp
include/mbgl/style/function/composite_function.hpp
include/mbgl/style/function/composite_interval_stops.hpp
+ include/mbgl/style/function/convert.hpp
include/mbgl/style/function/exponential_stops.hpp
include/mbgl/style/function/identity_stops.hpp
include/mbgl/style/function/interval_stops.hpp
include/mbgl/style/function/source_function.hpp
src/mbgl/style/function/categorical_stops.cpp
+ src/mbgl/style/function/expression.cpp
src/mbgl/style/function/identity_stops.cpp
# style/layers
@@ -401,6 +509,8 @@ set(MBGL_CORE_FILES
include/mbgl/style/layers/custom_layer.hpp
include/mbgl/style/layers/fill_extrusion_layer.hpp
include/mbgl/style/layers/fill_layer.hpp
+ include/mbgl/style/layers/heatmap_layer.hpp
+ include/mbgl/style/layers/hillshade_layer.hpp
include/mbgl/style/layers/line_layer.hpp
include/mbgl/style/layers/raster_layer.hpp
include/mbgl/style/layers/symbol_layer.hpp
@@ -427,6 +537,16 @@ set(MBGL_CORE_FILES
src/mbgl/style/layers/fill_layer_impl.hpp
src/mbgl/style/layers/fill_layer_properties.cpp
src/mbgl/style/layers/fill_layer_properties.hpp
+ src/mbgl/style/layers/heatmap_layer.cpp
+ src/mbgl/style/layers/heatmap_layer_impl.cpp
+ src/mbgl/style/layers/heatmap_layer_impl.hpp
+ src/mbgl/style/layers/heatmap_layer_properties.cpp
+ src/mbgl/style/layers/heatmap_layer_properties.hpp
+ src/mbgl/style/layers/hillshade_layer.cpp
+ src/mbgl/style/layers/hillshade_layer_impl.cpp
+ src/mbgl/style/layers/hillshade_layer_impl.hpp
+ src/mbgl/style/layers/hillshade_layer_properties.cpp
+ src/mbgl/style/layers/hillshade_layer_properties.hpp
src/mbgl/style/layers/line_layer.cpp
src/mbgl/style/layers/line_layer_impl.cpp
src/mbgl/style/layers/line_layer_impl.hpp
@@ -444,16 +564,22 @@ set(MBGL_CORE_FILES
src/mbgl/style/layers/symbol_layer_properties.hpp
# style/sources
+ include/mbgl/style/sources/custom_geometry_source.hpp
include/mbgl/style/sources/geojson_source.hpp
include/mbgl/style/sources/image_source.hpp
+ include/mbgl/style/sources/raster_dem_source.hpp
include/mbgl/style/sources/raster_source.hpp
include/mbgl/style/sources/vector_source.hpp
+ src/mbgl/style/sources/custom_geometry_source.cpp
+ src/mbgl/style/sources/custom_geometry_source_impl.cpp
+ src/mbgl/style/sources/custom_geometry_source_impl.hpp
src/mbgl/style/sources/geojson_source.cpp
src/mbgl/style/sources/geojson_source_impl.cpp
src/mbgl/style/sources/geojson_source_impl.hpp
src/mbgl/style/sources/image_source.cpp
src/mbgl/style/sources/image_source_impl.cpp
src/mbgl/style/sources/image_source_impl.hpp
+ src/mbgl/style/sources/raster_dem_source.cpp
src/mbgl/style/sources/raster_source.cpp
src/mbgl/style/sources/raster_source_impl.cpp
src/mbgl/style/sources/raster_source_impl.hpp
@@ -467,8 +593,10 @@ set(MBGL_CORE_FILES
src/mbgl/text/check_max_angle.hpp
src/mbgl/text/collision_feature.cpp
src/mbgl/text/collision_feature.hpp
- src/mbgl/text/collision_tile.cpp
- src/mbgl/text/collision_tile.hpp
+ src/mbgl/text/collision_index.cpp
+ src/mbgl/text/collision_index.hpp
+ src/mbgl/text/cross_tile_symbol_index.cpp
+ src/mbgl/text/cross_tile_symbol_index.hpp
src/mbgl/text/get_anchors.cpp
src/mbgl/text/get_anchors.hpp
src/mbgl/text/glyph.cpp
@@ -482,7 +610,8 @@ set(MBGL_CORE_FILES
src/mbgl/text/glyph_pbf.hpp
src/mbgl/text/glyph_range.hpp
src/mbgl/text/local_glyph_rasterizer.hpp
- src/mbgl/text/placement_config.hpp
+ src/mbgl/text/placement.cpp
+ src/mbgl/text/placement.hpp
src/mbgl/text/quads.cpp
src/mbgl/text/quads.hpp
src/mbgl/text/shaping.cpp
@@ -491,6 +620,8 @@ set(MBGL_CORE_FILES
# tile
include/mbgl/tile/tile_id.hpp
include/mbgl/tile/tile_necessity.hpp
+ src/mbgl/tile/custom_geometry_tile.cpp
+ src/mbgl/tile/custom_geometry_tile.hpp
src/mbgl/tile/geojson_tile.cpp
src/mbgl/tile/geojson_tile.hpp
src/mbgl/tile/geojson_tile_data.hpp
@@ -500,6 +631,10 @@ set(MBGL_CORE_FILES
src/mbgl/tile/geometry_tile_data.hpp
src/mbgl/tile/geometry_tile_worker.cpp
src/mbgl/tile/geometry_tile_worker.hpp
+ src/mbgl/tile/raster_dem_tile.cpp
+ src/mbgl/tile/raster_dem_tile.hpp
+ src/mbgl/tile/raster_dem_tile_worker.cpp
+ src/mbgl/tile/raster_dem_tile_worker.hpp
src/mbgl/tile/raster_tile.cpp
src/mbgl/tile/raster_tile.hpp
src/mbgl/tile/raster_tile_worker.cpp
@@ -519,7 +654,6 @@ set(MBGL_CORE_FILES
src/mbgl/tile/vector_tile_data.hpp
# util
- include/mbgl/util/any.hpp
include/mbgl/util/async_request.hpp
include/mbgl/util/async_task.hpp
include/mbgl/util/char_array_buffer.hpp
@@ -555,7 +689,9 @@ set(MBGL_CORE_FILES
include/mbgl/util/tileset.hpp
include/mbgl/util/timer.hpp
include/mbgl/util/traits.hpp
+ include/mbgl/util/tuple.hpp
include/mbgl/util/type_list.hpp
+ include/mbgl/util/unique_any.hpp
include/mbgl/util/unitbezier.hpp
include/mbgl/util/util.hpp
include/mbgl/util/variant.hpp
@@ -624,4 +760,5 @@ set(MBGL_CORE_FILES
src/mbgl/util/version.cpp
src/mbgl/util/version.hpp
src/mbgl/util/work_request.cpp
+
)
diff --git a/cmake/core.cmake b/cmake/core.cmake
index c4e711f558..43ec141b78 100644
--- a/cmake/core.cmake
+++ b/cmake/core.cmake
@@ -2,11 +2,6 @@ add_library(mbgl-core STATIC
${MBGL_CORE_FILES}
)
-target_compile_options(mbgl-core
- PRIVATE -fPIC
- PRIVATE -fvisibility-inlines-hidden
-)
-
target_include_directories(mbgl-core
PUBLIC include
PRIVATE src
@@ -14,7 +9,6 @@ target_include_directories(mbgl-core
target_add_mason_package(mbgl-core PUBLIC geometry)
target_add_mason_package(mbgl-core PUBLIC variant)
-target_add_mason_package(mbgl-core PUBLIC any)
target_add_mason_package(mbgl-core PRIVATE unique_resource)
target_add_mason_package(mbgl-core PRIVATE rapidjson)
target_add_mason_package(mbgl-core PRIVATE boost)
diff --git a/cmake/files.cmake.ejs b/cmake/files.cmake.ejs
new file mode 100644
index 0000000000..57126509ed
--- /dev/null
+++ b/cmake/files.cmake.ejs
@@ -0,0 +1,13 @@
+<%
+ const name = locals.name;
+ const groups = locals.groups;
+-%>
+# This file is generated. Do not edit. Regenerate this with scripts/generate-cmake-files.js
+
+set(MBGL_<%- snakeCaseUpper(name) %>_FILES
+<% for (const key of Object.keys(groups).sort()) { -%>
+ # <%- key %>
+ <%- groups[key].sort().join('\n ') %>
+
+<% } -%>
+)
diff --git a/cmake/filesource.cmake b/cmake/filesource.cmake
index bb1b4e8c05..861a845d6e 100644
--- a/cmake/filesource.cmake
+++ b/cmake/filesource.cmake
@@ -26,18 +26,10 @@ add_library(mbgl-filesource STATIC
target_add_mason_package(mbgl-filesource PUBLIC geometry)
target_add_mason_package(mbgl-filesource PUBLIC variant)
-target_add_mason_package(mbgl-filesource PUBLIC any)
target_add_mason_package(mbgl-filesource PRIVATE rapidjson)
target_add_mason_package(mbgl-filesource PRIVATE boost)
target_add_mason_package(mbgl-filesource PRIVATE geojson)
-set_xcode_property(mbgl-filesource GCC_SYMBOLS_PRIVATE_EXTERN YES)
-
-target_compile_options(mbgl-filesource
- PRIVATE -fPIC
- PRIVATE -fvisibility-inlines-hidden
-)
-
target_include_directories(mbgl-filesource
PRIVATE include
PRIVATE src
diff --git a/cmake/glfw.cmake b/cmake/glfw.cmake
index 29d8d2ba94..2b2cb47ecb 100644
--- a/cmake/glfw.cmake
+++ b/cmake/glfw.cmake
@@ -12,10 +12,6 @@ target_sources(mbgl-glfw
PRIVATE platform/default/mbgl/util/default_styles.hpp
)
-target_compile_options(mbgl-glfw
- PRIVATE -fvisibility-inlines-hidden
-)
-
target_include_directories(mbgl-glfw
PRIVATE platform/default
)
@@ -29,6 +25,7 @@ target_add_mason_package(mbgl-glfw PRIVATE geojson)
target_add_mason_package(mbgl-glfw PRIVATE geometry)
target_add_mason_package(mbgl-glfw PRIVATE glfw)
target_add_mason_package(mbgl-glfw PRIVATE variant)
+target_add_mason_package(mbgl-glfw PRIVATE args)
mbgl_platform_glfw()
diff --git a/cmake/loop-darwin.cmake b/cmake/loop-darwin.cmake
index 46511d2548..692aecb8a2 100644
--- a/cmake/loop-darwin.cmake
+++ b/cmake/loop-darwin.cmake
@@ -4,13 +4,6 @@ add_library(mbgl-loop-darwin STATIC
platform/darwin/src/timer.cpp
)
-set_xcode_property(mbgl-loop-darwin GCC_SYMBOLS_PRIVATE_EXTERN YES)
-
-target_compile_options(mbgl-loop-darwin
- PRIVATE -fPIC
- PRIVATE -fvisibility-inlines-hidden
-)
-
target_include_directories(mbgl-loop-darwin
PRIVATE include
PRIVATE src
diff --git a/cmake/loop-uv.cmake b/cmake/loop-uv.cmake
index 8840040963..f4e7ced00e 100644
--- a/cmake/loop-uv.cmake
+++ b/cmake/loop-uv.cmake
@@ -4,11 +4,6 @@ add_library(mbgl-loop-uv STATIC
platform/default/timer.cpp
)
-target_compile_options(mbgl-loop-uv
- PRIVATE -fPIC
- PRIVATE -fvisibility-inlines-hidden
-)
-
target_include_directories(mbgl-loop-uv
PRIVATE include
PRIVATE src
diff --git a/cmake/mbgl.cmake b/cmake/mbgl.cmake
index d853950c0d..3d82899538 100644
--- a/cmake/mbgl.cmake
+++ b/cmake/mbgl.cmake
@@ -6,73 +6,69 @@ if (NOT MBGL_PLATFORM)
endif()
endif()
-find_program(NodeJS_EXECUTABLE NAMES nodejs node)
-if (NOT NodeJS_EXECUTABLE)
- message(FATAL_ERROR "Could not find Node.js")
-endif()
-
-find_program(npm_EXECUTABLE NAMES npm)
-if (NOT npm_EXECUTABLE)
- message(FATAL_ERROR "Could not find npm")
-endif()
+if(WITH_NODEJS)
+ find_program(NodeJS_EXECUTABLE NAMES nodejs node)
+ if (NOT NodeJS_EXECUTABLE)
+ message(FATAL_ERROR "Could not find Node.js")
+ endif()
-function(_npm_install DIRECTORY NAME ADDITIONAL_DEPS)
- SET(NPM_INSTALL_FAILED FALSE)
- if("${DIRECTORY}/package.json" IS_NEWER_THAN "${DIRECTORY}/node_modules/.${NAME}.stamp")
- message(STATUS "Running 'npm install' for ${NAME}...")
- execute_process(
- COMMAND ${NodeJS_EXECUTABLE} ${npm_EXECUTABLE} install --ignore-scripts
- WORKING_DIRECTORY "${DIRECTORY}"
- RESULT_VARIABLE NPM_INSTALL_FAILED)
- if(NOT NPM_INSTALL_FAILED)
- execute_process(COMMAND ${CMAKE_COMMAND} -E touch "${DIRECTORY}/node_modules/.${NAME}.stamp")
- endif()
+ find_program(npm_EXECUTABLE NAMES npm)
+ if (NOT npm_EXECUTABLE)
+ message(FATAL_ERROR "Could not find npm")
endif()
- add_custom_command(
- OUTPUT "${DIRECTORY}/node_modules/.${NAME}.stamp"
- COMMAND ${NodeJS_EXECUTABLE} ${npm_EXECUTABLE} install --ignore-scripts
- COMMAND ${CMAKE_COMMAND} -E touch "${DIRECTORY}/node_modules/.${NAME}.stamp"
- WORKING_DIRECTORY "${DIRECTORY}"
- DEPENDS ${ADDITIONAL_DEPS} "${DIRECTORY}/package.json"
- COMMENT "Running 'npm install' for ${NAME}...")
-endfunction()
+ function(_npm_install DIRECTORY NAME ADDITIONAL_DEPS)
+ SET(NPM_INSTALL_FAILED FALSE)
+ if("${DIRECTORY}/package.json" IS_NEWER_THAN "${DIRECTORY}/node_modules/.${NAME}.stamp")
+ message(STATUS "Running 'npm install' for ${NAME}...")
+ execute_process(
+ COMMAND ${NodeJS_EXECUTABLE} ${npm_EXECUTABLE} install --ignore-scripts
+ WORKING_DIRECTORY "${DIRECTORY}"
+ RESULT_VARIABLE NPM_INSTALL_FAILED)
+ if(NOT NPM_INSTALL_FAILED)
+ execute_process(COMMAND ${CMAKE_COMMAND} -E touch "${DIRECTORY}/node_modules/.${NAME}.stamp")
+ endif()
+ endif()
-# Run submodule update
-message(STATUS "Updating submodules...")
-execute_process(
- COMMAND git submodule update --init mapbox-gl-js
- WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
+ add_custom_command(
+ OUTPUT "${DIRECTORY}/node_modules/.${NAME}.stamp"
+ COMMAND ${NodeJS_EXECUTABLE} ${npm_EXECUTABLE} install --ignore-scripts
+ COMMAND ${CMAKE_COMMAND} -E touch "${DIRECTORY}/node_modules/.${NAME}.stamp"
+ WORKING_DIRECTORY "${DIRECTORY}"
+ DEPENDS ${ADDITIONAL_DEPS} "${DIRECTORY}/package.json"
+ COMMENT "Running 'npm install' for ${NAME}...")
+ endfunction()
-if(MBGL_PLATFORM STREQUAL "ios")
+ # Run submodule update
+ message(STATUS "Updating submodules...")
execute_process(
- COMMAND git submodule update --init platform/ios/vendor/SMCalloutView platform/ios/uitest/KIF platform/ios/uitest/OHHTTPStubs
+ COMMAND git submodule update --init mapbox-gl-js
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
-endif()
-if(NOT EXISTS "${CMAKE_SOURCE_DIR}/mapbox-gl-js/node_modules")
- # Symlink mapbox-gl-js/node_modules so that the modules that are
- # about to be installed get cached between CI runs.
- execute_process(
- COMMAND ln -sF ../node_modules .
- WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/mapbox-gl-js")
-endif()
+ if(NOT EXISTS "${CMAKE_SOURCE_DIR}/mapbox-gl-js/node_modules")
+ # Symlink mapbox-gl-js/node_modules so that the modules that are
+ # about to be installed get cached between CI runs.
+ execute_process(
+ COMMAND ln -sF ../node_modules .
+ WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/mapbox-gl-js")
+ endif()
+
+ # Add target for running submodule update during builds
+ add_custom_target(
+ update-submodules ALL
+ COMMAND git submodule update --init mapbox-gl-js
+ WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
+ COMMENT "Updating submodules..."
+ )
-# Add target for running submodule update during builds
-add_custom_target(
- update-submodules ALL
- COMMAND git submodule update --init mapbox-gl-js
- WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
- COMMENT "Updating submodules..."
-)
-
-# Run npm install for both directories, and add custom commands, and a target that depends on them.
-_npm_install("${CMAKE_SOURCE_DIR}" mapbox-gl-native update-submodules)
-_npm_install("${CMAKE_SOURCE_DIR}/mapbox-gl-js/test/integration" mapbox-gl-js "${CMAKE_SOURCE_DIR}/node_modules/.mapbox-gl-native.stamp")
-add_custom_target(
- npm-install ALL
- DEPENDS "${CMAKE_SOURCE_DIR}/node_modules/.mapbox-gl-js.stamp"
-)
+ # Run npm install for both directories, and add custom commands, and a target that depends on them.
+ _npm_install("${CMAKE_SOURCE_DIR}" mapbox-gl-native update-submodules)
+ _npm_install("${CMAKE_SOURCE_DIR}/mapbox-gl-js/test/integration" mapbox-gl-js "${CMAKE_SOURCE_DIR}/node_modules/.mapbox-gl-native.stamp")
+ add_custom_target(
+ npm-install ALL
+ DEPENDS "${CMAKE_SOURCE_DIR}/node_modules/.mapbox-gl-js.stamp"
+ )
+endif()
# Generate source groups so the files are properly sorted in IDEs like Xcode.
function(create_source_groups target)
@@ -112,6 +108,12 @@ function(_get_xcconfig_property target var)
endif()
endfunction()
+if(MBGL_PLATFORM STREQUAL "ios")
+ execute_process(
+ COMMAND git submodule update --init platform/ios/vendor/SMCalloutView platform/ios/uitest/KIF platform/ios/uitest/OHHTTPStubs
+ WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
+endif()
+
function(write_xcconfig_target_properties)
foreach(target ${ARGN})
_get_xcconfig_property(${target} INCLUDE_DIRECTORIES)
@@ -143,11 +145,14 @@ macro(initialize_xcode_cxx_build_settings target)
# -Wshadow
set_xcode_property(${target} GCC_WARN_SHADOW YES)
- # -Wno-unknown-pragmas
- set_xcode_property(${target} GCC_WARN_UNKNOWN_PRAGMAS YES)
-
# -Wnon-virtual-dtor
set_xcode_property(${target} GCC_WARN_NON_VIRTUAL_DESTRUCTOR YES)
+
+ # -Wnon-literal-conversion
+ set_xcode_property(${target} CLANG_WARN_NON_LITERAL_NULL_CONVERSION YES)
+
+ # -Wrange-loop-analysis
+ set_xcode_property(${target} CLANG_WARN_RANGE_LOOP_ANALYSIS YES)
endmacro(initialize_xcode_cxx_build_settings)
# CMake 3.1 does not have this yet.
diff --git a/cmake/node.cmake b/cmake/node.cmake
index c256b13f93..0c2f556b7d 100644
--- a/cmake/node.cmake
+++ b/cmake/node.cmake
@@ -13,6 +13,7 @@ set_target_properties("mbgl-node" PROPERTIES CXX_STANDARD 14)
target_sources(mbgl-node
PRIVATE platform/node/src/node_logging.hpp
PRIVATE platform/node/src/node_logging.cpp
+ PRIVATE platform/node/src/node_conversion.hpp
PRIVATE platform/node/src/node_map.hpp
PRIVATE platform/node/src/node_map.cpp
PRIVATE platform/node/src/node_request.hpp
@@ -21,14 +22,11 @@ target_sources(mbgl-node
PRIVATE platform/node/src/node_feature.cpp
PRIVATE platform/node/src/node_thread_pool.hpp
PRIVATE platform/node/src/node_thread_pool.cpp
+ PRIVATE platform/node/src/node_expression.hpp
+ PRIVATE platform/node/src/node_expression.cpp
PRIVATE platform/node/src/util/async_queue.hpp
)
-target_compile_options(mbgl-node
- PRIVATE -fPIC
- PRIVATE -fvisibility-inlines-hidden
-)
-
target_include_directories(mbgl-node
PRIVATE platform/default
)
@@ -93,6 +91,17 @@ xcode_create_scheme(
xcode_create_scheme(
TARGET mbgl-node
TYPE node
+ NAME "node expression tests"
+ ARGS
+ "platform/node/test/expression.test.js"
+ OPTIONAL_ARGS
+ "group"
+ "test"
+)
+
+xcode_create_scheme(
+ TARGET mbgl-node
+ TYPE node
NAME "node-benchmark"
ARGS
"platform/node/test/benchmark.js"
diff --git a/cmake/offline.cmake b/cmake/offline.cmake
index c9a3349792..13b6c3fe70 100644
--- a/cmake/offline.cmake
+++ b/cmake/offline.cmake
@@ -6,10 +6,6 @@ target_sources(mbgl-offline
PRIVATE platform/default/mbgl/util/default_styles.hpp
)
-target_compile_options(mbgl-offline
- PRIVATE -fvisibility-inlines-hidden
-)
-
target_include_directories(mbgl-offline
PRIVATE platform/default
)
@@ -19,7 +15,7 @@ target_link_libraries(mbgl-offline
)
target_add_mason_package(mbgl-offline PRIVATE boost)
-target_add_mason_package(mbgl-offline PRIVATE boost_libprogram_options)
+target_add_mason_package(mbgl-offline PRIVATE args)
mbgl_platform_offline()
diff --git a/cmake/render.cmake b/cmake/render.cmake
index f69aed16c0..c6e7d9dd59 100644
--- a/cmake/render.cmake
+++ b/cmake/render.cmake
@@ -2,10 +2,6 @@ add_executable(mbgl-render
bin/render.cpp
)
-target_compile_options(mbgl-render
- PRIVATE -fvisibility-inlines-hidden
-)
-
target_include_directories(mbgl-render
PRIVATE platform/default
)
@@ -15,7 +11,8 @@ target_link_libraries(mbgl-render
)
target_add_mason_package(mbgl-render PRIVATE boost)
-target_add_mason_package(mbgl-render PRIVATE boost_libprogram_options)
+target_add_mason_package(mbgl-render PRIVATE geojson)
+target_add_mason_package(mbgl-render PRIVATE args)
mbgl_platform_render()
diff --git a/cmake/test-files.cmake b/cmake/test-files.cmake
index dfd646e624..790198a55e 100644
--- a/cmake/test-files.cmake
+++ b/cmake/test-files.cmake
@@ -1,4 +1,4 @@
-# Do not edit. Regenerate this with ./scripts/generate-test-files.sh
+# This file is generated. Do not edit. Regenerate this with scripts/generate-cmake-files.js
set(MBGL_TEST_FILES
# actor
@@ -15,18 +15,20 @@ set(MBGL_TEST_FILES
# api
test/api/annotations.test.cpp
test/api/api_misuse.test.cpp
+ test/api/custom_geometry_source.test.cpp
test/api/custom_layer.test.cpp
test/api/query.test.cpp
test/api/recycle_map.cpp
test/api/zoom_history.cpp
+ # geometry
+ test/geometry/dem_data.test.cpp
+
# gl
test/gl/bucket.test.cpp
+ test/gl/context.test.cpp
test/gl/object.test.cpp
- # include/mbgl
- test/include/mbgl/test.hpp
-
# map
test/map/map.test.cpp
test/map/prefetch.test.cpp
@@ -50,24 +52,6 @@ set(MBGL_TEST_FILES
test/sprite/sprite_loader.test.cpp
test/sprite/sprite_parser.test.cpp
- # src/mbgl/test
- test/src/mbgl/test/conversion_stubs.hpp
- test/src/mbgl/test/fake_file_source.hpp
- test/src/mbgl/test/fixture_log_observer.cpp
- test/src/mbgl/test/fixture_log_observer.hpp
- test/src/mbgl/test/getrss.cpp
- test/src/mbgl/test/getrss.hpp
- test/src/mbgl/test/stub_file_source.cpp
- test/src/mbgl/test/stub_file_source.hpp
- test/src/mbgl/test/stub_geometry_tile_feature.hpp
- test/src/mbgl/test/stub_layer_observer.hpp
- test/src/mbgl/test/stub_render_source_observer.hpp
- test/src/mbgl/test/stub_style_observer.hpp
- test/src/mbgl/test/stub_tile_observer.hpp
- test/src/mbgl/test/test.cpp
- test/src/mbgl/test/util.cpp
- test/src/mbgl/test/util.hpp
-
# storage
test/storage/asset_file_source.test.cpp
test/storage/default_file_source.test.cpp
@@ -81,6 +65,15 @@ set(MBGL_TEST_FILES
test/storage/resource.test.cpp
test/storage/sqlite.test.cpp
+ # style
+ test/style/filter.test.cpp
+ test/style/properties.test.cpp
+ test/style/source.test.cpp
+ test/style/style.test.cpp
+ test/style/style_image.test.cpp
+ test/style/style_layer.test.cpp
+ test/style/style_parser.test.cpp
+
# style/conversion
test/style/conversion/function.test.cpp
test/style/conversion/geojson_options.test.cpp
@@ -89,8 +82,9 @@ set(MBGL_TEST_FILES
test/style/conversion/stringify.test.cpp
test/style/conversion/tileset.test.cpp
- # style
- test/style/filter.test.cpp
+ # style/expression
+ test/style/expression/expression.test.cpp
+ test/style/expression/util.test.cpp
# style/function
test/style/function/camera_function.test.cpp
@@ -99,23 +93,36 @@ set(MBGL_TEST_FILES
test/style/function/interval_stops.test.cpp
test/style/function/source_function.test.cpp
- # style
- test/style/properties.test.cpp
- test/style/source.test.cpp
- test/style/style.test.cpp
- test/style/style_image.test.cpp
- test/style/style_layer.test.cpp
- test/style/style_parser.test.cpp
+ # test
+ test/include/mbgl/test.hpp
+ test/src/mbgl/test/fake_file_source.hpp
+ test/src/mbgl/test/fixture_log_observer.cpp
+ test/src/mbgl/test/fixture_log_observer.hpp
+ test/src/mbgl/test/getrss.cpp
+ test/src/mbgl/test/getrss.hpp
+ test/src/mbgl/test/stub_file_source.cpp
+ test/src/mbgl/test/stub_file_source.hpp
+ test/src/mbgl/test/stub_geometry_tile_feature.hpp
+ test/src/mbgl/test/stub_layer_observer.hpp
+ test/src/mbgl/test/stub_render_source_observer.hpp
+ test/src/mbgl/test/stub_style_observer.hpp
+ test/src/mbgl/test/stub_tile_observer.hpp
+ test/src/mbgl/test/test.cpp
+ test/src/mbgl/test/util.cpp
+ test/src/mbgl/test/util.hpp
# text
- test/text/glyph_loader.test.cpp
+ test/text/cross_tile_symbol_index.test.cpp
+ test/text/glyph_manager.test.cpp
test/text/glyph_pbf.test.cpp
+ test/text/local_glyph_rasterizer.test.cpp
test/text/quads.test.cpp
# tile
- test/tile/annotation_tile.test.cpp
+ test/tile/custom_geometry_tile.test.cpp
test/tile/geojson_tile.test.cpp
test/tile/geometry_tile_data.test.cpp
+ test/tile/raster_dem_tile.test.cpp
test/tile/raster_tile.test.cpp
test/tile/tile_coordinate.test.cpp
test/tile/tile_id.test.cpp
@@ -125,6 +132,7 @@ set(MBGL_TEST_FILES
test/util/async_task.test.cpp
test/util/dtoa.test.cpp
test/util/geo.test.cpp
+ test/util/grid_index.test.cpp
test/util/http_timeout.test.cpp
test/util/image.test.cpp
test/util/mapbox.test.cpp
@@ -142,5 +150,7 @@ set(MBGL_TEST_FILES
test/util/tile_range.test.cpp
test/util/timer.test.cpp
test/util/token.test.cpp
+ test/util/unique_any.test.cpp
test/util/url.test.cpp
+
)
diff --git a/cmake/test.cmake b/cmake/test.cmake
index c821d53316..183263c5a9 100644
--- a/cmake/test.cmake
+++ b/cmake/test.cmake
@@ -8,10 +8,6 @@ else()
)
endif()
-target_compile_options(mbgl-test
- PRIVATE -fvisibility-inlines-hidden
-)
-
set_source_files_properties(test/src/mbgl/test/util.cpp PROPERTIES COMPILE_FLAGS -DNODE_EXECUTABLE="${NodeJS_EXECUTABLE}")
target_include_directories(mbgl-test
@@ -27,7 +23,6 @@ target_link_libraries(mbgl-test
target_add_mason_package(mbgl-test PRIVATE geometry)
target_add_mason_package(mbgl-test PRIVATE variant)
-target_add_mason_package(mbgl-test PRIVATE any)
target_add_mason_package(mbgl-test PRIVATE unique_resource)
target_add_mason_package(mbgl-test PRIVATE rapidjson)
target_add_mason_package(mbgl-test PRIVATE gtest)
diff --git a/deps/ninja/ninja-linux b/deps/ninja/ninja-linux
deleted file mode 100755
index e4a6202d99..0000000000
--- a/deps/ninja/ninja-linux
+++ /dev/null
Binary files differ
diff --git a/deps/ninja/ninja-macos b/deps/ninja/ninja-macos
deleted file mode 100755
index 64fcacc550..0000000000
--- a/deps/ninja/ninja-macos
+++ /dev/null
Binary files differ
diff --git a/deps/run_gyp b/deps/run_gyp
deleted file mode 100755
index f25d50a5dd..0000000000
--- a/deps/run_gyp
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/usr/bin/env python
-import sys
-import os.path
-sys.path.insert(1, os.path.join(os.path.dirname(sys.argv[0]), 'gyp', 'pylib'))
-import gyp
-
-if __name__ == '__main__':
- sys.exit(gyp.script_main())
diff --git a/docker/Dockerfile b/docker/Dockerfile
deleted file mode 100644
index 64f1ca3c87..0000000000
--- a/docker/Dockerfile
+++ /dev/null
@@ -1,16 +0,0 @@
-FROM ubuntu:12.04
-
-ENV DEBIAN_FRONTEND noninteractive
-
-# Add other APT sources and keys
-ADD *.list /etc/apt/sources.list.d/
-ADD *.gpg.key /tmp/
-RUN find /tmp/*.gpg.key | xargs -n1 apt-key add
-
-# Recreate Travis CI environment
-RUN apt-get update -y
-RUN apt-get install -y git-core python-pip curl automake libtool make cmake pkg-config python-pip \
- libc6 libstdc++6 zlib1g-dev libcurl4-openssl-dev libpng-dev libsqlite3-dev \
- xvfb libglu1-mesa-dev libxrandr-dev libxinerama-dev libxi-dev libxcursor-dev xutils-dev \
- mesa-utils libxxf86vm-dev x11proto-xf86vidmode-dev cmake && \
- pip install awscli
diff --git a/docker/bitrise/android/Dockerfile b/docker/bitrise/android/Dockerfile
deleted file mode 100644
index 264fa01b98..0000000000
--- a/docker/bitrise/android/Dockerfile
+++ /dev/null
@@ -1,21 +0,0 @@
-FROM bitriseio/android-ndk:latest
-
-# Install Google Cloud SDK for Firebase
-RUN set -eu && \
- (echo "deb http://packages.cloud.google.com/apt cloud-sdk-$(lsb_release -c -s) main" | sudo tee /etc/apt/sources.list.d/google-cloud-sdk.list) && \
- (curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -) && \
- sudo apt-get update && \
- sudo apt-get install -y google-cloud-sdk python-dev python-setuptools ccache && \
- sudo apt-get clean && \
- sudo easy_install -U pip && \
- pip install --no-cache-dir awscli && \
- mkdir -p "${ANDROID_HOME}/licenses" && \
- (echo "8933bad161af4178b1185d1a37fbf41ea5269c55" > "${ANDROID_HOME}/licenses/android-sdk-license")
-
-RUN set -eu && \
- git clone --depth=1 "https://github.com/mapbox/mapbox-gl-native.git" . && \
- ccache -z && \
- BUILDTYPE=Debug make android-test-lib-arm-v7 && \
- BUILDTYPE=Debug make android-checkstyle && \
- ccache -s && \
- cd .. && rm -rf src && mkdir src
diff --git a/docker/build.sh b/docker/build.sh
deleted file mode 100755
index c6eb1b110d..0000000000
--- a/docker/build.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/usr/bin/env bash
-
-#!/usr/bin/env bash
-
-set -e
-set -o pipefail
-
-docker build -t mapbox/gl-native:travis docker
diff --git a/docker/clang-tidy/Dockerfile b/docker/clang-tidy/Dockerfile
deleted file mode 100644
index c880391592..0000000000
--- a/docker/clang-tidy/Dockerfile
+++ /dev/null
@@ -1,9 +0,0 @@
-FROM mapbox/gl-native:travis
-
-# Install compiler
-RUN apt-get -y install clang-3.8 lldb-3.8 clang-tidy-3.8 clang-format-3.8
-
-RUN useradd -ms /bin/bash mapbox
-USER mapbox
-ENV HOME /home/mapbox
-WORKDIR /home/mapbox
diff --git a/docker/clang-tidy/run.sh b/docker/clang-tidy/run.sh
deleted file mode 100755
index 421aca9700..0000000000
--- a/docker/clang-tidy/run.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-set -o pipefail
-
-./docker/build.sh
-
-docker build -t mapbox/gl-native:clang-tidy docker/clang-tidy
-
-docker run \
- -i \
- -v `pwd`:/home/mapbox/build \
- -t mapbox/gl-native:clang-tidy \
- build/docker/clang-tidy/tidy.sh
diff --git a/docker/clang-tidy/tidy.sh b/docker/clang-tidy/tidy.sh
deleted file mode 100755
index 4c1b74595a..0000000000
--- a/docker/clang-tidy/tidy.sh
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/usr/bin/env bash
-
-# set -e
-# set -o pipefail
-
-export CXX=clang++-3.8
-export BUILDTYPE=Release
-
-cd build
-
-# before_install
-source ./scripts/travis_helper.sh
-
-# install
-./platform/linux/scripts/install.sh
-
-export CLANG_TIDY=clang-tidy-3.8
-make tidy
diff --git a/docker/george-edison55-precise-backports.gpg.key b/docker/george-edison55-precise-backports.gpg.key
deleted file mode 100644
index 69971cfce8..0000000000
--- a/docker/george-edison55-precise-backports.gpg.key
+++ /dev/null
@@ -1,13 +0,0 @@
------BEGIN PGP PUBLIC KEY BLOCK-----
-Version: SKS 1.1.5
-Comment: Hostname: keyserver.ubuntu.com
-
-mI0ETFYUegEEAMF062ZPYWxX0blpJpLCz/oEXwvxJoQg1Sz4uD4Fs1FCfRwHMlydwbGvEjEr
-QG39OXf1y1+lGVI73BLcuDd/n0yBLN9brycDspZKnQ25VaRB6sl8EDR8XM5tiA/diW1EIygS
-Ad/NuwXv236e+1E+zvik6faeoxygJbbj0KN67Hx7ABEBAAG0HUxhdW5jaHBhZCBHZW9yZ2Ug
-RWRpc29uJ3MgUFBBiLYEEwECACAFAkxWFHoCGwMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAK
-CRAITs/Fgoq3JpJ0A/9RquDizs5oUhyqqT4t4EHEIH9+ckl/3cQj0peN5APA5TOqAS4iVxic
-GO3YB7yY4a+v5qQUalOMNAQRUigi3IwCcOSs94Bt5f7lK6xbU7mIO2D5a7cUCjN36FXe+oWp
-4s5odz5+9OZrIVzJw/NOdgneLqh2ts5jWGYmOg7POJk9mQ==
-=ItbQ
------END PGP PUBLIC KEY BLOCK-----
diff --git a/docker/george-edison55-precise-backports.list b/docker/george-edison55-precise-backports.list
deleted file mode 100644
index 2bae784dd6..0000000000
--- a/docker/george-edison55-precise-backports.list
+++ /dev/null
@@ -1,2 +0,0 @@
-deb http://ppa.launchpad.net/george-edison55/precise-backports/ubuntu precise main
-deb-src http://ppa.launchpad.net/george-edison55/precise-backports/ubuntu precise main
diff --git a/docker/linux/Dockerfile b/docker/linux/Dockerfile
deleted file mode 100644
index c797082d93..0000000000
--- a/docker/linux/Dockerfile
+++ /dev/null
@@ -1,14 +0,0 @@
-FROM mapbox/gl-native:travis
-
-# Install compiler
-RUN apt-get -y install gdb g++-5 gcc-5 libllvm3.4
-
-RUN useradd -ms /bin/bash mapbox
-USER mapbox
-ENV HOME /home/mapbox
-WORKDIR /home/mapbox
-
-# Node
-RUN git clone https://github.com/creationix/nvm.git ~/.nvm && \
- . ~/.nvm/nvm.sh && \
- NVM_DIR=~/.nvm nvm install 4.4.5
diff --git a/docker/linux/run.sh b/docker/linux/run.sh
deleted file mode 100755
index 3d41843cf2..0000000000
--- a/docker/linux/run.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-set -o pipefail
-
-./docker/build.sh
-
-docker build -t mapbox/gl-native:linux docker/linux
-
-docker run \
- -i \
- -v `pwd`:/home/mapbox/build \
- -t mapbox/gl-native:linux \
- build/docker/linux/test.sh
diff --git a/docker/linux/test.sh b/docker/linux/test.sh
deleted file mode 100755
index 207f7679cf..0000000000
--- a/docker/linux/test.sh
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/usr/bin/env bash
-
-# set -e
-# set -o pipefail
-
-export _CXX=g++-5
-export _CC=gcc-5
-export BUILDTYPE=Release
-
-# Node
-. ~/.nvm/nvm.sh
-nvm use 4.4.5
-
-# Xvfb
-start-stop-daemon --start --pidfile ~/xvfb.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -screen 0 1024x768x24 -ac +extension GLX +render -noreset
-
-cd build
-
-# before_install
-source ./scripts/travis_helper.sh
-source ./scripts/travis_setup.sh
-
-make linux
-make test
diff --git a/docker/ubuntu-toolchain-r.gpg.key b/docker/ubuntu-toolchain-r.gpg.key
deleted file mode 100644
index 854eef8520..0000000000
--- a/docker/ubuntu-toolchain-r.gpg.key
+++ /dev/null
@@ -1,13 +0,0 @@
------BEGIN PGP PUBLIC KEY BLOCK-----
-Version: SKS 1.1.5
-Comment: Hostname: keyserver.ubuntu.com
-
-mI0ESuBvRwEEAMi4cDba7xlKaaoXjO1n1HX8RKrkW+HEIl79nSOSJyvzysajs7zUow/OzCQp
-9NswqrDmNuH1+lPTTRNAGtK8r2ouq2rnXT1mTl23dpgHZ9spseR73s4ZBGw/ag4bpU5dNUSt
-vfmHhIjVCuiSpNn7cyy1JSSvSs3N2mxteKjXLBf7ABEBAAG0GkxhdW5jaHBhZCBUb29sY2hh
-aW4gYnVpbGRziLYEEwECACAFAkrgb0cCGwMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAKCRAe
-k3eiup7yfzGKA/4xzUqNACSlB+k+DxFFHqkwKa/ziFiAlkLQyyhm+iqz80htRZr7Ls/ZRYZl
-0aSU56/hLe0V+TviJ1s8qdN2lamkKdXIAFfavA04nOnTzyIBJ82EAUT3Nh45skMxo4z4iZMN
-msyaQpNl/m/lNtOLhR64v5ZybofB2EWkMxUzX8D/FQ==
-=LcUQ
------END PGP PUBLIC KEY BLOCK----- \ No newline at end of file
diff --git a/docker/ubuntu-toolchain-r.list b/docker/ubuntu-toolchain-r.list
deleted file mode 100644
index 741f4528e2..0000000000
--- a/docker/ubuntu-toolchain-r.list
+++ /dev/null
@@ -1,2 +0,0 @@
-deb http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu precise main
-deb-src http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu precise main \ No newline at end of file
diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md
deleted file mode 100644
index 9832b5da0a..0000000000
--- a/docs/TROUBLESHOOTING.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# Troubleshooting
-
-To trigger a complete rebuild, run `make clean`.
-
-If you are having trouble getting the dependencies right, you can blow away the `mason_packages` directory, or run `make distclean`. This means the `Makefile` and configure script will automatically install the dependencies again on the next try.
diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp
index c5f90d99e1..5ba23a76dd 100644
--- a/include/mbgl/map/map.hpp
+++ b/include/mbgl/map/map.hpp
@@ -127,6 +127,14 @@ public:
void setViewportMode(ViewportMode);
ViewportMode getViewportMode() const;
+ // Projection mode
+ void setAxonometric(bool);
+ bool getAxonometric() const;
+ void setXSkew(double ySkew);
+ double getXSkew() const;
+ void setYSkew(double ySkew);
+ double getYSkew() const;
+
// Size
void setSize(Size);
Size getSize() const;
diff --git a/include/mbgl/map/mode.hpp b/include/mbgl/map/mode.hpp
index 05de2df22c..4ee289d855 100644
--- a/include/mbgl/map/mode.hpp
+++ b/include/mbgl/map/mode.hpp
@@ -11,16 +11,8 @@ using EnumType = uint32_t;
enum class MapMode : EnumType {
Continuous, // continually updating map
- Still, // a once-off still image
-};
-
-// We can avoid redundant GL calls when it is known that the GL context is not
-// being shared. In a shared GL context case, we need to make sure that the
-// correct GL configurations are in use - they might have changed between render
-// calls.
-enum class GLContextMode : EnumType {
- Unique,
- Shared,
+ Static, // a once-off still image of an arbitrary viewport
+ Tile // a once-off still image of a single tile
};
// We can choose to constrain the map both horizontally or vertically, or only
diff --git a/include/mbgl/renderer/mode.hpp b/include/mbgl/renderer/mode.hpp
new file mode 100644
index 0000000000..6ff42d8058
--- /dev/null
+++ b/include/mbgl/renderer/mode.hpp
@@ -0,0 +1,18 @@
+#pragma once
+
+#include <cstdint>
+
+namespace mbgl {
+
+using EnumType = uint32_t;
+
+// We can avoid redundant GL calls when it is known that the GL context is not
+// being shared. In a shared GL context case, we need to make sure that the
+// correct GL configurations are in use - they might have changed between render
+// calls.
+enum class GLContextMode : EnumType {
+ Unique,
+ Shared,
+};
+
+} // namespace mbgl
diff --git a/include/mbgl/renderer/renderer.hpp b/include/mbgl/renderer/renderer.hpp
index a60f3f6e4d..798928087a 100644
--- a/include/mbgl/renderer/renderer.hpp
+++ b/include/mbgl/renderer/renderer.hpp
@@ -1,7 +1,7 @@
#pragma once
-#include <mbgl/map/mode.hpp>
#include <mbgl/renderer/query.hpp>
+#include <mbgl/renderer/mode.hpp>
#include <mbgl/annotation/annotation.hpp>
#include <mbgl/util/geo.hpp>
#include <mbgl/util/geo.hpp>
@@ -48,7 +48,7 @@ public:
void dumpDebugLogs();
// Memory
- void onLowMemory();
+ void reduceMemoryUse();
private:
class Impl;
diff --git a/include/mbgl/renderer/renderer_backend.hpp b/include/mbgl/renderer/renderer_backend.hpp
index 295838c71b..b83c128169 100644
--- a/include/mbgl/renderer/renderer_backend.hpp
+++ b/include/mbgl/renderer/renderer_backend.hpp
@@ -41,7 +41,7 @@ protected:
// Called with the name of an OpenGL extension that should be loaded. RendererBackend implementations
// must call the API-specific version that obtains the function pointer for this function,
// or a null pointer if unsupported/unavailable.
- virtual gl::ProcAddress initializeExtension(const char*) = 0;
+ virtual gl::ProcAddress getExtensionFunctionPointer(const char*) = 0;
// Called when the backend's GL context needs to be made active or inactive. These are called,
// as a matched pair, exclusively through BackendScope, in two situations:
diff --git a/src/mbgl/renderer/renderer_observer.hpp b/include/mbgl/renderer/renderer_observer.hpp
index 551b5c803e..551b5c803e 100644
--- a/src/mbgl/renderer/renderer_observer.hpp
+++ b/include/mbgl/renderer/renderer_observer.hpp
diff --git a/include/mbgl/storage/offline.hpp b/include/mbgl/storage/offline.hpp
index 117dd0591b..ef4a499e83 100644
--- a/include/mbgl/storage/offline.hpp
+++ b/include/mbgl/storage/offline.hpp
@@ -30,15 +30,15 @@ public:
OfflineTilePyramidRegionDefinition(std::string, LatLngBounds, double, double, float);
/* Private */
- std::vector<CanonicalTileID> tileCover(SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const;
- uint64_t tileCount(SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const;
+ std::vector<CanonicalTileID> tileCover(style::SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const;
+ uint64_t tileCount(style::SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const;
const std::string styleURL;
const LatLngBounds bounds;
const double minZoom;
const double maxZoom;
const float pixelRatio;
private:
- Range<uint8_t> coveringZoomRange(SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const;
+ Range<uint8_t> coveringZoomRange(style::SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const;
};
/*
diff --git a/include/mbgl/style/conversion.hpp b/include/mbgl/style/conversion.hpp
index 27504a89b1..71c2cec237 100644
--- a/include/mbgl/style/conversion.hpp
+++ b/include/mbgl/style/conversion.hpp
@@ -1,6 +1,8 @@
#pragma once
#include <mbgl/util/optional.hpp>
+#include <mbgl/util/feature.hpp>
+#include <mbgl/util/geojson.hpp>
#include <string>
@@ -9,9 +11,8 @@ namespace style {
namespace conversion {
/*
- The `conversion` namespace defines conversions from a templated type `V` representing a JSON
- object conforming to the schema defined by the Mapbox Style Specification, to the various C++
- types that form the C++ model of that domain:
+ The `conversion` namespace defines conversions from JSON structures conforming to the schema defined by
+ the Mapbox Style Specification, to the various C++ types that form the C++ model of that domain:
* `std::unique_ptr<Source>`
* `std::unique_ptr<Layer>`
@@ -20,15 +21,31 @@ namespace conversion {
A single template function serves as the public interface:
- template <class T, class V>
- optional<T> convert(const V& value, Error& error);
+ template <class T>
+ optional<T> convert(const Convertible& input, Error& error);
Where `T` is one of the above types. If the conversion fails, the result is empty, and the
error parameter includes diagnostic text suitable for presentation to a library user. Otherwise,
a filled optional is returned.
- The implementation of `convert` requires that the following are legal expressions for a value `v`
- of type `const V&`:
+ `Convertible` is a type that encapsulates a special form of polymorphism over various underlying types that
+ can serve as input to the conversion algorithm. For instance, on macOS, we need to support
+ conversion from both RapidJSON types, and a JSON structure represented with `NSArray`/`NSDictionary`/etc.
+ On Qt, we need to support conversion from RapidJSON types and QVariant.
+
+ We don't want to use traditional forms of polymorphism to accomplish this:
+
+ * Compile time polymorphism using a template parameter for the actual value type leads to
+ excessive code bloat and long compile times.
+ * Runtime polymorphism using virtual methods requires extra heap allocation and ubiquitous
+ use of std::unique_ptr, unsuitable for this performance-sensitive code.
+
+ Therefore, we're using a custom implementation of runtime polymorphism where we manually create and
+ dispatch through a table of function pointers (vtable), while keeping the storage for any of the possible
+ underlying types inline on the stack, using `std::aligned_storage`.
+
+ For a given underlying type T, an explicit specialization of `ConversionTraits<T>` must be provided. This
+ specialization must provide the following static methods:
* `isUndefined(v)` -- returns a boolean indication whether `v` is undefined or a JSON null
@@ -48,21 +65,239 @@ namespace conversion {
* `toNumber(v)` -- returns `optional<float>`, absence indicating `v` is not a JSON number
* `toDouble(v)` -- returns `optional<double>`, absence indicating `v` is not a JSON number
* `toString(v)` -- returns `optional<std::string>`, absence indicating `v` is not a JSON string
- * `toValue(v)` -- returns `optional<mbgl::Value>`, a variant type, for generic conversion,
+ * `toValue(v)` -- returns `optional<Value>`, a variant type, for generic conversion,
absence indicating `v` is not a boolean, number, or string. Numbers should be converted to
unsigned integer, signed integer, or floating point, in descending preference.
- The mbgl core implements these requirements for RapidJSON types, and the node bindings implement
- them for v8 types.
+ In addition, the type T must be move-constructable. And finally, `Convertible::Storage`, a typedef for
+ `std::aligned_storage_t`, must be large enough to satisfy the memory requirements for any of the
+ possible underlying types. (A static assert will fail if this is not the case.)
+
+ `Convertible` itself is movable, but not copyable. A moved-from `Convertible` is in an invalid state;
+ you must not do anything with it except let it go out of scope.
*/
struct Error { std::string message; };
+template <typename T>
+class ConversionTraits;
+
+class Convertible {
+public:
+ template <typename T>
+ Convertible(T&& value) : vtable(vtableForType<std::decay_t<T>>()) {
+ static_assert(sizeof(Storage) >= sizeof(std::decay_t<T>), "Storage must be large enough to hold value type");
+ new (static_cast<void*>(&storage)) std::decay_t<T>(std::forward<T>(value));
+ }
+
+ Convertible(Convertible&& v)
+ : vtable(v.vtable)
+ {
+ if (vtable) {
+ vtable->move(std::move(v.storage), this->storage);
+ }
+ }
+
+ ~Convertible() {
+ if (vtable) {
+ vtable->destroy(storage);
+ }
+ }
+
+ Convertible& operator=(Convertible&& v) {
+ if (vtable) {
+ vtable->destroy(storage);
+ }
+ vtable = v.vtable;
+ if (vtable) {
+ vtable->move(std::move(v.storage), this->storage);
+ }
+ v.vtable = nullptr;
+ return *this;
+ }
+
+ Convertible() = delete;
+ Convertible(const Convertible&) = delete;
+ Convertible& operator=(const Convertible&) = delete;
+
+ friend inline bool isUndefined(const Convertible& v) {
+ assert(v.vtable);
+ return v.vtable->isUndefined(v.storage);
+ }
+
+ friend inline bool isArray(const Convertible& v) {
+ assert(v.vtable);
+ return v.vtable->isArray(v.storage);
+ }
+
+ friend inline std::size_t arrayLength(const Convertible& v) {
+ assert(v.vtable);
+ return v.vtable->arrayLength(v.storage);
+ }
+
+ friend inline Convertible arrayMember(const Convertible& v, std::size_t i) {
+ assert(v.vtable);
+ return v.vtable->arrayMember(v.storage, i);
+ }
+
+ friend inline bool isObject(const Convertible& v) {
+ assert(v.vtable);
+ return v.vtable->isObject(v.storage);
+ }
+
+ friend inline optional<Convertible> objectMember(const Convertible& v, const char * name) {
+ assert(v.vtable);
+ return v.vtable->objectMember(v.storage, name);
+ }
+
+ friend inline optional<Error> eachMember(const Convertible& v, const std::function<optional<Error> (const std::string&, const Convertible&)>& fn) {
+ assert(v.vtable);
+ return v.vtable->eachMember(v.storage, fn);
+ }
+
+ friend inline optional<bool> toBool(const Convertible& v) {
+ assert(v.vtable);
+ return v.vtable->toBool(v.storage);
+ }
+
+ friend inline optional<float> toNumber(const Convertible& v) {
+ assert(v.vtable);
+ return v.vtable->toNumber(v.storage);
+ }
+
+ friend inline optional<double> toDouble(const Convertible& v) {
+ assert(v.vtable);
+ return v.vtable->toDouble(v.storage);
+ }
+
+ friend inline optional<std::string> toString(const Convertible& v) {
+ assert(v.vtable);
+ return v.vtable->toString(v.storage);
+ }
+
+ friend inline optional<Value> toValue(const Convertible& v) {
+ assert(v.vtable);
+ return v.vtable->toValue(v.storage);
+ }
+
+ friend inline optional<GeoJSON> toGeoJSON(const Convertible& v, Error& error) {
+ assert(v.vtable);
+ return v.vtable->toGeoJSON(v.storage, error);
+ }
+
+private:
+#if __ANDROID__
+ // Android: JSValue* or mbgl::android::Value
+ using Storage = std::aligned_storage_t<32, 8>;
+#elif __QT__
+ // Qt: JSValue* or QVariant
+ using Storage = std::aligned_storage_t<32, 8>;
+#else
+ // Node: JSValue* or v8::Local<v8::Value>
+ // iOS/macOS: JSValue* or id
+ using Storage = std::aligned_storage_t<8, 8>;
+#endif
+
+ struct VTable {
+ void (*move) (Storage&& src, Storage& dest);
+ void (*destroy) (Storage&);
+
+ bool (*isUndefined) (const Storage&);
+
+ bool (*isArray) (const Storage&);
+ std::size_t (*arrayLength) (const Storage&);
+ Convertible (*arrayMember) (const Storage&, std::size_t);
+
+ bool (*isObject) (const Storage&);
+ optional<Convertible> (*objectMember) (const Storage&, const char *);
+ optional<Error> (*eachMember) (const Storage&, const std::function<optional<Error> (const std::string&, const Convertible&)>&);
+
+ optional<bool> (*toBool) (const Storage&);
+ optional<float> (*toNumber) (const Storage&);
+ optional<double> (*toDouble) (const Storage&);
+ optional<std::string> (*toString) (const Storage&);
+ optional<Value> (*toValue) (const Storage&);
+
+ // https://github.com/mapbox/mapbox-gl-native/issues/5623
+ optional<GeoJSON> (*toGeoJSON) (const Storage&, Error&);
+ };
+
+ // Extracted this function from the table below to work around a GCC bug with differing
+ // visibility settings for capturing lambdas: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80947
+ template <typename T>
+ static auto vtableEachMember(const Storage& s, const std::function<optional<Error>(const std::string&, const Convertible&)>& fn) {
+ return ConversionTraits<T>::eachMember(reinterpret_cast<const T&>(s), [&](const std::string& k, T&& v) {
+ return fn(k, Convertible(std::move(v)));
+ });
+ }
+
+ template <typename T>
+ static VTable* vtableForType() {
+ using Traits = ConversionTraits<T>;
+ static VTable vtable = {
+ [] (Storage&& src, Storage& dest) {
+ auto srcValue = reinterpret_cast<T&&>(src);
+ new (static_cast<void*>(&dest)) T(std::move(srcValue));
+ srcValue.~T();
+ },
+ [] (Storage& s) {
+ reinterpret_cast<T&>(s).~T();
+ },
+ [] (const Storage& s) {
+ return Traits::isUndefined(reinterpret_cast<const T&>(s));
+ },
+ [] (const Storage& s) {
+ return Traits::isArray(reinterpret_cast<const T&>(s));
+ },
+ [] (const Storage& s) {
+ return Traits::arrayLength(reinterpret_cast<const T&>(s));
+ },
+ [] (const Storage& s, std::size_t i) {
+ return Convertible(Traits::arrayMember(reinterpret_cast<const T&>(s), i));
+ },
+ [] (const Storage& s) {
+ return Traits::isObject(reinterpret_cast<const T&>(s));
+ },
+ [] (const Storage& s, const char * key) {
+ optional<T> member = Traits::objectMember(reinterpret_cast<const T&>(s), key);
+ if (member) {
+ return optional<Convertible>(Convertible(std::move(*member)));
+ } else {
+ return optional<Convertible>();
+ }
+ },
+ vtableEachMember<T>,
+ [] (const Storage& s) {
+ return Traits::toBool(reinterpret_cast<const T&>(s));
+ },
+ [] (const Storage& s) {
+ return Traits::toNumber(reinterpret_cast<const T&>(s));
+ },
+ [] (const Storage& s) {
+ return Traits::toDouble(reinterpret_cast<const T&>(s));
+ },
+ [] (const Storage& s) {
+ return Traits::toString(reinterpret_cast<const T&>(s));
+ },
+ [] (const Storage& s) {
+ return Traits::toValue(reinterpret_cast<const T&>(s));
+ },
+ [] (const Storage& s, Error& err) {
+ return Traits::toGeoJSON(reinterpret_cast<const T&>(s), err);
+ }
+ };
+ return &vtable;
+ }
+
+ VTable* vtable;
+ Storage storage;
+};
+
template <class T, class Enable = void>
struct Converter;
-template <class T, class V, class...Args>
-optional<T> convert(const V& value, Error& error, Args&&...args) {
+template <class T, class...Args>
+optional<T> convert(const Convertible& value, Error& error, Args&&...args) {
return Converter<T>()(value, error, std::forward<Args>(args)...);
}
diff --git a/include/mbgl/style/conversion/constant.hpp b/include/mbgl/style/conversion/constant.hpp
index 07c0a35fae..7b3249da52 100644
--- a/include/mbgl/style/conversion/constant.hpp
+++ b/include/mbgl/style/conversion/constant.hpp
@@ -1,7 +1,6 @@
#pragma once
#include <mbgl/style/conversion.hpp>
-#include <mbgl/util/optional.hpp>
#include <mbgl/util/color.hpp>
#include <mbgl/util/enum.hpp>
#include <mbgl/util/string.hpp>
@@ -16,47 +15,22 @@ namespace conversion {
template <>
struct Converter<bool> {
- template <class V>
- optional<bool> operator()(const V& value, Error& error) const {
- optional<bool> converted = toBool(value);
- if (!converted) {
- error = { "value must be a boolean" };
- return {};
- }
- return *converted;
- }
+ optional<bool> operator()(const Convertible& value, Error& error) const;
};
template <>
struct Converter<float> {
- template <class V>
- optional<float> operator()(const V& value, Error& error) const {
- optional<float> converted = toNumber(value);
- if (!converted) {
- error = { "value must be a number" };
- return {};
- }
- return *converted;
- }
+ optional<float> operator()(const Convertible& value, Error& error) const;
};
template <>
struct Converter<std::string> {
- template <class V>
- optional<std::string> operator()(const V& value, Error& error) const {
- optional<std::string> converted = toString(value);
- if (!converted) {
- error = { "value must be a string" };
- return {};
- }
- return *converted;
- }
+ optional<std::string> operator()(const Convertible& value, Error& error) const;
};
template <class T>
struct Converter<T, typename std::enable_if_t<std::is_enum<T>::value>> {
- template <class V>
- optional<T> operator()(const V& value, Error& error) const {
+ optional<T> operator()(const Convertible& value, Error& error) const {
optional<std::string> string = toString(value);
if (!string) {
error = { "value must be a string" };
@@ -75,28 +49,12 @@ struct Converter<T, typename std::enable_if_t<std::is_enum<T>::value>> {
template <>
struct Converter<Color> {
- template <class V>
- optional<Color> operator()(const V& value, Error& error) const {
- optional<std::string> string = toString(value);
- if (!string) {
- error = { "value must be a string" };
- return {};
- }
-
- optional<Color> color = Color::parse(*string);
- if (!color) {
- error = { "value must be a valid color" };
- return {};
- }
-
- return *color;
- }
+ optional<Color> operator()(const Convertible& value, Error& error) const;
};
template <size_t N>
struct Converter<std::array<float, N>> {
- template <class V>
- optional<std::array<float, N>> operator()(const V& value, Error& error) const {
+ optional<std::array<float, N>> operator()(const Convertible& value, Error& error) const {
if (!isArray(value) || arrayLength(value) != N) {
error = { "value must be an array of " + util::toString(N) + " numbers" };
return {};
@@ -117,52 +75,12 @@ struct Converter<std::array<float, N>> {
template <>
struct Converter<std::vector<float>> {
- template <class V>
- optional<std::vector<float>> operator()(const V& value, Error& error) const {
- if (!isArray(value)) {
- error = { "value must be an array" };
- return {};
- }
-
- std::vector<float> result;
- result.reserve(arrayLength(value));
-
- for (std::size_t i = 0; i < arrayLength(value); ++i) {
- optional<float> number = toNumber(arrayMember(value, i));
- if (!number) {
- error = { "value must be an array of numbers" };
- return {};
- }
- result.push_back(*number);
- }
-
- return result;
- }
+ optional<std::vector<float>> operator()(const Convertible& value, Error& error) const;
};
template <>
struct Converter<std::vector<std::string>> {
- template <class V>
- optional<std::vector<std::string>> operator()(const V& value, Error& error) const {
- if (!isArray(value)) {
- error = { "value must be an array" };
- return {};
- }
-
- std::vector<std::string> result;
- result.reserve(arrayLength(value));
-
- for (std::size_t i = 0; i < arrayLength(value); ++i) {
- optional<std::string> string = toString(arrayMember(value, i));
- if (!string) {
- error = { "value must be an array of strings" };
- return {};
- }
- result.push_back(*string);
- }
-
- return result;
- }
+ optional<std::vector<std::string>> operator()(const Convertible& value, Error& error) const;
};
} // namespace conversion
diff --git a/include/mbgl/style/conversion/coordinate.hpp b/include/mbgl/style/conversion/coordinate.hpp
index 732624e77f..e11db5e32f 100644
--- a/include/mbgl/style/conversion/coordinate.hpp
+++ b/include/mbgl/style/conversion/coordinate.hpp
@@ -10,26 +10,7 @@ namespace conversion {
template<>
struct Converter<LatLng> {
public:
- template <class V>
- optional<LatLng> operator() (const V& value, Error& error) const {
- if (!isArray(value) || arrayLength(value) < 2 ) {
- error = { "coordinate array must contain numeric longitude and latitude values" };
- return {};
- }
- //Style spec uses GeoJSON convention for specifying coordinates
- optional<double> latitude = toDouble(arrayMember(value, 1));
- optional<double> longitude = toDouble(arrayMember(value, 0));
-
- if (!latitude || !longitude) {
- error = { "coordinate array must contain numeric longitude and latitude values" };
- return {};
- }
- if (*latitude < -90 || *latitude > 90 ){
- error = { "coordinate latitude must be between -90 and 90" };
- return {};
- }
- return LatLng(*latitude, *longitude);
- }
+ optional<LatLng> operator() (const Convertible& value, Error& error) const;
};
} // namespace conversion
diff --git a/include/mbgl/style/conversion/custom_geometry_source_options.hpp b/include/mbgl/style/conversion/custom_geometry_source_options.hpp
new file mode 100644
index 0000000000..dedecd1aa4
--- /dev/null
+++ b/include/mbgl/style/conversion/custom_geometry_source_options.hpp
@@ -0,0 +1,84 @@
+#pragma once
+
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/sources/custom_geometry_source.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+template <>
+struct Converter<CustomGeometrySource::Options> {
+
+ template <class V>
+ optional<CustomGeometrySource::Options> operator()(const V& value, Error& error) const {
+ CustomGeometrySource::Options options;
+
+ const auto minzoomValue = objectMember(value, "minzoom");
+ if (minzoomValue) {
+ if (toNumber(*minzoomValue)) {
+ options.zoomRange.min = static_cast<uint8_t>(*toNumber(*minzoomValue));
+ } else {
+ error = { "GeoJSON source minzoom value must be a number" };
+ return {};
+ }
+ }
+
+ const auto maxzoomValue = objectMember(value, "maxzoom");
+ if (maxzoomValue) {
+ if (toNumber(*maxzoomValue)) {
+ options.zoomRange.max = static_cast<uint8_t>(*toNumber(*maxzoomValue));
+ } else {
+ error = { "GeoJSON source maxzoom value must be a number" };
+ return {};
+ }
+ }
+
+ const auto bufferValue = objectMember(value, "buffer");
+ if (bufferValue) {
+ if (toNumber(*bufferValue)) {
+ options.tileOptions.buffer = static_cast<uint16_t>(*toNumber(*bufferValue));
+ } else {
+ error = { "GeoJSON source buffer value must be a number" };
+ return {};
+ }
+ }
+
+ const auto toleranceValue = objectMember(value, "tolerance");
+ if (toleranceValue) {
+ if (toNumber(*toleranceValue)) {
+ options.tileOptions.tolerance = static_cast<double>(*toNumber(*toleranceValue));
+ } else {
+ error = { "GeoJSON source tolerance value must be a number" };
+ return {};
+ }
+ }
+
+ const auto wrapValue = objectMember(value, "wrap");
+ if (wrapValue) {
+ if (toBool(*wrapValue)) {
+ options.tileOptions.wrap = static_cast<bool>(*toBool(*wrapValue));
+ } else {
+ error = { "CustomGeometrySource TileOptions wrap value must be a boolean" };
+ return {};
+ }
+ }
+
+ const auto clipValue = objectMember(value, "clip");
+ if (clipValue) {
+ if (toBool(*clipValue)) {
+ options.tileOptions.clip = static_cast<double>(*toBool(*clipValue));
+ } else {
+ error = { "CustomGeometrySource TileOptiosn clip value must be a boolean" };
+ return {};
+ }
+ }
+
+ return { options };
+ }
+
+};
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/conversion/data_driven_property_value.hpp b/include/mbgl/style/conversion/data_driven_property_value.hpp
index 79b15dcfb0..8880d28fb1 100644
--- a/include/mbgl/style/conversion/data_driven_property_value.hpp
+++ b/include/mbgl/style/conversion/data_driven_property_value.hpp
@@ -4,6 +4,13 @@
#include <mbgl/style/conversion.hpp>
#include <mbgl/style/conversion/constant.hpp>
#include <mbgl/style/conversion/function.hpp>
+#include <mbgl/style/conversion/expression.hpp>
+#include <mbgl/style/expression/is_expression.hpp>
+#include <mbgl/style/expression/is_constant.hpp>
+#include <mbgl/style/expression/find_zoom_curve.hpp>
+
+#include <unordered_set>
+
namespace mbgl {
namespace style {
@@ -11,10 +18,27 @@ namespace conversion {
template <class T>
struct Converter<DataDrivenPropertyValue<T>> {
- template <class V>
- optional<DataDrivenPropertyValue<T>> operator()(const V& value, Error& error) const {
+
+ optional<DataDrivenPropertyValue<T>> operator()(const Convertible& value, Error& error) const {
if (isUndefined(value)) {
return DataDrivenPropertyValue<T>();
+ } else if (expression::isExpression(value)) {
+ optional<std::unique_ptr<Expression>> expression = convert<std::unique_ptr<Expression>>(
+ value,
+ error,
+ valueTypeToExpressionType<T>());
+
+ if (!expression) {
+ return {};
+ }
+
+ if (isFeatureConstant(**expression)) {
+ return DataDrivenPropertyValue<T>(CameraFunction<T>(std::move(*expression)));
+ } else if (isZoomConstant(**expression)) {
+ return DataDrivenPropertyValue<T>(SourceFunction<T>(std::move(*expression)));
+ } else {
+ return DataDrivenPropertyValue<T>(CompositeFunction<T>(std::move(*expression)));
+ }
} else if (!isObject(value)) {
optional<T> constant = convert<T>(value, error);
if (!constant) {
diff --git a/include/mbgl/style/conversion/expression.hpp b/include/mbgl/style/conversion/expression.hpp
new file mode 100644
index 0000000000..c5fcf906a7
--- /dev/null
+++ b/include/mbgl/style/conversion/expression.hpp
@@ -0,0 +1,39 @@
+#pragma once
+
+#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/style/expression/type.hpp>
+#include <mbgl/style/conversion.hpp>
+
+#include <memory>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+using namespace mbgl::style::expression;
+
+template<> struct Converter<std::unique_ptr<Expression>> {
+ optional<std::unique_ptr<Expression>> operator()(const Convertible& value, Error& error, type::Type expected) const {
+ ParsingContext ctx(optional<type::Type> {expected});
+ ParseResult parsed = ctx.parse(value);
+ if (parsed) {
+ return std::move(*parsed);
+ }
+ std::string combinedError;
+ for (const ParsingError& parsingError : ctx.getErrors()) {
+ if (combinedError.size() > 0) {
+ combinedError += "\n";
+ }
+ if (parsingError.key.size() > 0) {
+ combinedError += parsingError.key + ": ";
+ }
+ combinedError += parsingError.message;
+ }
+ error = { combinedError };
+ return {};
+ };
+};
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/conversion/filter.hpp b/include/mbgl/style/conversion/filter.hpp
index 986d1bf80d..9daf6ea7a4 100644
--- a/include/mbgl/style/conversion/filter.hpp
+++ b/include/mbgl/style/conversion/filter.hpp
@@ -2,7 +2,6 @@
#include <mbgl/style/filter.hpp>
#include <mbgl/style/conversion.hpp>
-#include <mbgl/util/geometry.hpp>
namespace mbgl {
namespace style {
@@ -11,247 +10,7 @@ namespace conversion {
template <>
struct Converter<Filter> {
public:
- template <class V>
- optional<Filter> operator()(const V& value, Error& error) const {
- if (!isArray(value)) {
- error = { "filter expression must be an array" };
- return {};
- }
-
- if (arrayLength(value) < 1) {
- error = { "filter expression must have at least 1 element" };
- return {};
- }
-
- optional<std::string> op = toString(arrayMember(value, 0));
- if (!op) {
- error = { "filter operator must be a string" };
- return {};
- }
-
- if (*op == "==") {
- return convertEqualityFilter<EqualsFilter, TypeEqualsFilter, IdentifierEqualsFilter>(value, error);
- } else if (*op == "!=") {
- return convertEqualityFilter<NotEqualsFilter, TypeNotEqualsFilter, IdentifierNotEqualsFilter>(value, error);
- } else if (*op == ">") {
- return convertBinaryFilter<GreaterThanFilter>(value, error);
- } else if (*op == ">=") {
- return convertBinaryFilter<GreaterThanEqualsFilter>(value, error);
- } else if (*op == "<") {
- return convertBinaryFilter<LessThanFilter>(value, error);
- } else if (*op == "<=") {
- return convertBinaryFilter<LessThanEqualsFilter>(value, error);
- } else if (*op == "in") {
- return convertSetFilter<InFilter, TypeInFilter, IdentifierInFilter>(value, error);
- } else if (*op == "!in") {
- return convertSetFilter<NotInFilter, TypeNotInFilter, IdentifierNotInFilter>(value, error);
- } else if (*op == "all") {
- return convertCompoundFilter<AllFilter>(value, error);
- } else if (*op == "any") {
- return convertCompoundFilter<AnyFilter>(value, error);
- } else if (*op == "none") {
- return convertCompoundFilter<NoneFilter>(value, error);
- } else if (*op == "has") {
- return convertUnaryFilter<HasFilter, HasIdentifierFilter>(value, error);
- } else if (*op == "!has") {
- return convertUnaryFilter<NotHasFilter, NotHasIdentifierFilter>(value, error);
- }
-
- error = { R"(filter operator must be one of "==", "!=", ">", ">=", "<", "<=", "in", "!in", "all", "any", "none", "has", or "!has")" };
- return {};
- }
-
-private:
- optional<Value> normalizeValue(const optional<Value>& value, Error& error) const {
- if (!value) {
- error = { "filter expression value must be a boolean, number, or string" };
- return {};
- } else {
- return *value;
- }
- }
-
- template <class V>
- optional<FeatureType> toFeatureType(const V& value, Error& error) const {
- optional<std::string> type = toString(value);
- if (!type) {
- error = { "value for $type filter must be a string" };
- return {};
- } else if (*type == "Point") {
- return FeatureType::Point;
- } else if (*type == "LineString") {
- return FeatureType::LineString;
- } else if (*type == "Polygon") {
- return FeatureType::Polygon;
- } else {
- error = { "value for $type filter must be Point, LineString, or Polygon" };
- return {};
- }
- }
-
- template <class V>
- optional<FeatureIdentifier> toFeatureIdentifier(const V& value, Error& error) const {
- optional<Value> identifier = toValue(value);
- if (!identifier) {
- error = { "filter expression value must be a boolean, number, or string" };
- return {};
- } else {
- return (*identifier).match(
- [] (uint64_t t) -> optional<FeatureIdentifier> { return { t }; },
- [] ( int64_t t) -> optional<FeatureIdentifier> { return { t }; },
- [] ( double t) -> optional<FeatureIdentifier> { return { t }; },
- [] (const std::string& t) -> optional<FeatureIdentifier> { return { t }; },
- [&] (const auto&) -> optional<FeatureIdentifier> {
- error = { "filter expression value must be a boolean, number, or string" };
- return {};
- });
- }
- }
-
- template <class FilterType, class IdentifierFilterType, class V>
- optional<Filter> convertUnaryFilter(const V& value, Error& error) const {
- if (arrayLength(value) < 2) {
- error = { "filter expression must have 2 elements" };
- return {};
- }
-
- optional<std::string> key = toString(arrayMember(value, 1));
- if (!key) {
- error = { "filter expression key must be a string" };
- return {};
- }
-
- if (*key == "$id") {
- return { IdentifierFilterType {} };
- } else {
- return { FilterType { *key } };
- }
- }
-
- template <class FilterType, class TypeFilterType, class IdentifierFilterType, class V>
- optional<Filter> convertEqualityFilter(const V& value, Error& error) const {
- if (arrayLength(value) < 3) {
- error = { "filter expression must have 3 elements" };
- return {};
- }
-
- optional<std::string> key = toString(arrayMember(value, 1));
- if (!key) {
- error = { "filter expression key must be a string" };
- return {};
- }
-
- if (*key == "$type") {
- optional<FeatureType> filterValue = toFeatureType(arrayMember(value, 2), error);
- if (!filterValue) {
- return {};
- }
-
- return { TypeFilterType { *filterValue } };
-
- } else if (*key == "$id") {
- optional<FeatureIdentifier> filterValue = toFeatureIdentifier(arrayMember(value, 2), error);
- if (!filterValue) {
- return {};
- }
-
- return { IdentifierFilterType { *filterValue } };
-
- } else {
- optional<Value> filterValue = normalizeValue(toValue(arrayMember(value, 2)), error);
- if (!filterValue) {
- return {};
- }
-
- return { FilterType { *key, *filterValue } };
- }
- }
-
- template <class FilterType, class V>
- optional<Filter> convertBinaryFilter(const V& value, Error& error) const {
- if (arrayLength(value) < 3) {
- error = { "filter expression must have 3 elements" };
- return {};
- }
-
- optional<std::string> key = toString(arrayMember(value, 1));
- if (!key) {
- error = { "filter expression key must be a string" };
- return {};
- }
-
- optional<Value> filterValue = normalizeValue(toValue(arrayMember(value, 2)), error);
- if (!filterValue) {
- return {};
- }
-
- return { FilterType { *key, *filterValue } };
- }
-
- template <class FilterType, class TypeFilterType, class IdentifierFilterType, class V>
- optional<Filter> convertSetFilter(const V& value, Error& error) const {
- if (arrayLength(value) < 2) {
- error = { "filter expression must at least 2 elements" };
- return {};
- }
-
- optional<std::string> key = toString(arrayMember(value, 1));
- if (!key) {
- error = { "filter expression key must be a string" };
- return {};
- }
-
- if (*key == "$type") {
- std::vector<FeatureType> values;
- for (std::size_t i = 2; i < arrayLength(value); ++i) {
- optional<FeatureType> filterValue = toFeatureType(arrayMember(value, i), error);
- if (!filterValue) {
- return {};
- }
- values.push_back(*filterValue);
- }
-
- return { TypeFilterType { std::move(values) } };
-
- } else if (*key == "$id") {
- std::vector<FeatureIdentifier> values;
- for (std::size_t i = 2; i < arrayLength(value); ++i) {
- optional<FeatureIdentifier> filterValue = toFeatureIdentifier(arrayMember(value, i), error);
- if (!filterValue) {
- return {};
- }
- values.push_back(*filterValue);
- }
-
- return { IdentifierFilterType { std::move(values) } };
-
- } else {
- std::vector<Value> values;
- for (std::size_t i = 2; i < arrayLength(value); ++i) {
- optional<Value> filterValue = normalizeValue(toValue(arrayMember(value, i)), error);
- if (!filterValue) {
- return {};
- }
- values.push_back(*filterValue);
- }
-
- return { FilterType { *key, std::move(values) } };
- }
- }
-
- template <class FilterType, class V>
- optional<Filter> convertCompoundFilter(const V& value, Error& error) const {
- std::vector<Filter> filters;
- for (std::size_t i = 1; i < arrayLength(value); ++i) {
- optional<Filter> element = operator()(arrayMember(value, i), error);
- if (!element) {
- return {};
- }
- filters.push_back(*element);
- }
-
- return { FilterType { std::move(filters) } };
- }
+ optional<Filter> operator()(const Convertible& value, Error& error) const;
};
} // namespace conversion
diff --git a/include/mbgl/style/conversion/function.hpp b/include/mbgl/style/conversion/function.hpp
index 752b6dd045..e230884944 100644
--- a/include/mbgl/style/conversion/function.hpp
+++ b/include/mbgl/style/conversion/function.hpp
@@ -11,8 +11,8 @@ namespace mbgl {
namespace style {
namespace conversion {
-template <class D, class R, class V>
-optional<std::map<D, R>> convertStops(const V& value, Error& error) {
+template <class D, class R>
+optional<std::map<D, R>> convertStops(const Convertible& value, Error& error) {
auto stopsValue = objectMember(value, "stops");
if (!stopsValue) {
error = { "function value must specify stops" };
@@ -63,8 +63,7 @@ template <class T>
struct Converter<ExponentialStops<T>> {
static constexpr const char * type = "exponential";
- template <class V>
- optional<ExponentialStops<T>> operator()(const V& value, Error& error) const {
+ optional<ExponentialStops<T>> operator()(const Convertible& value, Error& error) const {
auto stops = convertStops<float, T>(value, error);
if (!stops) {
return {};
@@ -89,8 +88,7 @@ template <class T>
struct Converter<IntervalStops<T>> {
static constexpr const char * type = "interval";
- template <class V>
- optional<IntervalStops<T>> operator()(const V& value, Error& error) const {
+ optional<IntervalStops<T>> operator()(const Convertible& value, Error& error) const {
auto stops = convertStops<float, T>(value, error);
if (!stops) {
return {};
@@ -101,8 +99,7 @@ struct Converter<IntervalStops<T>> {
template <>
struct Converter<CategoricalValue> {
- template <class V>
- optional<CategoricalValue> operator()(const V& value, Error& error) const {
+ optional<CategoricalValue> operator()(const Convertible& value, Error& error) const {
auto b = toBool(value);
if (b) {
return { *b };
@@ -127,8 +124,7 @@ template <class T>
struct Converter<CategoricalStops<T>> {
static constexpr const char * type = "categorical";
- template <class V>
- optional<CategoricalStops<T>> operator()(const V& value, Error& error) const {
+ optional<CategoricalStops<T>> operator()(const Convertible& value, Error& error) const {
auto stops = convertStops<CategoricalValue, T>(value, error);
if (!stops) {
return {};
@@ -142,8 +138,7 @@ template <class T>
struct Converter<IdentityStops<T>> {
static constexpr const char * type = "identity";
- template <class V>
- optional<IdentityStops<T>> operator()(const V&, Error&) const {
+ optional<IdentityStops<T>> operator()(const Convertible&, Error&) const {
return IdentityStops<T>();
}
};
@@ -154,8 +149,7 @@ struct StopsConverter;
template <class T, class... Ts>
struct StopsConverter<T, variant<Ts...>> {
public:
- template <class V>
- optional<variant<Ts...>> operator()(const V& value, Error& error) const {
+ optional<variant<Ts...>> operator()(const Convertible& value, Error& error) const {
std::string type = util::Interpolatable<T>::value ? "exponential" : "interval";
auto typeValue = objectMember(value, "type");
@@ -193,8 +187,7 @@ public:
template <class T>
struct Converter<CameraFunction<T>> {
- template <class V>
- optional<CameraFunction<T>> operator()(const V& value, Error& error) const {
+ optional<CameraFunction<T>> operator()(const Convertible& value, Error& error) const {
if (!isObject(value)) {
error = { "function must be an object" };
return {};
@@ -209,8 +202,8 @@ struct Converter<CameraFunction<T>> {
}
};
-template <class T, class V>
-optional<optional<T>> convertDefaultValue(const V& value, Error& error) {
+template <class T>
+optional<optional<T>> convertDefaultValue(const Convertible& value, Error& error) {
auto defaultValueValue = objectMember(value, "default");
if (!defaultValueValue) {
return optional<T>();
@@ -227,8 +220,7 @@ optional<optional<T>> convertDefaultValue(const V& value, Error& error) {
template <class T>
struct Converter<SourceFunction<T>> {
- template <class V>
- optional<SourceFunction<T>> operator()(const V& value, Error& error) const {
+ optional<SourceFunction<T>> operator()(const Convertible& value, Error& error) const {
if (!isObject(value)) {
error = { "function must be an object" };
return {};
@@ -267,8 +259,7 @@ struct CompositeValue : std::pair<float, S> {
template <class S>
struct Converter<CompositeValue<S>> {
- template <class V>
- optional<CompositeValue<S>> operator()(const V& value, Error& error) const {
+ optional<CompositeValue<S>> operator()(const Convertible& value, Error& error) const {
if (!isObject(value)) {
error = { "stop must be an object" };
return {};
@@ -304,8 +295,7 @@ template <class T>
struct Converter<CompositeExponentialStops<T>> {
static constexpr const char * type = "exponential";
- template <class V>
- optional<CompositeExponentialStops<T>> operator()(const V& value, Error& error) const {
+ optional<CompositeExponentialStops<T>> operator()(const Convertible& value, Error& error) const {
auto stops = convertStops<CompositeValue<float>, T>(value, error);
if (!stops) {
return {};
@@ -330,8 +320,7 @@ template <class T>
struct Converter<CompositeIntervalStops<T>> {
static constexpr const char * type = "interval";
- template <class V>
- optional<CompositeIntervalStops<T>> operator()(const V& value, Error& error) const {
+ optional<CompositeIntervalStops<T>> operator()(const Convertible& value, Error& error) const {
auto stops = convertStops<CompositeValue<float>, T>(value, error);
if (!stops) {
return {};
@@ -350,8 +339,7 @@ template <class T>
struct Converter<CompositeCategoricalStops<T>> {
static constexpr const char * type = "categorical";
- template <class V>
- optional<CompositeCategoricalStops<T>> operator()(const V& value, Error& error) const {
+ optional<CompositeCategoricalStops<T>> operator()(const Convertible& value, Error& error) const {
auto stops = convertStops<CompositeValue<CategoricalValue>, T>(value, error);
if (!stops) {
return {};
@@ -368,8 +356,7 @@ struct Converter<CompositeCategoricalStops<T>> {
template <class T>
struct Converter<CompositeFunction<T>> {
- template <class V>
- optional<CompositeFunction<T>> operator()(const V& value, Error& error) const {
+ optional<CompositeFunction<T>> operator()(const Convertible& value, Error& error) const {
if (!isObject(value)) {
error = { "function must be an object" };
return {};
diff --git a/include/mbgl/style/conversion/geojson.hpp b/include/mbgl/style/conversion/geojson.hpp
index 0b594f066c..403c5f953b 100644
--- a/include/mbgl/style/conversion/geojson.hpp
+++ b/include/mbgl/style/conversion/geojson.hpp
@@ -7,15 +7,13 @@ namespace mbgl {
namespace style {
namespace conversion {
+// Workaround until https://github.com/mapbox/mapbox-gl-native/issues/5623 is done.
+optional<GeoJSON> parseGeoJSON(const std::string&, Error&);
+
template <>
struct Converter<GeoJSON> {
public:
- optional<GeoJSON> operator()(const std::string&, Error&) const;
-
- // This is explicitly specialized in the .cpp file for JSValue. It may also be explicitly
- // specialized for SDK-specific types (e.g. mbgl::android::Value).
- template <class V>
- optional<GeoJSON> operator()(const V&, Error&) const;
+ optional<GeoJSON> operator()(const Convertible&, Error&) const;
};
} // namespace conversion
diff --git a/include/mbgl/style/conversion/geojson_options.hpp b/include/mbgl/style/conversion/geojson_options.hpp
index 1c9c18250c..3f625babb6 100644
--- a/include/mbgl/style/conversion/geojson_options.hpp
+++ b/include/mbgl/style/conversion/geojson_options.hpp
@@ -9,84 +9,7 @@ namespace conversion {
template <>
struct Converter<GeoJSONOptions> {
-
- template <class V>
- optional<GeoJSONOptions> operator()(const V& value, Error& error) const {
- GeoJSONOptions options;
-
- const auto minzoomValue = objectMember(value, "minzoom");
- if (minzoomValue) {
- if (toNumber(*minzoomValue)) {
- options.minzoom = static_cast<uint8_t>(*toNumber(*minzoomValue));
- } else {
- error = { "GeoJSON source minzoom value must be a number" };
- return {};
- }
- }
-
- const auto maxzoomValue = objectMember(value, "maxzoom");
- if (maxzoomValue) {
- if (toNumber(*maxzoomValue)) {
- options.maxzoom = static_cast<uint8_t>(*toNumber(*maxzoomValue));
- } else {
- error = { "GeoJSON source maxzoom value must be a number" };
- return {};
- }
- }
-
- const auto bufferValue = objectMember(value, "buffer");
- if (bufferValue) {
- if (toNumber(*bufferValue)) {
- options.buffer = static_cast<uint16_t>(*toNumber(*bufferValue));
- } else {
- error = { "GeoJSON source buffer value must be a number" };
- return {};
- }
- }
-
- const auto toleranceValue = objectMember(value, "tolerance");
- if (toleranceValue) {
- if (toNumber(*toleranceValue)) {
- options.tolerance = static_cast<double>(*toNumber(*toleranceValue));
- } else {
- error = { "GeoJSON source tolerance value must be a number" };
- return {};
- }
- }
-
- const auto clusterValue = objectMember(value, "cluster");
- if (clusterValue) {
- if (toBool(*clusterValue)) {
- options.cluster = *toBool(*clusterValue);
- } else {
- error = { "GeoJSON source cluster value must be a boolean" };
- return {};
- }
- }
-
- const auto clusterMaxZoomValue = objectMember(value, "clusterMaxZoom");
- if (clusterMaxZoomValue) {
- if (toNumber(*clusterMaxZoomValue)) {
- options.clusterMaxZoom = static_cast<uint8_t>(*toNumber(*clusterMaxZoomValue));
- } else {
- error = { "GeoJSON source clusterMaxZoom value must be a number" };
- return {};
- }
- }
-
- const auto clusterRadiusValue = objectMember(value, "clusterRadius");
- if (clusterRadiusValue) {
- if (toNumber(*clusterRadiusValue)) {
- options.clusterRadius = static_cast<double>(*toNumber(*clusterRadiusValue));
- } else {
- error = { "GeoJSON source clusterRadius value must be a number" };
- return {};
- }
- }
-
- return { options };
- }
-
+ optional<GeoJSONOptions> operator()(const Convertible& value, Error& error) const;
};
} // namespace conversion
diff --git a/include/mbgl/style/conversion/get_json_type.hpp b/include/mbgl/style/conversion/get_json_type.hpp
new file mode 100644
index 0000000000..f7efebccce
--- /dev/null
+++ b/include/mbgl/style/conversion/get_json_type.hpp
@@ -0,0 +1,14 @@
+#pragma once
+
+#include <mbgl/style/conversion.hpp>
+#include <string>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+std::string getJSONType(const Convertible& value);
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/conversion/heatmap_color_property_value.hpp b/include/mbgl/style/conversion/heatmap_color_property_value.hpp
new file mode 100644
index 0000000000..e3689c524c
--- /dev/null
+++ b/include/mbgl/style/conversion/heatmap_color_property_value.hpp
@@ -0,0 +1,46 @@
+#pragma once
+
+#include <mbgl/style/heatmap_color_property_value.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/constant.hpp>
+#include <mbgl/style/conversion/function.hpp>
+#include <mbgl/style/conversion/expression.hpp>
+#include <mbgl/style/expression/value.hpp>
+#include <mbgl/style/expression/is_constant.hpp>
+#include <mbgl/style/expression/is_expression.hpp>
+#include <mbgl/style/expression/find_zoom_curve.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+template <>
+struct Converter<HeatmapColorPropertyValue> {
+ optional<HeatmapColorPropertyValue> operator()(const Convertible& value, Error& error) const {
+ if (isUndefined(value)) {
+ return HeatmapColorPropertyValue();
+ } else if (isExpression(value)) {
+ optional<std::unique_ptr<Expression>> expression = convert<std::unique_ptr<Expression>>(value, error, expression::type::Color);
+ if (!expression) {
+ return {};
+ }
+ assert(*expression);
+ if (!isFeatureConstant(**expression)) {
+ error = { "property expressions not supported" };
+ return {};
+ }
+ if (!isZoomConstant(**expression)) {
+ error = { "zoom expressions not supported" };
+ return {};
+ }
+ return {HeatmapColorPropertyValue(std::move(*expression))};
+ } else {
+ error = { "heatmap-color must be an expression" };
+ return {};
+ }
+ }
+};
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/conversion/layer.hpp b/include/mbgl/style/conversion/layer.hpp
index c694c3162a..1c0e2e2f07 100644
--- a/include/mbgl/style/conversion/layer.hpp
+++ b/include/mbgl/style/conversion/layer.hpp
@@ -1,223 +1,24 @@
#pragma once
#include <mbgl/style/layer.hpp>
-#include <mbgl/style/layers/background_layer.hpp>
-#include <mbgl/style/layers/circle_layer.hpp>
-#include <mbgl/style/layers/fill_layer.hpp>
-#include <mbgl/style/layers/fill_extrusion_layer.hpp>
-#include <mbgl/style/layers/line_layer.hpp>
-#include <mbgl/style/layers/raster_layer.hpp>
-#include <mbgl/style/layers/symbol_layer.hpp>
#include <mbgl/style/conversion.hpp>
-#include <mbgl/style/conversion/constant.hpp>
-#include <mbgl/style/conversion/filter.hpp>
-#include <mbgl/style/conversion/make_property_setters.hpp>
+
+#include <memory>
namespace mbgl {
namespace style {
namespace conversion {
-template <class V>
-optional<Error> setLayoutProperty(Layer& layer, const std::string& name, const V& value) {
- static const auto setters = makeLayoutPropertySetters<V>();
- auto it = setters.find(name);
- if (it == setters.end()) {
- return Error { "property not found" };
- }
- return it->second(layer, value);
-}
-
-template <class V>
-optional<Error> setPaintProperty(Layer& layer, const std::string& name, const V& value) {
- static const auto setters = makePaintPropertySetters<V>();
- auto it = setters.find(name);
- if (it == setters.end()) {
- return Error { "property not found" };
- }
- return it->second(layer, value);
-}
-
-template <class V>
-optional<Error> setPaintProperties(Layer& layer, const V& value) {
- auto paintValue = objectMember(value, "paint");
- if (!paintValue) {
- return {};
- }
- if (!isObject(*paintValue)) {
- return { { "paint must be an object" } };
- }
- return eachMember(*paintValue, [&] (const std::string& k, const V& v) {
- return setPaintProperty(layer, k, v);
- });
-}
-
template <>
struct Converter<std::unique_ptr<Layer>> {
public:
- template <class V>
- optional<std::unique_ptr<Layer>> operator()(const V& value, Error& error) const {
- if (!isObject(value)) {
- error = { "layer must be an object" };
- return {};
- }
-
- auto idValue = objectMember(value, "id");
- if (!idValue) {
- error = { "layer must have an id" };
- return {};
- }
-
- optional<std::string> id = toString(*idValue);
- if (!id) {
- error = { "layer id must be a string" };
- return {};
- }
-
- auto typeValue = objectMember(value, "type");
- if (!typeValue) {
- error = { "layer must have a type" };
- return {};
- }
-
- optional<std::string> type = toString(*typeValue);
- if (!type) {
- error = { "layer type must be a string" };
- return {};
- }
-
- optional<std::unique_ptr<Layer>> converted;
-
- if (*type == "fill") {
- converted = convertVectorLayer<FillLayer>(*id, value, error);
- } else if (*type == "fill-extrusion") {
- converted = convertVectorLayer<FillExtrusionLayer>(*id, value, error);
- } else if (*type == "line") {
- converted = convertVectorLayer<LineLayer>(*id, value, error);
- } else if (*type == "circle") {
- converted = convertVectorLayer<CircleLayer>(*id, value, error);
- } else if (*type == "symbol") {
- converted = convertVectorLayer<SymbolLayer>(*id, value, error);
- } else if (*type == "raster") {
- converted = convertRasterLayer(*id, value, error);
- } else if (*type == "background") {
- converted = convertBackgroundLayer(*id, value, error);
- } else {
- error = { "invalid layer type" };
- return {};
- }
-
- if (!converted) {
- return converted;
- }
-
- std::unique_ptr<Layer> layer = std::move(*converted);
-
- auto minzoomValue = objectMember(value, "minzoom");
- if (minzoomValue) {
- optional<float> minzoom = toNumber(*minzoomValue);
- if (!minzoom) {
- error = { "minzoom must be numeric" };
- return {};
- }
- layer->setMinZoom(*minzoom);
- }
-
- auto maxzoomValue = objectMember(value, "maxzoom");
- if (maxzoomValue) {
- optional<float> maxzoom = toNumber(*maxzoomValue);
- if (!maxzoom) {
- error = { "maxzoom must be numeric" };
- return {};
- }
- layer->setMaxZoom(*maxzoom);
- }
-
- auto layoutValue = objectMember(value, "layout");
- if (layoutValue) {
- if (!isObject(*layoutValue)) {
- error = { "layout must be an object" };
- return {};
- }
- optional<Error> error_ = eachMember(*layoutValue, [&] (const std::string& k, const V& v) {
- return setLayoutProperty(*layer, k, v);
- });
- if (error_) {
- error = *error_;
- return {};
- }
- }
-
- optional<Error> error_ = setPaintProperties(*layer, value);
- if (error_) {
- error = *error_;
- return {};
- }
-
- return std::move(layer);
- }
-
-private:
- template <class LayerType, class V>
- optional<std::unique_ptr<Layer>> convertVectorLayer(const std::string& id, const V& value, Error& error) const {
- auto sourceValue = objectMember(value, "source");
- if (!sourceValue) {
- error = { "layer must have a source" };
- return {};
- }
-
- optional<std::string> source = toString(*sourceValue);
- if (!source) {
- error = { "layer source must be a string" };
- return {};
- }
-
- std::unique_ptr<LayerType> layer = std::make_unique<LayerType>(id, *source);
-
- auto sourceLayerValue = objectMember(value, "source-layer");
- if (sourceLayerValue) {
- optional<std::string> sourceLayer = toString(*sourceLayerValue);
- if (!sourceLayer) {
- error = { "layer source-layer must be a string" };
- return {};
- }
- layer->setSourceLayer(*sourceLayer);
- }
-
- auto filterValue = objectMember(value, "filter");
- if (filterValue) {
- optional<Filter> filter = convert<Filter>(*filterValue, error);
- if (!filter) {
- return {};
- }
- layer->setFilter(*filter);
- }
-
- return { std::move(layer) };
- }
-
- template <class V>
- optional<std::unique_ptr<Layer>> convertRasterLayer(const std::string& id, const V& value, Error& error) const {
- auto sourceValue = objectMember(value, "source");
- if (!sourceValue) {
- error = { "layer must have a source" };
- return {};
- }
-
- optional<std::string> source = toString(*sourceValue);
- if (!source) {
- error = { "layer source must be a string" };
- return {};
- }
-
- return { std::make_unique<RasterLayer>(id, *source) };
- }
-
- template <class V>
- optional<std::unique_ptr<Layer>> convertBackgroundLayer(const std::string& id, const V&, Error&) const {
- return { std::make_unique<BackgroundLayer>(id) };
- }
+ optional<std::unique_ptr<Layer>> operator()(const Convertible& value, Error& error) const;
};
+optional<Error> setLayoutProperty(Layer& layer, const std::string& name, const Convertible& value);
+optional<Error> setPaintProperty(Layer& layer, const std::string& name, const Convertible& value);
+optional<Error> setPaintProperties(Layer& layer, const Convertible& value);
+
} // namespace conversion
} // namespace style
} // namespace mbgl
diff --git a/include/mbgl/style/conversion/light.hpp b/include/mbgl/style/conversion/light.hpp
index ba162516c0..289fca2e31 100644
--- a/include/mbgl/style/conversion/light.hpp
+++ b/include/mbgl/style/conversion/light.hpp
@@ -2,9 +2,6 @@
#include <mbgl/style/light.hpp>
#include <mbgl/style/conversion.hpp>
-#include <mbgl/style/conversion/position.hpp>
-#include <mbgl/style/conversion/property_value.hpp>
-#include <mbgl/style/conversion/transition_options.hpp>
namespace mbgl {
namespace style {
@@ -13,108 +10,7 @@ namespace conversion {
template <>
struct Converter<Light> {
public:
- template <class V>
- optional<Light> operator()(const V& value, Error& error) const {
- if (!isObject(value)) {
- error = { "light must be an object" };
- return {};
- }
-
- Light light;
-
- const auto anchor = objectMember(value, "anchor");
- if (anchor) {
- optional<PropertyValue<LightAnchorType>> convertedAnchor =
- convert<PropertyValue<LightAnchorType>>(*anchor, error);
-
- if (convertedAnchor) {
- light.setAnchor(*convertedAnchor);
- } else {
- return {};
- }
- }
-
- const auto anchorTransition = objectMember(value, "anchor-transition");
- if (anchorTransition) {
- optional<TransitionOptions> transition =
- convert<TransitionOptions>(*anchorTransition, error);
- if (transition) {
- light.setAnchorTransition(*transition);
- } else {
- return {};
- }
- }
-
- const auto color = objectMember(value, "color");
- if (color) {
- optional<PropertyValue<Color>> convertedColor =
- convert<PropertyValue<Color>>(*color, error);
-
- if (convertedColor) {
- light.setColor(*convertedColor);
- } else {
- return {};
- }
- }
-
- const auto colorTransition = objectMember(value, "color-transition");
- if (colorTransition) {
- optional<TransitionOptions> transition =
- convert<TransitionOptions>(*colorTransition, error);
- if (transition) {
- light.setColorTransition(*transition);
- } else {
- return {};
- }
- }
-
- const auto position = objectMember(value, "position");
- if (position) {
- optional<PropertyValue<Position>> convertedPosition =
- convert<PropertyValue<Position>>(*position, error);
-
- if (convertedPosition) {
- light.setPosition(*convertedPosition);
- } else {
- return {};
- }
- }
-
- const auto positionTransition = objectMember(value, "position-transition");
- if (positionTransition) {
- optional<TransitionOptions> transition =
- convert<TransitionOptions>(*positionTransition, error);
- if (transition) {
- light.setPositionTransition(*transition);
- } else {
- return {};
- }
- }
-
- const auto intensity = objectMember(value, "intensity");
- if (intensity) {
- optional<PropertyValue<float>> convertedIntensity =
- convert<PropertyValue<float>>(*intensity, error);
-
- if (convertedIntensity) {
- light.setIntensity(*convertedIntensity);
- } else {
- return {};
- }
- }
-
- const auto intensityTransition = objectMember(value, "intensity-transition");
- if (intensityTransition) {
- optional<TransitionOptions> transition =
- convert<TransitionOptions>(*intensityTransition, error);
- if (transition) {
- light.setIntensityTransition(*transition);
- } else {
- return {};
- }
- }
- return { std::move(light) };
- };
+ optional<Light> operator()(const Convertible& value, Error& error) const;
};
} // namespace conversion
diff --git a/include/mbgl/style/conversion/make_property_setters.hpp b/include/mbgl/style/conversion/make_property_setters.hpp
deleted file mode 100644
index 59b0e7be32..0000000000
--- a/include/mbgl/style/conversion/make_property_setters.hpp
+++ /dev/null
@@ -1,211 +0,0 @@
-#pragma once
-
-// This file is generated. Edit make_property_setters.hpp.ejs, then run `make style-code`.
-
-#include <mbgl/style/conversion/property_setter.hpp>
-
-#include <mbgl/style/layers/fill_layer.hpp>
-#include <mbgl/style/layers/line_layer.hpp>
-#include <mbgl/style/layers/symbol_layer.hpp>
-#include <mbgl/style/layers/circle_layer.hpp>
-#include <mbgl/style/layers/fill_extrusion_layer.hpp>
-#include <mbgl/style/layers/raster_layer.hpp>
-#include <mbgl/style/layers/background_layer.hpp>
-
-#include <unordered_map>
-
-namespace mbgl {
-namespace style {
-namespace conversion {
-
-template <class V>
-auto makeLayoutPropertySetters() {
- std::unordered_map<std::string, PropertySetter<V>> result;
-
- result["visibility"] = &setVisibility<V>;
-
-
- result["line-cap"] = &setProperty<V, LineLayer, PropertyValue<LineCapType>, &LineLayer::setLineCap>;
- result["line-join"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<LineJoinType>, &LineLayer::setLineJoin>;
- result["line-miter-limit"] = &setProperty<V, LineLayer, PropertyValue<float>, &LineLayer::setLineMiterLimit>;
- result["line-round-limit"] = &setProperty<V, LineLayer, PropertyValue<float>, &LineLayer::setLineRoundLimit>;
-
- result["symbol-placement"] = &setProperty<V, SymbolLayer, PropertyValue<SymbolPlacementType>, &SymbolLayer::setSymbolPlacement>;
- result["symbol-spacing"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setSymbolSpacing>;
- result["symbol-avoid-edges"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setSymbolAvoidEdges>;
- result["icon-allow-overlap"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconAllowOverlap>;
- result["icon-ignore-placement"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconIgnorePlacement>;
- result["icon-optional"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconOptional>;
- result["icon-rotation-alignment"] = &setProperty<V, SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setIconRotationAlignment>;
- result["icon-size"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconSize>;
- result["icon-text-fit"] = &setProperty<V, SymbolLayer, PropertyValue<IconTextFitType>, &SymbolLayer::setIconTextFit>;
- result["icon-text-fit-padding"] = &setProperty<V, SymbolLayer, PropertyValue<std::array<float, 4>>, &SymbolLayer::setIconTextFitPadding>;
- result["icon-image"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<std::string>, &SymbolLayer::setIconImage>;
- result["icon-rotate"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconRotate>;
- result["icon-padding"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setIconPadding>;
- result["icon-keep-upright"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconKeepUpright>;
- result["icon-offset"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<std::array<float, 2>>, &SymbolLayer::setIconOffset>;
- result["icon-anchor"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<SymbolAnchorType>, &SymbolLayer::setIconAnchor>;
- result["icon-pitch-alignment"] = &setProperty<V, SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setIconPitchAlignment>;
- result["text-pitch-alignment"] = &setProperty<V, SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setTextPitchAlignment>;
- result["text-rotation-alignment"] = &setProperty<V, SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setTextRotationAlignment>;
- result["text-field"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<std::string>, &SymbolLayer::setTextField>;
- result["text-font"] = &setProperty<V, SymbolLayer, PropertyValue<std::vector<std::string>>, &SymbolLayer::setTextFont>;
- result["text-size"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextSize>;
- result["text-max-width"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextMaxWidth>;
- result["text-line-height"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextLineHeight>;
- result["text-letter-spacing"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextLetterSpacing>;
- result["text-justify"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<TextJustifyType>, &SymbolLayer::setTextJustify>;
- result["text-anchor"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<SymbolAnchorType>, &SymbolLayer::setTextAnchor>;
- result["text-max-angle"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextMaxAngle>;
- result["text-rotate"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextRotate>;
- result["text-padding"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextPadding>;
- result["text-keep-upright"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextKeepUpright>;
- result["text-transform"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<TextTransformType>, &SymbolLayer::setTextTransform>;
- result["text-offset"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<std::array<float, 2>>, &SymbolLayer::setTextOffset>;
- result["text-allow-overlap"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextAllowOverlap>;
- result["text-ignore-placement"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextIgnorePlacement>;
- result["text-optional"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextOptional>;
-
-
-
-
-
- return result;
-}
-
-template <class V>
-auto makePaintPropertySetters() {
- std::unordered_map<std::string, PropertySetter<V>> result;
-
- result["fill-antialias"] = &setProperty<V, FillLayer, PropertyValue<bool>, &FillLayer::setFillAntialias>;
- result["fill-antialias-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillAntialiasTransition>;
- result["fill-opacity"] = &setProperty<V, FillLayer, DataDrivenPropertyValue<float>, &FillLayer::setFillOpacity>;
- result["fill-opacity-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillOpacityTransition>;
- result["fill-color"] = &setProperty<V, FillLayer, DataDrivenPropertyValue<Color>, &FillLayer::setFillColor>;
- result["fill-color-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillColorTransition>;
- result["fill-outline-color"] = &setProperty<V, FillLayer, DataDrivenPropertyValue<Color>, &FillLayer::setFillOutlineColor>;
- result["fill-outline-color-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillOutlineColorTransition>;
- result["fill-translate"] = &setProperty<V, FillLayer, PropertyValue<std::array<float, 2>>, &FillLayer::setFillTranslate>;
- result["fill-translate-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillTranslateTransition>;
- result["fill-translate-anchor"] = &setProperty<V, FillLayer, PropertyValue<TranslateAnchorType>, &FillLayer::setFillTranslateAnchor>;
- result["fill-translate-anchor-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillTranslateAnchorTransition>;
- result["fill-pattern"] = &setProperty<V, FillLayer, PropertyValue<std::string>, &FillLayer::setFillPattern>;
- result["fill-pattern-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillPatternTransition>;
-
- result["line-opacity"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineOpacity>;
- result["line-opacity-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineOpacityTransition>;
- result["line-color"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<Color>, &LineLayer::setLineColor>;
- result["line-color-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineColorTransition>;
- result["line-translate"] = &setProperty<V, LineLayer, PropertyValue<std::array<float, 2>>, &LineLayer::setLineTranslate>;
- result["line-translate-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineTranslateTransition>;
- result["line-translate-anchor"] = &setProperty<V, LineLayer, PropertyValue<TranslateAnchorType>, &LineLayer::setLineTranslateAnchor>;
- result["line-translate-anchor-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineTranslateAnchorTransition>;
- result["line-width"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineWidth>;
- result["line-width-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineWidthTransition>;
- result["line-gap-width"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineGapWidth>;
- result["line-gap-width-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineGapWidthTransition>;
- result["line-offset"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineOffset>;
- result["line-offset-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineOffsetTransition>;
- result["line-blur"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineBlur>;
- result["line-blur-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineBlurTransition>;
- result["line-dasharray"] = &setProperty<V, LineLayer, PropertyValue<std::vector<float>>, &LineLayer::setLineDasharray>;
- result["line-dasharray-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineDasharrayTransition>;
- result["line-pattern"] = &setProperty<V, LineLayer, PropertyValue<std::string>, &LineLayer::setLinePattern>;
- result["line-pattern-transition"] = &setTransition<V, LineLayer, &LineLayer::setLinePatternTransition>;
-
- result["icon-opacity"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconOpacity>;
- result["icon-opacity-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconOpacityTransition>;
- result["icon-color"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<Color>, &SymbolLayer::setIconColor>;
- result["icon-color-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconColorTransition>;
- result["icon-halo-color"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<Color>, &SymbolLayer::setIconHaloColor>;
- result["icon-halo-color-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconHaloColorTransition>;
- result["icon-halo-width"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconHaloWidth>;
- result["icon-halo-width-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconHaloWidthTransition>;
- result["icon-halo-blur"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconHaloBlur>;
- result["icon-halo-blur-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconHaloBlurTransition>;
- result["icon-translate"] = &setProperty<V, SymbolLayer, PropertyValue<std::array<float, 2>>, &SymbolLayer::setIconTranslate>;
- result["icon-translate-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconTranslateTransition>;
- result["icon-translate-anchor"] = &setProperty<V, SymbolLayer, PropertyValue<TranslateAnchorType>, &SymbolLayer::setIconTranslateAnchor>;
- result["icon-translate-anchor-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconTranslateAnchorTransition>;
- result["text-opacity"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextOpacity>;
- result["text-opacity-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextOpacityTransition>;
- result["text-color"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<Color>, &SymbolLayer::setTextColor>;
- result["text-color-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextColorTransition>;
- result["text-halo-color"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<Color>, &SymbolLayer::setTextHaloColor>;
- result["text-halo-color-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextHaloColorTransition>;
- result["text-halo-width"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextHaloWidth>;
- result["text-halo-width-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextHaloWidthTransition>;
- result["text-halo-blur"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextHaloBlur>;
- result["text-halo-blur-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextHaloBlurTransition>;
- result["text-translate"] = &setProperty<V, SymbolLayer, PropertyValue<std::array<float, 2>>, &SymbolLayer::setTextTranslate>;
- result["text-translate-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextTranslateTransition>;
- result["text-translate-anchor"] = &setProperty<V, SymbolLayer, PropertyValue<TranslateAnchorType>, &SymbolLayer::setTextTranslateAnchor>;
- result["text-translate-anchor-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextTranslateAnchorTransition>;
-
- result["circle-radius"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleRadius>;
- result["circle-radius-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleRadiusTransition>;
- result["circle-color"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<Color>, &CircleLayer::setCircleColor>;
- result["circle-color-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleColorTransition>;
- result["circle-blur"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleBlur>;
- result["circle-blur-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleBlurTransition>;
- result["circle-opacity"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleOpacity>;
- result["circle-opacity-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleOpacityTransition>;
- result["circle-translate"] = &setProperty<V, CircleLayer, PropertyValue<std::array<float, 2>>, &CircleLayer::setCircleTranslate>;
- result["circle-translate-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleTranslateTransition>;
- result["circle-translate-anchor"] = &setProperty<V, CircleLayer, PropertyValue<TranslateAnchorType>, &CircleLayer::setCircleTranslateAnchor>;
- result["circle-translate-anchor-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleTranslateAnchorTransition>;
- result["circle-pitch-scale"] = &setProperty<V, CircleLayer, PropertyValue<CirclePitchScaleType>, &CircleLayer::setCirclePitchScale>;
- result["circle-pitch-scale-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCirclePitchScaleTransition>;
- result["circle-pitch-alignment"] = &setProperty<V, CircleLayer, PropertyValue<AlignmentType>, &CircleLayer::setCirclePitchAlignment>;
- result["circle-pitch-alignment-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCirclePitchAlignmentTransition>;
- result["circle-stroke-width"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleStrokeWidth>;
- result["circle-stroke-width-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleStrokeWidthTransition>;
- result["circle-stroke-color"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<Color>, &CircleLayer::setCircleStrokeColor>;
- result["circle-stroke-color-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleStrokeColorTransition>;
- result["circle-stroke-opacity"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleStrokeOpacity>;
- result["circle-stroke-opacity-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleStrokeOpacityTransition>;
-
- result["fill-extrusion-opacity"] = &setProperty<V, FillExtrusionLayer, PropertyValue<float>, &FillExtrusionLayer::setFillExtrusionOpacity>;
- result["fill-extrusion-opacity-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionOpacityTransition>;
- result["fill-extrusion-color"] = &setProperty<V, FillExtrusionLayer, DataDrivenPropertyValue<Color>, &FillExtrusionLayer::setFillExtrusionColor>;
- result["fill-extrusion-color-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionColorTransition>;
- result["fill-extrusion-translate"] = &setProperty<V, FillExtrusionLayer, PropertyValue<std::array<float, 2>>, &FillExtrusionLayer::setFillExtrusionTranslate>;
- result["fill-extrusion-translate-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionTranslateTransition>;
- result["fill-extrusion-translate-anchor"] = &setProperty<V, FillExtrusionLayer, PropertyValue<TranslateAnchorType>, &FillExtrusionLayer::setFillExtrusionTranslateAnchor>;
- result["fill-extrusion-translate-anchor-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionTranslateAnchorTransition>;
- result["fill-extrusion-pattern"] = &setProperty<V, FillExtrusionLayer, PropertyValue<std::string>, &FillExtrusionLayer::setFillExtrusionPattern>;
- result["fill-extrusion-pattern-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionPatternTransition>;
- result["fill-extrusion-height"] = &setProperty<V, FillExtrusionLayer, DataDrivenPropertyValue<float>, &FillExtrusionLayer::setFillExtrusionHeight>;
- result["fill-extrusion-height-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionHeightTransition>;
- result["fill-extrusion-base"] = &setProperty<V, FillExtrusionLayer, DataDrivenPropertyValue<float>, &FillExtrusionLayer::setFillExtrusionBase>;
- result["fill-extrusion-base-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionBaseTransition>;
-
- result["raster-opacity"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterOpacity>;
- result["raster-opacity-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterOpacityTransition>;
- result["raster-hue-rotate"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterHueRotate>;
- result["raster-hue-rotate-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterHueRotateTransition>;
- result["raster-brightness-min"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterBrightnessMin>;
- result["raster-brightness-min-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterBrightnessMinTransition>;
- result["raster-brightness-max"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterBrightnessMax>;
- result["raster-brightness-max-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterBrightnessMaxTransition>;
- result["raster-saturation"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterSaturation>;
- result["raster-saturation-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterSaturationTransition>;
- result["raster-contrast"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterContrast>;
- result["raster-contrast-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterContrastTransition>;
- result["raster-fade-duration"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterFadeDuration>;
- result["raster-fade-duration-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterFadeDurationTransition>;
-
- result["background-color"] = &setProperty<V, BackgroundLayer, PropertyValue<Color>, &BackgroundLayer::setBackgroundColor>;
- result["background-color-transition"] = &setTransition<V, BackgroundLayer, &BackgroundLayer::setBackgroundColorTransition>;
- result["background-pattern"] = &setProperty<V, BackgroundLayer, PropertyValue<std::string>, &BackgroundLayer::setBackgroundPattern>;
- result["background-pattern-transition"] = &setTransition<V, BackgroundLayer, &BackgroundLayer::setBackgroundPatternTransition>;
- result["background-opacity"] = &setProperty<V, BackgroundLayer, PropertyValue<float>, &BackgroundLayer::setBackgroundOpacity>;
- result["background-opacity-transition"] = &setTransition<V, BackgroundLayer, &BackgroundLayer::setBackgroundOpacityTransition>;
-
- return result;
-}
-
-} // namespace conversion
-} // namespace style
-} // namespace mbgl
diff --git a/include/mbgl/style/conversion/make_property_setters.hpp.ejs b/include/mbgl/style/conversion/make_property_setters.hpp.ejs
deleted file mode 100644
index 19c9f70538..0000000000
--- a/include/mbgl/style/conversion/make_property_setters.hpp.ejs
+++ /dev/null
@@ -1,48 +0,0 @@
-#pragma once
-
-// This file is generated. Edit make_property_setters.hpp.ejs, then run `make style-code`.
-
-#include <mbgl/style/conversion/property_setter.hpp>
-
-<% for (const layer of locals.layers) { -%>
-#include <mbgl/style/layers/<%- layer.type.replace('-', '_') %>_layer.hpp>
-<% } -%>
-
-#include <unordered_map>
-
-namespace mbgl {
-namespace style {
-namespace conversion {
-
-template <class V>
-auto makeLayoutPropertySetters() {
- std::unordered_map<std::string, PropertySetter<V>> result;
-
- result["visibility"] = &setVisibility<V>;
-
-<% for (const layer of locals.layers) { -%>
-<% for (const property of layer.layoutProperties) { -%>
- result["<%- property.name %>"] = &setProperty<V, <%- camelize(layer.type) %>Layer, <%- propertyValueType(property) %>, &<%- camelize(layer.type) %>Layer::set<%- camelize(property.name) %>>;
-<% } -%>
-
-<% } -%>
- return result;
-}
-
-template <class V>
-auto makePaintPropertySetters() {
- std::unordered_map<std::string, PropertySetter<V>> result;
-
-<% for (const layer of locals.layers) { -%>
-<% for (const property of layer.paintProperties) { -%>
- result["<%- property.name %>"] = &setProperty<V, <%- camelize(layer.type) %>Layer, <%- propertyValueType(property) %>, &<%- camelize(layer.type) %>Layer::set<%- camelize(property.name) %>>;
- result["<%- property.name %>-transition"] = &setTransition<V, <%- camelize(layer.type) %>Layer, &<%- camelize(layer.type) %>Layer::set<%- camelize(property.name) %>Transition>;
-<% } -%>
-
-<% } -%>
- return result;
-}
-
-} // namespace conversion
-} // namespace style
-} // namespace mbgl
diff --git a/include/mbgl/style/conversion/position.hpp b/include/mbgl/style/conversion/position.hpp
index 7036b03822..044c45862d 100644
--- a/include/mbgl/style/conversion/position.hpp
+++ b/include/mbgl/style/conversion/position.hpp
@@ -2,9 +2,6 @@
#include <mbgl/style/conversion.hpp>
#include <mbgl/style/position.hpp>
-#include <mbgl/util/optional.hpp>
-
-#include <array>
namespace mbgl {
namespace style {
@@ -12,16 +9,7 @@ namespace conversion {
template <>
struct Converter<Position> {
- template <class V>
- optional<Position> operator()(const V& value, Error& error) const {
- optional<std::array<float, 3>> spherical = convert<std::array<float, 3>>(value, error);
-
- if (!spherical) {
- return {};
- }
-
- return Position(*spherical);
- }
+ optional<Position> operator()(const Convertible& value, Error& error) const;
};
} // namespace conversion
diff --git a/include/mbgl/style/conversion/property_value.hpp b/include/mbgl/style/conversion/property_value.hpp
index f8937da07d..97117de2ec 100644
--- a/include/mbgl/style/conversion/property_value.hpp
+++ b/include/mbgl/style/conversion/property_value.hpp
@@ -4,6 +4,11 @@
#include <mbgl/style/conversion.hpp>
#include <mbgl/style/conversion/constant.hpp>
#include <mbgl/style/conversion/function.hpp>
+#include <mbgl/style/conversion/expression.hpp>
+#include <mbgl/style/expression/value.hpp>
+#include <mbgl/style/expression/is_constant.hpp>
+#include <mbgl/style/expression/is_expression.hpp>
+#include <mbgl/style/expression/find_zoom_curve.hpp>
namespace mbgl {
namespace style {
@@ -11,10 +16,20 @@ namespace conversion {
template <class T>
struct Converter<PropertyValue<T>> {
- template <class V>
- optional<PropertyValue<T>> operator()(const V& value, Error& error) const {
+ optional<PropertyValue<T>> operator()(const Convertible& value, Error& error) const {
if (isUndefined(value)) {
return PropertyValue<T>();
+ } else if (isExpression(value)) {
+ optional<std::unique_ptr<Expression>> expression = convert<std::unique_ptr<Expression>>(value, error, valueTypeToExpressionType<T>());
+ if (!expression) {
+ return {};
+ }
+ if (isFeatureConstant(**expression)) {
+ return { CameraFunction<T>(std::move(*expression)) };
+ } else {
+ error = { "property expressions not supported" };
+ return {};
+ }
} else if (isObject(value)) {
optional<CameraFunction<T>> function = convert<CameraFunction<T>>(value, error);
if (!function) {
diff --git a/include/mbgl/style/conversion/source.hpp b/include/mbgl/style/conversion/source.hpp
index e0563ac10b..2cf2e36da4 100644
--- a/include/mbgl/style/conversion/source.hpp
+++ b/include/mbgl/style/conversion/source.hpp
@@ -1,16 +1,9 @@
#pragma once
#include <mbgl/style/conversion.hpp>
-#include <mbgl/style/conversion/coordinate.hpp>
-#include <mbgl/style/conversion/geojson.hpp>
-#include <mbgl/style/conversion/geojson_options.hpp>
-#include <mbgl/style/conversion/tileset.hpp>
#include <mbgl/style/source.hpp>
-#include <mbgl/style/sources/geojson_source.hpp>
-#include <mbgl/style/sources/raster_source.hpp>
-#include <mbgl/style/sources/vector_source.hpp>
-#include <mbgl/style/sources/image_source.hpp>
-#include <mbgl/util/geo.hpp>
+
+#include <memory>
namespace mbgl {
namespace style {
@@ -19,170 +12,7 @@ namespace conversion {
template <>
struct Converter<std::unique_ptr<Source>> {
public:
-
- template <class V>
- optional<std::unique_ptr<Source>> operator()(const V& value, Error& error, const std::string& id) const {
- if (!isObject(value)) {
- error = { "source must be an object" };
- return {};
- }
-
- auto typeValue = objectMember(value, "type");
- if (!typeValue) {
- error = { "source must have a type" };
- return {};
- }
-
- optional<std::string> type = toString(*typeValue);
- if (!type) {
- error = { "source type must be a string" };
- return {};
- }
-
- if (*type == "raster") {
- return convertRasterSource(id, value, error);
- } else if (*type == "vector") {
- return convertVectorSource(id, value, error);
- } else if (*type == "geojson") {
- return convertGeoJSONSource(id, value, error);
- } else if (*type == "image") {
- return convertImageSource(id, value, error);
- } else {
- error = { "invalid source type" };
- return {};
- }
- }
-
-private:
- // A tile source can either specify a URL to TileJSON, or inline TileJSON.
- template <class V>
- optional<variant<std::string, Tileset>> convertURLOrTileset(const V& value, Error& error) const {
- auto urlVal = objectMember(value, "url");
- if (!urlVal) {
- optional<Tileset> tileset = convert<Tileset>(value, error);
- if (!tileset) {
- return {};
- }
- return { *tileset };
- }
-
- optional<std::string> url = toString(*urlVal);
- if (!url) {
- error = { "source url must be a string" };
- return {};
- }
-
- return { *url };
- }
-
- template <class V>
- optional<std::unique_ptr<Source>> convertRasterSource(const std::string& id,
- const V& value,
- Error& error) const {
- optional<variant<std::string, Tileset>> urlOrTileset = convertURLOrTileset(value, error);
- if (!urlOrTileset) {
- return {};
- }
-
- uint16_t tileSize = util::tileSize;
- auto tileSizeValue = objectMember(value, "tileSize");
- if (tileSizeValue) {
- optional<float> size = toNumber(*tileSizeValue);
- if (!size || *size < 0 || *size > std::numeric_limits<uint16_t>::max()) {
- error = { "invalid tileSize" };
- return {};
- }
- tileSize = *size;
- }
-
- return { std::make_unique<RasterSource>(id, std::move(*urlOrTileset), tileSize) };
- }
-
- template <class V>
- optional<std::unique_ptr<Source>> convertVectorSource(const std::string& id,
- const V& value,
- Error& error) const {
- optional<variant<std::string, Tileset>> urlOrTileset = convertURLOrTileset(value, error);
- if (!urlOrTileset) {
- return {};
- }
-
- return { std::make_unique<VectorSource>(id, std::move(*urlOrTileset)) };
- }
-
- template <class V>
- optional<std::unique_ptr<Source>> convertGeoJSONSource(const std::string& id,
- const V& value,
- Error& error) const {
- auto dataValue = objectMember(value, "data");
- if (!dataValue) {
- error = { "GeoJSON source must have a data value" };
- return {};
- }
-
- optional<GeoJSONOptions> options = convert<GeoJSONOptions>(value, error);
- if (!options) {
- return {};
- }
-
- auto result = std::make_unique<GeoJSONSource>(id, *options);
-
- if (isObject(*dataValue)) {
- optional<GeoJSON> geoJSON = convert<GeoJSON>(*dataValue, error);
- if (!geoJSON) {
- return {};
- }
- result->setGeoJSON(std::move(*geoJSON));
- } else if (toString(*dataValue)) {
- result->setURL(*toString(*dataValue));
- } else {
- error = { "GeoJSON data must be a URL or an object" };
- return {};
- }
-
- return { std::move(result) };
- }
-
- template <class V>
- optional<std::unique_ptr<Source>> convertImageSource(const std::string& id,
- const V& value,
- Error& error) const {
- auto urlValue = objectMember(value, "url");
- if (!urlValue) {
- error = { "Image source must have a url value" };
- return {};
- }
-
- auto urlString = toString(*urlValue);
- if (!urlString) {
- error = { "Image url must be a URL string" };
- return {};
- }
-
- auto coordinatesValue = objectMember(value, "coordinates");
- if (!coordinatesValue) {
- error = { "Image source must have a coordinates values" };
- return {};
- }
-
- if (!isArray(*coordinatesValue) || arrayLength(*coordinatesValue) != 4) {
- error = { "Image coordinates must be an array of four longitude latitude pairs" };
- return {};
- }
-
- std::array<LatLng, 4> coordinates;
- for (std::size_t i=0; i < 4; i++) {
- auto latLng = conversion::convert<LatLng>(arrayMember(*coordinatesValue,i), error);
- if (!latLng) {
- return {};
- }
- coordinates[i] = *latLng;
- }
- auto result = std::make_unique<ImageSource>(id, coordinates);
- result->setURL(*urlString);
-
- return { std::move(result) };
- }
+ optional<std::unique_ptr<Source>> operator()(const Convertible& value, Error& error, const std::string& id) const;
};
} // namespace conversion
diff --git a/include/mbgl/style/conversion/tileset.hpp b/include/mbgl/style/conversion/tileset.hpp
index 6ec46aa7b6..1fb4acf70d 100644
--- a/include/mbgl/style/conversion/tileset.hpp
+++ b/include/mbgl/style/conversion/tileset.hpp
@@ -10,101 +10,7 @@ namespace conversion {
template <>
struct Converter<Tileset> {
public:
-
- bool validateLatitude(const double lat) const {
- return lat <= 90 && lat >= -90;
- }
-
- template <class V>
- optional<Tileset> operator()(const V& value, Error& error) const {
- Tileset result;
-
- auto tiles = objectMember(value, "tiles");
- if (!tiles) {
- error = { "source must have tiles" };
- return {};
- }
-
- if (!isArray(*tiles)) {
- error = { "source tiles must be an array" };
- return {};
- }
-
- for (std::size_t i = 0; i < arrayLength(*tiles); i++) {
- optional<std::string> urlTemplate = toString(arrayMember(*tiles, i));
- if (!urlTemplate) {
- error = { "source tiles member must be a string" };
- return {};
- }
- result.tiles.push_back(std::move(*urlTemplate));
- }
-
- auto schemeValue = objectMember(value, "scheme");
- if (schemeValue) {
- optional<std::string> scheme = toString(*schemeValue);
- if (scheme && *scheme == "tms") {
- result.scheme = Tileset::Scheme::TMS;
- }
- }
-
- auto minzoomValue = objectMember(value, "minzoom");
- if (minzoomValue) {
- optional<float> minzoom = toNumber(*minzoomValue);
- if (!minzoom || *minzoom < 0 || *minzoom > std::numeric_limits<uint8_t>::max()) {
- error = { "invalid minzoom" };
- return {};
- }
- result.zoomRange.min = *minzoom;
- }
-
- auto maxzoomValue = objectMember(value, "maxzoom");
- if (maxzoomValue) {
- optional<float> maxzoom = toNumber(*maxzoomValue);
- if (!maxzoom || *maxzoom < 0 || *maxzoom > std::numeric_limits<uint8_t>::max()) {
- error = { "invalid maxzoom" };
- return {};
- }
- result.zoomRange.max = *maxzoom;
- }
-
- auto attributionValue = objectMember(value, "attribution");
- if (attributionValue) {
- optional<std::string> attribution = toString(*attributionValue);
- if (!attribution) {
- error = { "source attribution must be a string" };
- return {};
- }
- result.attribution = std::move(*attribution);
- }
-
- auto boundsValue = objectMember(value, "bounds");
- if (boundsValue) {
- if (!isArray(*boundsValue) || arrayLength(*boundsValue) != 4) {
- error = { "bounds must be an array with left, bottom, top, and right values" };
- return {};
- }
- optional<double> left = toDouble(arrayMember(*boundsValue, 0));
- optional<double> bottom = toDouble(arrayMember(*boundsValue, 1));
- optional<double> right = toDouble(arrayMember(*boundsValue, 2));
- optional<double> top = toDouble(arrayMember(*boundsValue, 3));
-
- if (!left || !right || !bottom || !top) {
- error = { "bounds array must contain numeric longitude and latitude values" };
- return {};
- }
- if (!validateLatitude(*bottom) || !validateLatitude(*top) || top <= bottom){
- error = { "bounds latitude values must be between -90 and 90 with bottom less than top" };
- return {};
- }
- if(*left >= *right) {
- error = { "bounds left longitude should be less than right longitude" };
- return {};
- }
- result.bounds = LatLngBounds::hull({ *bottom, *left }, { *top, *right });
- }
-
- return result;
- }
+ optional<Tileset> operator()(const Convertible& value, Error& error) const;
};
} // namespace conversion
diff --git a/include/mbgl/style/conversion/transition_options.hpp b/include/mbgl/style/conversion/transition_options.hpp
index de8834d578..0563f39ac3 100644
--- a/include/mbgl/style/conversion/transition_options.hpp
+++ b/include/mbgl/style/conversion/transition_options.hpp
@@ -10,37 +10,7 @@ namespace conversion {
template <>
struct Converter<TransitionOptions> {
public:
- template <class V>
- optional<TransitionOptions> operator()(const V& value, Error& error) const {
- if (!isObject(value)) {
- error = { "transition must be an object" };
- return {};
- }
-
- TransitionOptions result;
-
- auto duration = objectMember(value, "duration");
- if (duration) {
- auto number = toNumber(*duration);
- if (!number) {
- error = { "duration must be a number" };
- return {};
- }
- result.duration = { std::chrono::milliseconds(int64_t(*number)) };
- }
-
- auto delay = objectMember(value, "delay");
- if (delay) {
- auto number = toNumber(*delay);
- if (!number) {
- error = { "delay must be a number" };
- return {};
- }
- result.delay = { std::chrono::milliseconds(int64_t(*number)) };
- }
-
- return result;
- }
+ optional<TransitionOptions> operator()(const Convertible& value, Error& error) const;
};
} // namespace conversion
diff --git a/include/mbgl/style/expression/array_assertion.hpp b/include/mbgl/style/expression/array_assertion.hpp
new file mode 100644
index 0000000000..af153611ff
--- /dev/null
+++ b/include/mbgl/style/expression/array_assertion.hpp
@@ -0,0 +1,46 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/expression/type.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/style/conversion.hpp>
+
+#include <memory>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+class ArrayAssertion : public Expression {
+public:
+ ArrayAssertion(type::Array type_, std::unique_ptr<Expression> input_) :
+ Expression(type_),
+ input(std::move(input_))
+ {}
+
+ static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx);
+
+ EvaluationResult evaluate(const EvaluationContext& params) const override;
+ void eachChild(const std::function<void(const Expression&)>& visit) const override;
+
+ bool operator==(const Expression& e) const override {
+ if (auto rhs = dynamic_cast<const ArrayAssertion*>(&e)) {
+ return getType() == rhs->getType() && *input == *(rhs->input);
+ }
+ return false;
+ }
+
+ std::vector<optional<Value>> possibleOutputs() const override {
+ return input->possibleOutputs();
+ }
+
+ mbgl::Value serialize() const override;
+ std::string getOperator() const override { return "array"; }
+
+private:
+ std::unique_ptr<Expression> input;
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/assertion.hpp b/include/mbgl/style/expression/assertion.hpp
new file mode 100644
index 0000000000..d1e919b10f
--- /dev/null
+++ b/include/mbgl/style/expression/assertion.hpp
@@ -0,0 +1,38 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+
+#include <memory>
+#include <vector>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+class Assertion : public Expression {
+public:
+ Assertion(type::Type type_, std::vector<std::unique_ptr<Expression>> inputs_) :
+ Expression(type_),
+ inputs(std::move(inputs_))
+ {}
+
+ static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx);
+
+ EvaluationResult evaluate(const EvaluationContext& params) const override;
+ void eachChild(const std::function<void(const Expression&)>& visit) const override;
+
+ bool operator==(const Expression& e) const override;
+
+ std::vector<optional<Value>> possibleOutputs() const override;
+
+ std::string getOperator() const override;
+
+private:
+ std::vector<std::unique_ptr<Expression>> inputs;
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/at.hpp b/include/mbgl/style/expression/at.hpp
new file mode 100644
index 0000000000..1e6f1c7dd2
--- /dev/null
+++ b/include/mbgl/style/expression/at.hpp
@@ -0,0 +1,44 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <memory>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+class At : public Expression {
+public:
+ At(std::unique_ptr<Expression> index_, std::unique_ptr<Expression> input_) :
+ Expression(input_->getType().get<type::Array>().itemType),
+ index(std::move(index_)),
+ input(std::move(input_))
+ {}
+
+ static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx);
+
+ EvaluationResult evaluate(const EvaluationContext& params) const override;
+ void eachChild(const std::function<void(const Expression&)>&) const override;
+
+ bool operator==(const Expression& e) const override {
+ if (auto rhs = dynamic_cast<const At*>(&e)) {
+ return *index == *(rhs->index) && *input == *(rhs->input);
+ }
+ return false;
+ }
+
+ std::vector<optional<Value>> possibleOutputs() const override {
+ return { nullopt };
+ }
+
+ std::string getOperator() const override { return "at"; }
+
+private:
+ std::unique_ptr<Expression> index;
+ std::unique_ptr<Expression> input;
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/boolean_operator.hpp b/include/mbgl/style/expression/boolean_operator.hpp
new file mode 100644
index 0000000000..6d0f85756a
--- /dev/null
+++ b/include/mbgl/style/expression/boolean_operator.hpp
@@ -0,0 +1,52 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/conversion.hpp>
+
+#include <memory>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+class Any : public Expression {
+public:
+ Any(std::vector<std::unique_ptr<Expression>> inputs_) :
+ Expression(type::Boolean),
+ inputs(std::move(inputs_))
+ {}
+
+ static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx);
+
+ EvaluationResult evaluate(const EvaluationContext& params) const override;
+ void eachChild(const std::function<void(const Expression&)>& visit) const override;
+ bool operator==(const Expression& e) const override;
+ std::vector<optional<Value>> possibleOutputs() const override;
+
+ std::string getOperator() const override { return "any"; }
+private:
+ std::vector<std::unique_ptr<Expression>> inputs;
+};
+
+class All : public Expression {
+public:
+ All(std::vector<std::unique_ptr<Expression>> inputs_) :
+ Expression(type::Boolean),
+ inputs(std::move(inputs_))
+ {}
+
+ static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx);
+
+ EvaluationResult evaluate(const EvaluationContext& params) const override;
+ void eachChild(const std::function<void(const Expression&)>& visit) const override;
+ bool operator==(const Expression& e) const override;
+ std::vector<optional<Value>> possibleOutputs() const override;
+
+ std::string getOperator() const override { return "all"; }
+private:
+ std::vector<std::unique_ptr<Expression>> inputs;
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/case.hpp b/include/mbgl/style/expression/case.hpp
new file mode 100644
index 0000000000..667ca53712
--- /dev/null
+++ b/include/mbgl/style/expression/case.hpp
@@ -0,0 +1,39 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+
+#include <memory>
+#include <vector>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+class Case : public Expression {
+public:
+ using Branch = std::pair<std::unique_ptr<Expression>, std::unique_ptr<Expression>>;
+
+ Case(type::Type type_, std::vector<Branch> branches_, std::unique_ptr<Expression> otherwise_)
+ : Expression(type_), branches(std::move(branches_)), otherwise(std::move(otherwise_)) {
+ }
+
+ static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx);
+
+ EvaluationResult evaluate(const EvaluationContext& params) const override;
+ void eachChild(const std::function<void(const Expression&)>& visit) const override;
+
+ bool operator==(const Expression& e) const override;
+
+ std::vector<optional<Value>> possibleOutputs() const override;
+
+ std::string getOperator() const override { return "case"; }
+private:
+ std::vector<Branch> branches;
+ std::unique_ptr<Expression> otherwise;
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/check_subtype.hpp b/include/mbgl/style/expression/check_subtype.hpp
new file mode 100644
index 0000000000..90e5169de7
--- /dev/null
+++ b/include/mbgl/style/expression/check_subtype.hpp
@@ -0,0 +1,17 @@
+#pragma once
+
+#include <mbgl/style/expression/type.hpp>
+#include <mbgl/util/optional.hpp>
+#include <memory>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+namespace type {
+
+optional<std::string> checkSubtype(const Type& expected, const Type& t);
+
+} // namespace type
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/coalesce.hpp b/include/mbgl/style/expression/coalesce.hpp
new file mode 100644
index 0000000000..a858bef695
--- /dev/null
+++ b/include/mbgl/style/expression/coalesce.hpp
@@ -0,0 +1,48 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+
+#include <memory>
+#include <map>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+class Coalesce : public Expression {
+public:
+ using Args = std::vector<std::unique_ptr<Expression>>;
+ Coalesce(const type::Type& type_, Args args_) :
+ Expression(type_),
+ args(std::move(args_))
+ {}
+
+ static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx);
+
+
+ EvaluationResult evaluate(const EvaluationContext& params) const override;
+
+ void eachChild(const std::function<void(const Expression&)>& visit) const override;
+
+ bool operator==(const Expression& e) const override;
+
+ std::vector<optional<Value>> possibleOutputs() const override;
+
+ std::size_t getLength() const {
+ return args.size();
+ }
+
+ Expression* getChild(std::size_t i) const {
+ return args.at(i).get();
+ }
+
+ std::string getOperator() const override { return "coalesce"; }
+private:
+ Args args;
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/coercion.hpp b/include/mbgl/style/expression/coercion.hpp
new file mode 100644
index 0000000000..d83bd6dfa7
--- /dev/null
+++ b/include/mbgl/style/expression/coercion.hpp
@@ -0,0 +1,40 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/conversion.hpp>
+
+#include <memory>
+#include <vector>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+/**
+ * Special form for error-coalescing coercion expressions "to-number",
+ * "to-color". Since these coercions can fail at runtime, they accept multiple
+ * arguments, only evaluating one at a time until one succeeds.
+ */
+class Coercion : public Expression {
+public:
+ Coercion(type::Type type_, std::vector<std::unique_ptr<Expression>> inputs_);
+
+ static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx);
+
+ EvaluationResult evaluate(const EvaluationContext& params) const override;
+ void eachChild(const std::function<void(const Expression&)>& visit) const override;
+
+ bool operator==(const Expression& e) const override;
+
+ std::vector<optional<Value>> possibleOutputs() const override;
+
+ std::string getOperator() const override;
+private:
+ EvaluationResult (*coerceSingleValue) (const Value& v);
+ std::vector<std::unique_ptr<Expression>> inputs;
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
+
diff --git a/include/mbgl/style/expression/compound_expression.hpp b/include/mbgl/style/expression/compound_expression.hpp
new file mode 100644
index 0000000000..6baaae862f
--- /dev/null
+++ b/include/mbgl/style/expression/compound_expression.hpp
@@ -0,0 +1,147 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/style/expression/type.hpp>
+#include <mbgl/style/expression/value.hpp>
+
+#include <mbgl/util/optional.hpp>
+#include <mbgl/util/variant.hpp>
+
+#include <memory>
+#include <vector>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+/*
+ CompoundExpression provides a mechanism for implementing an expression
+ simply by providing a list of pure functions of the form
+ (const T0& arg0, const T1& arg1, ...) -> Result<U> where T0, T1, ..., U are
+ member types of mbgl::style::expression::Value.
+
+ The majority of expressions specified in the style-spec are implemented in
+ this fashion (see compound_expression.cpp).
+*/
+
+
+/*
+ Represents the parameter list for an expression that takes an arbitrary
+ number of arguments (of a specific type).
+*/
+struct VarargsType { type::Type type; };
+template <typename T>
+struct Varargs : std::vector<T> { using std::vector<T>::vector; };
+
+namespace detail {
+// Base class for the Signature<Fn> structs that are used to determine the
+// each CompoundExpression definition's type::Type data from the type of its
+// "evaluate" function.
+struct SignatureBase {
+ SignatureBase(type::Type result_, variant<std::vector<type::Type>, VarargsType> params_, std::string name_) :
+ result(std::move(result_)),
+ params(std::move(params_)),
+ name(std::move(name_))
+ {}
+ virtual ~SignatureBase() = default;
+ virtual std::unique_ptr<Expression> makeExpression(std::vector<std::unique_ptr<Expression>>) const = 0;
+ type::Type result;
+ variant<std::vector<type::Type>, VarargsType> params;
+ std::string name;
+};
+} // namespace detail
+
+
+/*
+ Common base class for CompoundExpression<Signature> instances. Used to
+ allow downcasting (and access to things like name & parameter list) during
+ an Expression tree traversal.
+*/
+class CompoundExpressionBase : public Expression {
+public:
+ CompoundExpressionBase(std::string name_, const detail::SignatureBase& signature) :
+ Expression(signature.result),
+ name(std::move(name_)),
+ params(signature.params)
+ {}
+
+ std::string getName() const { return name; }
+ optional<std::size_t> getParameterCount() const {
+ return params.match(
+ [&](const VarargsType&) { return optional<std::size_t>(); },
+ [&](const std::vector<type::Type>& p) -> optional<std::size_t> { return p.size(); }
+ );
+ }
+
+ std::vector<optional<Value>> possibleOutputs() const override {
+ return { nullopt };
+ }
+
+private:
+ std::string name;
+ variant<std::vector<type::Type>, VarargsType> params;
+};
+
+template <typename Signature>
+class CompoundExpression : public CompoundExpressionBase {
+public:
+ using Args = typename Signature::Args;
+
+ CompoundExpression(const std::string& name_,
+ Signature signature_,
+ typename Signature::Args args_) :
+ CompoundExpressionBase(name_, signature_),
+ signature(signature_),
+ args(std::move(args_))
+ {}
+
+ EvaluationResult evaluate(const EvaluationContext& evaluationParams) const override {
+ return signature.apply(evaluationParams, args);
+ }
+
+ void eachChild(const std::function<void(const Expression&)>& visit) const override {
+ for (const std::unique_ptr<Expression>& e : args) {
+ visit(*e);
+ }
+ }
+
+ bool operator==(const Expression& e) const override {
+ if (auto rhs = dynamic_cast<const CompoundExpression*>(&e)) {
+ return getName() == rhs->getName() && Expression::childrenEqual(args, rhs->args);
+ }
+ return false;
+ }
+
+ std::string getOperator() const override {
+ return signature.name;
+ }
+
+private:
+ Signature signature;
+ typename Signature::Args args;
+};
+
+/*
+ Holds the map of expression name => implementation (which is just one or
+ more evaluation functions, each wrapped in a Signature struct).
+*/
+struct CompoundExpressionRegistry {
+ using Definition = std::vector<std::unique_ptr<detail::SignatureBase>>;
+ static std::unordered_map<std::string, Definition> definitions;
+};
+
+ParseResult parseCompoundExpression(const std::string name, const mbgl::style::conversion::Convertible& value, ParsingContext& ctx);
+
+ParseResult createCompoundExpression(const CompoundExpressionRegistry::Definition& definition,
+ std::vector<std::unique_ptr<Expression>> args,
+ ParsingContext& ctx);
+
+ParseResult createCompoundExpression(const std::string& name,
+ std::vector<std::unique_ptr<Expression>> args,
+ ParsingContext& ctx);
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/equals.hpp b/include/mbgl/style/expression/equals.hpp
new file mode 100644
index 0000000000..54df890a68
--- /dev/null
+++ b/include/mbgl/style/expression/equals.hpp
@@ -0,0 +1,33 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/style/conversion.hpp>
+
+#include <memory>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+class Equals : public Expression {
+public:
+ Equals(std::unique_ptr<Expression> lhs, std::unique_ptr<Expression> rhs, bool negate);
+
+ static ParseResult parse(const mbgl::style::conversion::Convertible&, ParsingContext&);
+
+ void eachChild(const std::function<void(const Expression&)>& visit) const override;
+ bool operator==(const Expression&) const override;
+ EvaluationResult evaluate(const EvaluationContext&) const override;
+ std::vector<optional<Value>> possibleOutputs() const override;
+
+ std::string getOperator() const override { return negate ? "!=" : "=="; }
+private:
+ std::unique_ptr<Expression> lhs;
+ std::unique_ptr<Expression> rhs;
+ bool negate;
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/expression.hpp b/include/mbgl/style/expression/expression.hpp
new file mode 100644
index 0000000000..c41ac0b5f1
--- /dev/null
+++ b/include/mbgl/style/expression/expression.hpp
@@ -0,0 +1,190 @@
+#pragma once
+
+#include <mbgl/util/optional.hpp>
+#include <mbgl/util/variant.hpp>
+#include <mbgl/util/color.hpp>
+#include <mbgl/style/expression/type.hpp>
+#include <mbgl/style/expression/value.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+
+#include <array>
+#include <vector>
+#include <memory>
+
+namespace mbgl {
+
+class GeometryTileFeature;
+
+namespace style {
+namespace expression {
+
+class EvaluationError {
+public:
+ std::string message;
+};
+
+class EvaluationContext {
+public:
+ EvaluationContext(float zoom_) : zoom(zoom_), feature(nullptr) {}
+ EvaluationContext(GeometryTileFeature const * feature_) : zoom(optional<float>()), feature(feature_) {}
+ EvaluationContext(float zoom_, GeometryTileFeature const * feature_) :
+ zoom(zoom_), feature(feature_)
+ {}
+ EvaluationContext(optional<float> zoom_, GeometryTileFeature const * feature_, optional<double> heatmapDensity_) :
+ zoom(std::move(zoom_)), feature(feature_), heatmapDensity(std::move(heatmapDensity_))
+ {}
+
+ optional<float> zoom;
+ GeometryTileFeature const * feature;
+ optional<double> heatmapDensity;
+};
+
+template <typename T>
+class Result : private variant<EvaluationError, T> {
+public:
+ using variant<EvaluationError, T>::variant;
+ using Value = T;
+
+ Result() = default;
+
+ explicit operator bool () const {
+ return this->template is<T>();
+ }
+
+ // optional does some type trait magic for this one, so this might
+ // be problematic as is.
+ const T* operator->() const {
+ assert(this->template is<T>());
+ return std::addressof(this->template get<T>());
+ }
+
+ T* operator->() {
+ assert(this->template is<T>());
+ return std::addressof(this->template get<T>());
+ }
+
+ T& operator*() {
+ assert(this->template is<T>());
+ return this->template get<T>();
+ }
+
+ const T& operator*() const {
+ assert(this->template is<T>());
+ return this->template get<T>();
+ }
+
+ const EvaluationError& error() const {
+ assert(this->template is<EvaluationError>());
+ return this->template get<EvaluationError>();
+ }
+};
+
+class EvaluationResult : public Result<Value> {
+public:
+ using Result::Result; // NOLINT
+
+ EvaluationResult() = default;
+
+ EvaluationResult(const std::array<double, 4>& arr) :
+ Result(toExpressionValue(arr))
+ {}
+
+ // used only for the special (private) "error" expression
+ EvaluationResult(const type::ErrorType&) {
+ assert(false);
+ }
+};
+
+/*
+ Expression is an abstract class that serves as an interface and base class
+ for particular expression implementations.
+
+ CompoundExpression implements the majority of expressions in the spec by
+ inferring the argument and output from a simple function (const T0& arg0,
+ const T1& arg1, ...) -> Result<U> where T0, T1, ..., U are member types of
+ mbgl::style::expression::Value.
+
+ The other Expression subclasses (Let, Curve, Match, etc.) exist in order to
+ implement expressions that need specialized parsing, type checking, or
+ evaluation logic that can't be handled by CompoundExpression's inference
+ mechanism.
+
+ Each Expression subclass also provides a static
+ ParseResult ExpressionClass::parse(const V&, ParsingContext),
+ which handles parsing a style-spec JSON representation of the expression.
+*/
+class Expression {
+public:
+ Expression(type::Type type_) : type(std::move(type_)) {}
+ virtual ~Expression() = default;
+
+ virtual EvaluationResult evaluate(const EvaluationContext& params) const = 0;
+ virtual void eachChild(const std::function<void(const Expression&)>&) const = 0;
+ virtual bool operator==(const Expression&) const = 0;
+ bool operator!=(const Expression& rhs) const {
+ return !operator==(rhs);
+ }
+
+ type::Type getType() const { return type; };
+
+ EvaluationResult evaluate(optional<float> zoom, const Feature& feature, optional<double> heatmapDensity) const;
+
+ /**
+ * Statically analyze the expression, attempting to enumerate possible outputs. Returns
+ * an array of values plus the sentinel null optional value, used to indicate that the
+ * complete set of outputs is statically undecidable.
+ */
+ virtual std::vector<optional<Value>> possibleOutputs() const = 0;
+
+ virtual mbgl::Value serialize() const {
+ std::vector<mbgl::Value> serialized;
+ serialized.emplace_back(getOperator());
+ eachChild([&](const Expression &child) {
+ serialized.emplace_back(child.serialize());
+ });
+ return serialized;
+ };
+
+ virtual std::string getOperator() const = 0;
+
+protected:
+ template <typename T>
+ static bool childrenEqual(const T& lhs, const T& rhs) {
+ if (lhs.size() != rhs.size()) return false;
+ for (auto leftChild = lhs.begin(), rightChild = rhs.begin();
+ leftChild != lhs.end();
+ leftChild++, rightChild++)
+ {
+ if (!Expression::childEqual(*leftChild, *rightChild)) return false;
+ }
+ return true;
+ }
+
+ static bool childEqual(const std::unique_ptr<Expression>& lhs, const std::unique_ptr<Expression>& rhs) {
+ return *lhs == *rhs;
+ }
+
+ template <typename T>
+ static bool childEqual(const std::pair<T, std::unique_ptr<Expression>>& lhs,
+ const std::pair<T, std::unique_ptr<Expression>>& rhs) {
+ return lhs.first == rhs.first && *(lhs.second) == *(rhs.second);
+ }
+
+ template <typename T>
+ static bool childEqual(const std::pair<T, std::shared_ptr<Expression>>& lhs,
+ const std::pair<T, std::shared_ptr<Expression>>& rhs) {
+ return lhs.first == rhs.first && *(lhs.second) == *(rhs.second);
+ }
+
+ static bool childEqual(const std::pair<std::unique_ptr<Expression>, std::unique_ptr<Expression>>& lhs,
+ const std::pair<std::unique_ptr<Expression>, std::unique_ptr<Expression>>& rhs) {
+ return *(lhs.first) == *(rhs.first) && *(lhs.second) == *(rhs.second);
+ }
+
+private:
+ type::Type type;
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/find_zoom_curve.hpp b/include/mbgl/style/expression/find_zoom_curve.hpp
new file mode 100644
index 0000000000..6301938033
--- /dev/null
+++ b/include/mbgl/style/expression/find_zoom_curve.hpp
@@ -0,0 +1,20 @@
+#pragma once
+
+#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/style/expression/interpolate.hpp>
+#include <mbgl/style/expression/step.hpp>
+
+#include <mbgl/util/variant.hpp>
+#include <mbgl/util/optional.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+optional<variant<const InterpolateBase*, const Step*, ParsingError>> findZoomCurve(const expression::Expression* e);
+
+variant<const InterpolateBase*, const Step*> findZoomCurveChecked(const expression::Expression* e);
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/get_covering_stops.hpp b/include/mbgl/style/expression/get_covering_stops.hpp
new file mode 100644
index 0000000000..157aefe7bc
--- /dev/null
+++ b/include/mbgl/style/expression/get_covering_stops.hpp
@@ -0,0 +1,18 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/util/range.hpp>
+#include <memory>
+#include <map>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+// Return the smallest range of stops that covers the interval [lower, upper]
+Range<float> getCoveringStops(const std::map<double, std::unique_ptr<Expression>>& stops,
+ const double lower, const double upper);
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/interpolate.hpp b/include/mbgl/style/expression/interpolate.hpp
new file mode 100644
index 0000000000..cc744ac7b7
--- /dev/null
+++ b/include/mbgl/style/expression/interpolate.hpp
@@ -0,0 +1,195 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/style/expression/get_covering_stops.hpp>
+#include <mbgl/style/conversion.hpp>
+
+#include <mbgl/util/interpolate.hpp>
+#include <mbgl/util/range.hpp>
+#include <mbgl/util/unitbezier.hpp>
+
+#include <memory>
+#include <map>
+#include <cmath>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+class ExponentialInterpolator {
+public:
+ ExponentialInterpolator(double base_) : base(base_) {}
+
+ double base;
+
+ double interpolationFactor(const Range<double>& inputLevels, const double input) const {
+ return util::interpolationFactor(base,
+ Range<float> {
+ static_cast<float>(inputLevels.min),
+ static_cast<float>(inputLevels.max)
+ },
+ input);
+ }
+
+ bool operator==(const ExponentialInterpolator& rhs) const {
+ return base == rhs.base;
+ }
+};
+
+class CubicBezierInterpolator {
+public:
+ CubicBezierInterpolator(double x1_, double y1_, double x2_, double y2_) : ub(x1_, y1_, x2_, y2_) {}
+
+ double interpolationFactor(const Range<double>& inputLevels, const double input) const {
+ return ub.solve(input / (inputLevels.max - inputLevels.min), 1e-6);
+ }
+
+ bool operator==(const CubicBezierInterpolator& rhs) const {
+ return ub == rhs.ub;
+ }
+
+ util::UnitBezier ub;
+};
+
+
+ParseResult parseInterpolate(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx);
+
+class InterpolateBase : public Expression {
+public:
+ using Interpolator = variant<ExponentialInterpolator, CubicBezierInterpolator>;
+
+ InterpolateBase(const type::Type& type_,
+ Interpolator interpolator_,
+ std::unique_ptr<Expression> input_,
+ std::map<double, std::unique_ptr<Expression>> stops_
+ ) : Expression(type_),
+ interpolator(std::move(interpolator_)),
+ input(std::move(input_)),
+ stops(std::move(stops_))
+ {}
+
+ const std::unique_ptr<Expression>& getInput() const { return input; }
+ const Interpolator& getInterpolator() const { return interpolator; }
+
+ void eachChild(const std::function<void(const Expression&)>& visit) const override {
+ visit(*input);
+ for (const auto& stop : stops) {
+ visit(*stop.second);
+ }
+ }
+
+ void eachStop(const std::function<void(double, const Expression&)>& visit) const {
+ for (const auto& stop : stops) {
+ visit(stop.first, *stop.second);
+ }
+ }
+
+ // Return the smallest range of stops that covers the interval [lower, upper]
+ Range<float> getCoveringStops(const double lower, const double upper) const {
+ return ::mbgl::style::expression::getCoveringStops(stops, lower, upper);
+ }
+
+ double interpolationFactor(const Range<double>& inputLevels, const double inputValue) const {
+ return interpolator.match(
+ [&](const auto& interp) { return interp.interpolationFactor(inputLevels, inputValue); }
+ );
+ }
+
+ std::vector<optional<Value>> possibleOutputs() const override;
+
+protected:
+ const Interpolator interpolator;
+ const std::unique_ptr<Expression> input;
+ const std::map<double, std::unique_ptr<Expression>> stops;
+};
+
+template <typename T>
+class Interpolate : public InterpolateBase {
+public:
+ Interpolate(type::Type type_,
+ Interpolator interpolator_,
+ std::unique_ptr<Expression> input_,
+ std::map<double, std::unique_ptr<Expression>> stops_
+ ) : InterpolateBase(std::move(type_), std::move(interpolator_), std::move(input_), std::move(stops_))
+ {
+ static_assert(util::Interpolatable<T>::value, "Interpolate expression requires an interpolatable value type.");
+ }
+
+ EvaluationResult evaluate(const EvaluationContext& params) const override {
+ const EvaluationResult evaluatedInput = input->evaluate(params);
+ if (!evaluatedInput) {
+ return evaluatedInput.error();
+ }
+
+ float x = *fromExpressionValue<float>(*evaluatedInput);
+ if (std::isnan(x)) {
+ return EvaluationError { "Input is not a number." };
+ }
+
+ if (stops.empty()) {
+ return EvaluationError { "No stops in exponential curve." };
+ }
+
+ auto it = stops.upper_bound(x);
+ if (it == stops.end()) {
+ return stops.rbegin()->second->evaluate(params);
+ } else if (it == stops.begin()) {
+ return stops.begin()->second->evaluate(params);
+ } else {
+ float t = interpolationFactor({ std::prev(it)->first, it->first }, x);
+
+ if (t == 0.0f) {
+ return std::prev(it)->second->evaluate(params);
+ }
+ if (t == 1.0f) {
+ return it->second->evaluate(params);
+ }
+
+ EvaluationResult lower = std::prev(it)->second->evaluate(params);
+ if (!lower) {
+ return lower.error();
+ }
+ EvaluationResult upper = it->second->evaluate(params);
+ if (!upper) {
+ return upper.error();
+ }
+
+ if (!lower->is<T>()) {
+ return EvaluationError {
+ "Expected value to be of type " + toString(valueTypeToExpressionType<T>()) +
+ ", but found " + toString(typeOf(*lower)) + " instead."
+ };
+ }
+
+ if (!upper->is<T>()) {
+ return EvaluationError {
+ "Expected value to be of type " + toString(valueTypeToExpressionType<T>()) +
+ ", but found " + toString(typeOf(*upper)) + " instead."
+ };
+ }
+ return util::interpolate(lower->get<T>(), upper->get<T>(), t);
+ }
+ }
+
+ bool operator==(const Expression& e) const override {
+ if (auto rhs = dynamic_cast<const Interpolate*>(&e)) {
+ if (interpolator != rhs->interpolator ||
+ *input != *(rhs->input) ||
+ stops.size() != rhs->stops.size())
+ {
+ return false;
+ }
+
+ return Expression::childrenEqual(stops, rhs->stops);
+ }
+ return false;
+ }
+
+ mbgl::Value serialize() const override;
+ std::string getOperator() const override { return "interpolate"; }
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/is_constant.hpp b/include/mbgl/style/expression/is_constant.hpp
new file mode 100644
index 0000000000..29e03ccbc0
--- /dev/null
+++ b/include/mbgl/style/expression/is_constant.hpp
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/expression/compound_expression.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+template <typename T>
+bool isGlobalPropertyConstant(const Expression& expression, const T& properties) {
+ if (auto e = dynamic_cast<const CompoundExpressionBase*>(&expression)) {
+ for (const std::string& property : properties) {
+ if (e->getName() == property) {
+ return false;
+ }
+ }
+ }
+
+ bool isConstant = true;
+ expression.eachChild([&](const Expression& e) {
+ if (isConstant && !isGlobalPropertyConstant(e, properties)) {
+ isConstant = false;
+ }
+ });
+ return isConstant;
+};
+
+bool isFeatureConstant(const Expression& expression);
+bool isZoomConstant(const Expression& e);
+
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/is_expression.hpp b/include/mbgl/style/expression/is_expression.hpp
new file mode 100644
index 0000000000..77c489619c
--- /dev/null
+++ b/include/mbgl/style/expression/is_expression.hpp
@@ -0,0 +1,13 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+bool isExpression(const conversion::Convertible& value);
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/let.hpp b/include/mbgl/style/expression/let.hpp
new file mode 100644
index 0000000000..75d2adda62
--- /dev/null
+++ b/include/mbgl/style/expression/let.hpp
@@ -0,0 +1,80 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/style/conversion.hpp>
+
+#include <memory>
+#include <map>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+class Let : public Expression {
+public:
+ using Bindings = std::map<std::string, std::shared_ptr<Expression>>;
+
+ Let(Bindings bindings_, std::unique_ptr<Expression> result_) :
+ Expression(result_->getType()),
+ bindings(std::move(bindings_)),
+ result(std::move(result_))
+ {}
+
+ static ParseResult parse(const mbgl::style::conversion::Convertible&, ParsingContext&);
+
+ EvaluationResult evaluate(const EvaluationContext& params) const override;
+ void eachChild(const std::function<void(const Expression&)>&) const override;
+
+ bool operator==(const Expression& e) const override {
+ if (auto rhs = dynamic_cast<const Let*>(&e)) {
+ return *result == *(rhs->result);
+ }
+ return false;
+ }
+
+ std::vector<optional<Value>> possibleOutputs() const override;
+
+ Expression* getResult() const {
+ return result.get();
+ }
+
+ mbgl::Value serialize() const override;
+ std::string getOperator() const override { return "let"; }
+private:
+ Bindings bindings;
+ std::unique_ptr<Expression> result;
+};
+
+class Var : public Expression {
+public:
+ Var(std::string name_, std::shared_ptr<Expression> value_) :
+ Expression(value_->getType()),
+ name(std::move(name_)),
+ value(value_)
+ {}
+
+ static ParseResult parse(const mbgl::style::conversion::Convertible&, ParsingContext&);
+
+ EvaluationResult evaluate(const EvaluationContext& params) const override;
+ void eachChild(const std::function<void(const Expression&)>&) const override;
+
+ bool operator==(const Expression& e) const override {
+ if (auto rhs = dynamic_cast<const Var*>(&e)) {
+ return *value == *(rhs->value);
+ }
+ return false;
+ }
+
+ std::vector<optional<Value>> possibleOutputs() const override;
+
+ mbgl::Value serialize() const override;
+ std::string getOperator() const override { return "var"; }
+private:
+ std::string name;
+ std::shared_ptr<Expression> value;
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/literal.hpp b/include/mbgl/style/expression/literal.hpp
new file mode 100644
index 0000000000..d854b419f4
--- /dev/null
+++ b/include/mbgl/style/expression/literal.hpp
@@ -0,0 +1,52 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/style/conversion.hpp>
+
+#include <memory>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+class Literal : public Expression {
+public:
+ Literal(Value value_)
+ : Expression(typeOf(value_))
+ , value(value_)
+ {}
+
+ Literal(type::Array type_, std::vector<Value> value_)
+ : Expression(type_)
+ , value(value_)
+ {}
+
+ EvaluationResult evaluate(const EvaluationContext&) const override {
+ return value;
+ }
+
+ static ParseResult parse(const mbgl::style::conversion::Convertible&, ParsingContext&);
+
+ void eachChild(const std::function<void(const Expression&)>&) const override {}
+
+ bool operator==(const Expression& e) const override {
+ if (auto rhs = dynamic_cast<const Literal*>(&e)) {
+ return value == rhs->value;
+ }
+ return false;
+ }
+
+ std::vector<optional<Value>> possibleOutputs() const override {
+ return {{ value }};
+ }
+
+ mbgl::Value serialize() const override;
+ std::string getOperator() const override { return "literal"; }
+private:
+ Value value;
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/match.hpp b/include/mbgl/style/expression/match.hpp
new file mode 100644
index 0000000000..3775e38067
--- /dev/null
+++ b/include/mbgl/style/expression/match.hpp
@@ -0,0 +1,48 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/style/conversion.hpp>
+
+#include <memory>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+template <typename T>
+class Match : public Expression {
+public:
+ using Branches = std::unordered_map<T, std::shared_ptr<Expression>>;
+
+ Match(type::Type type_,
+ std::unique_ptr<Expression> input_,
+ Branches branches_,
+ std::unique_ptr<Expression> otherwise_
+ ) : Expression(type_),
+ input(std::move(input_)),
+ branches(std::move(branches_)),
+ otherwise(std::move(otherwise_))
+ {}
+
+ EvaluationResult evaluate(const EvaluationContext& params) const override;
+
+ void eachChild(const std::function<void(const Expression&)>& visit) const override;
+
+ bool operator==(const Expression& e) const override;
+
+ std::vector<optional<Value>> possibleOutputs() const override;
+
+ mbgl::Value serialize() const override;
+ std::string getOperator() const override { return "match"; }
+private:
+ std::unique_ptr<Expression> input;
+ Branches branches;
+ std::unique_ptr<Expression> otherwise;
+};
+
+ParseResult parseMatch(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx);
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/parsing_context.hpp b/include/mbgl/style/expression/parsing_context.hpp
new file mode 100644
index 0000000000..f92a4c95ea
--- /dev/null
+++ b/include/mbgl/style/expression/parsing_context.hpp
@@ -0,0 +1,156 @@
+#pragma once
+
+#include <mbgl/util/optional.hpp>
+#include <mbgl/util/string.hpp>
+#include <mbgl/style/expression/type.hpp>
+#include <mbgl/style/conversion.hpp>
+
+#include <iterator>
+#include <map>
+#include <string>
+#include <vector>
+#include <memory>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+class Expression;
+
+struct ParsingError {
+ std::string message;
+ std::string key;
+ bool operator==(const ParsingError& rhs) const { return message == rhs.message && key == rhs.key; }
+};
+
+using ParseResult = optional<std::unique_ptr<Expression>>;
+
+namespace detail {
+
+class Scope {
+public:
+ Scope(const std::map<std::string, std::shared_ptr<Expression>>& bindings_, std::shared_ptr<Scope> parent_ = nullptr) :
+ bindings(bindings_),
+ parent(std::move(parent_))
+ {}
+
+ const std::map<std::string, std::shared_ptr<Expression>>& bindings;
+ std::shared_ptr<Scope> parent;
+
+ optional<std::shared_ptr<Expression>> get(const std::string& name) {
+ auto it = bindings.find(name);
+ if (it != bindings.end()) {
+ return {it->second};
+ } else if (parent) {
+ return parent->get(name);
+ } else {
+ return optional<std::shared_ptr<Expression>>();
+ }
+ }
+};
+
+} // namespace detail
+
+class ParsingContext {
+public:
+ ParsingContext() : errors(std::make_shared<std::vector<ParsingError>>()) {}
+ ParsingContext(std::string key_) : key(std::move(key_)), errors(std::make_shared<std::vector<ParsingError>>()) {}
+ explicit ParsingContext(optional<type::Type> expected_)
+ : expected(std::move(expected_)),
+ errors(std::make_shared<std::vector<ParsingError>>())
+ {}
+ ParsingContext(ParsingContext&&) = default;
+
+ ParsingContext(const ParsingContext&) = delete;
+ ParsingContext& operator=(const ParsingContext&) = delete;
+
+ std::string getKey() const { return key; }
+ optional<type::Type> getExpected() const { return expected; }
+ const std::vector<ParsingError>& getErrors() const { return *errors; }
+
+ enum TypeAnnotationOption {
+ includeTypeAnnotations,
+ omitTypeAnnotations
+ };
+
+ /*
+ Parse the given style-spec JSON value into an Expression object.
+ Specifically, this function is responsible for determining the expression
+ type (either Literal, or the one named in value[0]) and dispatching to the
+ appropriate ParseXxxx::parse(const V&, ParsingContext) method.
+ */
+ ParseResult parse(const mbgl::style::conversion::Convertible& value,
+ TypeAnnotationOption typeAnnotationOption = includeTypeAnnotations);
+
+ /*
+ Parse a child expression.
+ */
+ ParseResult parse(const mbgl::style::conversion::Convertible&,
+ std::size_t,
+ optional<type::Type> = {},
+ TypeAnnotationOption typeAnnotationOption = includeTypeAnnotations);
+
+ /*
+ Parse a child expression.
+ */
+ ParseResult parse(const mbgl::style::conversion::Convertible&,
+ std::size_t index,
+ optional<type::Type>,
+ const std::map<std::string, std::shared_ptr<Expression>>&);
+
+ /*
+ Check whether `t` is a subtype of `expected`, collecting an error if not.
+ */
+ optional<std::string> checkType(const type::Type& t);
+
+ optional<std::shared_ptr<Expression>> getBinding(const std::string name) {
+ if (!scope) return optional<std::shared_ptr<Expression>>();
+ return scope->get(name);
+ }
+
+ void error(std::string message) {
+ errors->push_back({message, key});
+ }
+
+ void error(std::string message, std::size_t child) {
+ errors->push_back({message, key + "[" + util::toString(child) + "]"});
+ }
+
+ void error(std::string message, std::size_t child, std::size_t grandchild) {
+ errors->push_back({message, key + "[" + util::toString(child) + "][" + util::toString(grandchild) + "]"});
+ }
+
+ void appendErrors(ParsingContext&& ctx) {
+ errors->reserve(errors->size() + ctx.errors->size());
+ std::move(ctx.errors->begin(), ctx.errors->end(), std::inserter(*errors, errors->end()));
+ ctx.errors->clear();
+ }
+
+ void clearErrors() {
+ errors->clear();
+ }
+
+private:
+ ParsingContext(std::string key_,
+ std::shared_ptr<std::vector<ParsingError>> errors_,
+ optional<type::Type> expected_,
+ std::shared_ptr<detail::Scope> scope_)
+ : key(std::move(key_)),
+ expected(std::move(expected_)),
+ scope(std::move(scope_)),
+ errors(std::move(errors_))
+ {}
+
+ std::string key;
+ optional<type::Type> expected;
+ std::shared_ptr<detail::Scope> scope;
+ std::shared_ptr<std::vector<ParsingError>> errors;
+};
+
+using ParseFunction = ParseResult (*)(const conversion::Convertible&, ParsingContext&);
+using ExpressionRegistry = std::unordered_map<std::string, ParseFunction>;
+const ExpressionRegistry& getExpressionRegistry();
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/step.hpp b/include/mbgl/style/expression/step.hpp
new file mode 100644
index 0000000000..2f9524a53c
--- /dev/null
+++ b/include/mbgl/style/expression/step.hpp
@@ -0,0 +1,50 @@
+
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/style/conversion.hpp>
+
+#include <mbgl/util/range.hpp>
+
+#include <memory>
+#include <map>
+
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+class Step : public Expression {
+public:
+ Step(const type::Type& type_,
+ std::unique_ptr<Expression> input_,
+ std::map<double, std::unique_ptr<Expression>> stops_
+ ) : Expression(type_),
+ input(std::move(input_)),
+ stops(std::move(stops_))
+ {}
+
+ EvaluationResult evaluate(const EvaluationContext& params) const override;
+ void eachChild(const std::function<void(const Expression&)>& visit) const override;
+ void eachStop(const std::function<void(double, const Expression&)>& visit) const;
+
+ const std::unique_ptr<Expression>& getInput() const { return input; }
+ Range<float> getCoveringStops(const double lower, const double upper) const;
+
+ bool operator==(const Expression& e) const override;
+
+ std::vector<optional<Value>> possibleOutputs() const override;
+
+ static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx);
+
+ mbgl::Value serialize() const override;
+ std::string getOperator() const override { return "step"; }
+private:
+ const std::unique_ptr<Expression> input;
+ const std::map<double, std::unique_ptr<Expression>> stops;
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/type.hpp b/include/mbgl/style/expression/type.hpp
new file mode 100644
index 0000000000..513c4bdc17
--- /dev/null
+++ b/include/mbgl/style/expression/type.hpp
@@ -0,0 +1,112 @@
+#pragma once
+
+#include <mbgl/util/optional.hpp>
+#include <mbgl/util/string.hpp>
+#include <mbgl/util/variant.hpp>
+#include <vector>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+namespace type {
+
+template <class T>
+std::string toString(const T& t);
+
+struct NullType {
+ constexpr NullType() {};
+ std::string getName() const { return "null"; }
+ bool operator==(const NullType&) const { return true; }
+};
+
+struct NumberType {
+ constexpr NumberType() {};
+ std::string getName() const { return "number"; }
+ bool operator==(const NumberType&) const { return true; }
+};
+
+struct BooleanType {
+ constexpr BooleanType() {};
+ std::string getName() const { return "boolean"; }
+ bool operator==(const BooleanType&) const { return true; }
+};
+
+struct StringType {
+ constexpr StringType() {};
+ std::string getName() const { return "string"; }
+ bool operator==(const StringType&) const { return true; }
+};
+
+struct ColorType {
+ constexpr ColorType() {};
+ std::string getName() const { return "color"; }
+ bool operator==(const ColorType&) const { return true; }
+};
+
+struct ObjectType {
+ constexpr ObjectType() {};
+ std::string getName() const { return "object"; }
+ bool operator==(const ObjectType&) const { return true; }
+};
+
+struct ErrorType {
+ constexpr ErrorType() {};
+ std::string getName() const { return "error"; }
+ bool operator==(const ErrorType&) const { return true; }
+};
+
+struct ValueType {
+ constexpr ValueType() {};
+ std::string getName() const { return "value"; }
+ bool operator==(const ValueType&) const { return true; }
+};
+
+constexpr NullType Null;
+constexpr NumberType Number;
+constexpr StringType String;
+constexpr BooleanType Boolean;
+constexpr ColorType Color;
+constexpr ValueType Value;
+constexpr ObjectType Object;
+constexpr ErrorType Error;
+
+struct Array;
+
+using Type = variant<
+ NullType,
+ NumberType,
+ BooleanType,
+ StringType,
+ ColorType,
+ ObjectType,
+ ValueType,
+ mapbox::util::recursive_wrapper<Array>,
+ ErrorType>;
+
+struct Array {
+ explicit Array(Type itemType_) : itemType(std::move(itemType_)) {}
+ Array(Type itemType_, std::size_t N_) : itemType(std::move(itemType_)), N(N_) {}
+ Array(Type itemType_, optional<std::size_t> N_) : itemType(std::move(itemType_)), N(std::move(N_)) {}
+ std::string getName() const {
+ if (N) {
+ return "array<" + toString(itemType) + ", " + util::toString(*N) + ">";
+ } else if (itemType == Value) {
+ return "array";
+ } else {
+ return "array<" + toString(itemType) + ">";
+ }
+ }
+
+ bool operator==(const Array& rhs) const { return itemType == rhs.itemType && N == rhs.N; }
+
+ Type itemType;
+ optional<std::size_t> N;
+};
+
+template <class T>
+std::string toString(const T& type) { return type.match([&] (const auto& t) { return t.getName(); }); }
+
+} // namespace type
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/value.hpp b/include/mbgl/style/expression/value.hpp
new file mode 100644
index 0000000000..7839ff2ca7
--- /dev/null
+++ b/include/mbgl/style/expression/value.hpp
@@ -0,0 +1,163 @@
+#pragma once
+
+#include <mbgl/style/expression/type.hpp>
+#include <mbgl/style/position.hpp>
+#include <mbgl/style/types.hpp>
+#include <mbgl/util/color.hpp>
+#include <mbgl/util/enum.hpp>
+#include <mbgl/util/feature.hpp>
+#include <mbgl/util/variant.hpp>
+
+#include <array>
+#include <vector>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+struct Value;
+
+using ValueBase = variant<
+ NullValue,
+ bool,
+ double,
+ std::string,
+ Color,
+ mapbox::util::recursive_wrapper<std::vector<Value>>,
+ mapbox::util::recursive_wrapper<std::unordered_map<std::string, Value>>>;
+struct Value : ValueBase {
+ using ValueBase::ValueBase;
+
+ // Javascript's Number.MAX_SAFE_INTEGER
+ static uint64_t maxSafeInteger() { return 9007199254740991ULL; }
+
+ static bool isSafeInteger(uint64_t x) { return x <= maxSafeInteger(); };
+ static bool isSafeInteger(int64_t x) {
+ return static_cast<uint64_t>(x > 0 ? x : -x) <= maxSafeInteger();
+ }
+ static bool isSafeInteger(double x) {
+ return static_cast<uint64_t>(x > 0 ? x : -x) <= maxSafeInteger();
+ }
+
+};
+
+constexpr NullValue Null = NullValue();
+
+type::Type typeOf(const Value& value);
+std::string stringify(const Value& value);
+
+/*
+ Returns a Type object representing the expression type that corresponds to
+ the value type T. (Specialized for primitives and specific array types in
+ the .cpp.)
+*/
+template <typename T>
+type::Type valueTypeToExpressionType();
+
+/*
+ Conversions between style value types and expression::Value
+*/
+
+// no-op overloads
+Value toExpressionValue(const Value&);
+
+// T = Value (just wrap in optional)
+template <typename T>
+std::enable_if_t<std::is_same<T, Value>::value,
+optional<T>> fromExpressionValue(const Value& v)
+{
+ return optional<T>(v);
+}
+
+// T = member type of Value
+template <typename T>
+std::enable_if_t< std::is_convertible<T, Value>::value && !std::is_same<T, Value>::value,
+optional<T>> fromExpressionValue(const Value& v)
+{
+ return v.template is<T>() ? v.template get<T>() : optional<T>();
+}
+
+// real conversions
+template <typename T, typename Enable = std::enable_if_t< !std::is_convertible<T, Value>::value >>
+Value toExpressionValue(const T& value);
+
+template <typename T>
+std::enable_if_t< !std::is_convertible<T, Value>::value,
+optional<T>> fromExpressionValue(const Value& v);
+
+
+
+template <class T, class Enable = void>
+struct ValueConverter {
+ using ExpressionType = T;
+
+ static Value toExpressionValue(const T& value) {
+ return Value(value);
+ }
+ static optional<T> fromExpressionValue(const Value& value) {
+ return value.template is<T>() ? value.template get<T>() : optional<T>();
+ }
+};
+
+template <>
+struct ValueConverter<float> {
+ using ExpressionType = double;
+ static type::Type expressionType() { return type::Number; }
+ static Value toExpressionValue(const float value);
+ static optional<float> fromExpressionValue(const Value& value);
+};
+
+template<>
+struct ValueConverter<mbgl::Value> {
+ static Value toExpressionValue(const mbgl::Value& value);
+ static mbgl::Value fromExpressionValue(const Value& value);
+};
+
+template <typename T, std::size_t N>
+struct ValueConverter<std::array<T, N>> {
+ using ExpressionType = std::vector<Value>;
+ static type::Type expressionType() {
+ return type::Array(valueTypeToExpressionType<T>(), N);
+ }
+ static Value toExpressionValue(const std::array<T, N>& value);
+ static optional<std::array<T, N>> fromExpressionValue(const Value& value);
+};
+
+template <typename T>
+struct ValueConverter<std::vector<T>> {
+ using ExpressionType = std::vector<Value>;
+ static type::Type expressionType() {
+ return type::Array(valueTypeToExpressionType<T>());
+ }
+ static Value toExpressionValue(const std::vector<T>& value);
+ static optional<std::vector<T>> fromExpressionValue(const Value& value);
+};
+
+template <>
+struct ValueConverter<Position> {
+ using ExpressionType = std::vector<Value>;
+ static type::Type expressionType() { return type::Array(type::Number, 3); }
+ static Value toExpressionValue(const mbgl::style::Position& value);
+ static optional<Position> fromExpressionValue(const Value& v);
+};
+
+template <typename T>
+struct ValueConverter<T, std::enable_if_t< std::is_enum<T>::value >> {
+ using ExpressionType = std::string;
+ static type::Type expressionType() { return type::String; }
+ static Value toExpressionValue(const T& value);
+ static optional<T> fromExpressionValue(const Value& value);
+};
+
+template <typename T>
+std::vector<optional<T>> fromExpressionValues(const std::vector<optional<Value>>& values) {
+ std::vector<optional<T>> result;
+ for (const auto& value : values) {
+ result.push_back(value ? fromExpressionValue<T>(*value) : nullopt);
+ }
+ return result;
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/function/camera_function.hpp b/include/mbgl/style/function/camera_function.hpp
index 7fde365b3d..1da5d2c601 100644
--- a/include/mbgl/style/function/camera_function.hpp
+++ b/include/mbgl/style/function/camera_function.hpp
@@ -1,5 +1,12 @@
#pragma once
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/expression/interpolate.hpp>
+#include <mbgl/style/expression/step.hpp>
+#include <mbgl/style/expression/find_zoom_curve.hpp>
+#include <mbgl/style/expression/value.hpp>
+#include <mbgl/style/expression/is_constant.hpp>
+#include <mbgl/style/function/convert.hpp>
#include <mbgl/style/function/exponential_stops.hpp>
#include <mbgl/style/function/interval_stops.hpp>
#include <mbgl/util/interpolate.hpp>
@@ -18,24 +25,66 @@ public:
IntervalStops<T>>,
variant<
IntervalStops<T>>>;
+
+ CameraFunction(std::unique_ptr<expression::Expression> expression_)
+ : expression(std::move(expression_)),
+ zoomCurve(expression::findZoomCurveChecked(expression.get()))
+ {
+ assert(!expression::isZoomConstant(*expression));
+ assert(expression::isFeatureConstant(*expression));
+ }
CameraFunction(Stops stops_)
- : stops(std::move(stops_)) {
- }
+ : stops(std::move(stops_)),
+ expression(stops.match([&] (const auto& s) {
+ return expression::Convert::toExpression(s);
+ })),
+ zoomCurve(expression::findZoomCurveChecked(expression.get()))
+ {}
T evaluate(float zoom) const {
- return stops.match([&] (const auto& s) {
- return s.evaluate(zoom).value_or(T());
- });
+ const expression::EvaluationResult result = expression->evaluate(expression::EvaluationContext(zoom, nullptr));
+ if (result) {
+ const optional<T> typed = expression::fromExpressionValue<T>(*result);
+ return typed ? *typed : T();
+ }
+ return T();
}
+ float interpolationFactor(const Range<float>& inputLevels, const float inputValue) const {
+ return zoomCurve.match(
+ [&](const expression::InterpolateBase* z) {
+ return z->interpolationFactor(Range<double> { inputLevels.min, inputLevels.max }, inputValue);
+ },
+ [&](const expression::Step*) { return 0.0f; }
+ );
+ }
+
+ Range<float> getCoveringStops(const float lower, const float upper) const {
+ return zoomCurve.match(
+ [&](auto z) { return z->getCoveringStops(lower, upper); }
+ );
+ }
+
+ std::vector<optional<T>> possibleOutputs() const {
+ return expression::fromExpressionValues<T>(expression->possibleOutputs());
+ }
+
friend bool operator==(const CameraFunction& lhs,
const CameraFunction& rhs) {
- return lhs.stops == rhs.stops;
+ return *lhs.expression == *rhs.expression;
}
- Stops stops;
bool useIntegerZoom = false;
+
+ 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;
};
} // namespace style
diff --git a/include/mbgl/style/function/composite_function.hpp b/include/mbgl/style/function/composite_function.hpp
index 7b524b6021..f391b101ae 100644
--- a/include/mbgl/style/function/composite_function.hpp
+++ b/include/mbgl/style/function/composite_function.hpp
@@ -1,5 +1,12 @@
#pragma once
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/expression/interpolate.hpp>
+#include <mbgl/style/expression/step.hpp>
+#include <mbgl/style/expression/find_zoom_curve.hpp>
+#include <mbgl/style/expression/value.hpp>
+#include <mbgl/style/expression/is_constant.hpp>
+#include <mbgl/style/function/convert.hpp>
#include <mbgl/style/function/composite_exponential_stops.hpp>
#include <mbgl/style/function/composite_interval_stops.hpp>
#include <mbgl/style/function/composite_categorical_stops.hpp>
@@ -43,110 +50,77 @@ public:
CompositeIntervalStops<T>,
CompositeCategoricalStops<T>>>;
- CompositeFunction(std::string property_, Stops stops_, optional<T> defaultValue_ = {})
- : property(std::move(property_)),
- stops(std::move(stops_)),
- defaultValue(std::move(defaultValue_)) {
- }
-
- struct CoveringRanges {
- float zoom;
- Range<float> coveringZoomRange;
- Range<InnerStops> coveringStopsRange;
- };
-
- // Return the relevant stop zoom values and inner stops that bracket a given zoom level. This
- // is the first step toward evaluating the function, and is used for in the course of both partial
- // evaluation of data-driven paint properties, and full evaluation of data-driven layout properties.
- CoveringRanges coveringRanges(float zoom) const {
- return stops.match(
- [&] (const auto& s) {
- assert(!s.stops.empty());
- auto minIt = s.stops.lower_bound(zoom);
- auto maxIt = s.stops.upper_bound(zoom);
-
- // lower_bound yields first element >= zoom, but we want the *last*
- // element <= zoom, so if we found a stop > zoom, back up by one.
- if (minIt != s.stops.begin() && minIt != s.stops.end() && minIt->first > zoom) {
- minIt--;
- }
-
- return CoveringRanges {
- zoom,
- Range<float> {
- minIt == s.stops.end() ? s.stops.rbegin()->first : minIt->first,
- maxIt == s.stops.end() ? s.stops.rbegin()->first : maxIt->first
- },
- Range<InnerStops> {
- s.innerStops(minIt == s.stops.end() ? s.stops.rbegin()->second : minIt->second),
- s.innerStops(maxIt == s.stops.end() ? s.stops.rbegin()->second : maxIt->second)
- }
- };
- }
- );
- }
-
- // Given a range of zoom values (typically two adjacent integer zoom levels, e.g. 5.0 and 6.0),
- // return the covering ranges for both. This is used in the course of partial evaluation for
- // data-driven paint properties.
- Range<CoveringRanges> rangeOfCoveringRanges(Range<float> zoomRange) {
- return Range<CoveringRanges> {
- coveringRanges(zoomRange.min),
- coveringRanges(zoomRange.max)
- };
+ CompositeFunction(std::unique_ptr<expression::Expression> expression_)
+ : expression(std::move(expression_)),
+ zoomCurve(expression::findZoomCurveChecked(expression.get()))
+ {
+ assert(!expression::isZoomConstant(*expression));
+ assert(!expression::isFeatureConstant(*expression));
}
- // Given the covering ranges for range of zoom values (typically two adjacent integer zoom levels,
- // e.g. 5.0 and 6.0), and a feature, return the results of fully evaluating the function for that
- // feature at each of the two zoom levels. These two results are what go into the paint vertex buffers
- // for vertices associated with this feature. The shader will interpolate between them at render time.
+ CompositeFunction(std::string property_, Stops stops_, optional<T> defaultValue_ = {})
+ : property(std::move(property_)),
+ stops(std::move(stops_)),
+ defaultValue(std::move(defaultValue_)),
+ expression(stops.match([&] (const auto& s) {
+ return expression::Convert::toExpression(property, s);
+ })),
+ zoomCurve(expression::findZoomCurveChecked(expression.get()))
+ {}
+
+ // Return the range obtained by evaluating the function at each of the zoom levels in zoomRange
template <class Feature>
- Range<T> evaluate(const Range<CoveringRanges>& ranges, const Feature& feature, T finalDefaultValue) {
- optional<Value> value = feature.getValue(property);
- if (!value) {
- return Range<T> {
- defaultValue.value_or(finalDefaultValue),
- defaultValue.value_or(finalDefaultValue)
- };
- }
+ Range<T> evaluate(const Range<float>& zoomRange, const Feature& feature, T finalDefaultValue) {
return Range<T> {
- evaluateFinal(ranges.min, *value, finalDefaultValue),
- evaluateFinal(ranges.max, *value, finalDefaultValue)
+ evaluate(zoomRange.min, feature, finalDefaultValue),
+ evaluate(zoomRange.max, feature, finalDefaultValue)
};
}
- // Fully evaluate the function for a zoom value and feature. This is used when evaluating data-driven
- // layout properties.
template <class Feature>
T evaluate(float zoom, const Feature& feature, T finalDefaultValue) const {
- optional<Value> value = feature.getValue(property);
- if (!value) {
- return defaultValue.value_or(finalDefaultValue);
+ const expression::EvaluationResult result = expression->evaluate(expression::EvaluationContext({zoom}, &feature));
+ if (result) {
+ const optional<T> typed = expression::fromExpressionValue<T>(*result);
+ return typed ? *typed : defaultValue ? *defaultValue : finalDefaultValue;
}
- return evaluateFinal(coveringRanges(zoom), *value, finalDefaultValue);
+ return defaultValue ? *defaultValue : finalDefaultValue;
+ }
+
+ float interpolationFactor(const Range<float>& inputLevels, const float inputValue) const {
+ return zoomCurve.match(
+ [&](const expression::InterpolateBase* z) {
+ return z->interpolationFactor(Range<double> { inputLevels.min, inputLevels.max }, inputValue);
+ },
+ [&](const expression::Step*) { return 0.0f; }
+ );
+ }
+
+ Range<float> getCoveringStops(const float lower, const float upper) const {
+ return zoomCurve.match(
+ [&](auto z) { return z->getCoveringStops(lower, upper); }
+ );
+ }
+
+ std::vector<optional<T>> possibleOutputs() const {
+ return expression::fromExpressionValues<T>(expression->possibleOutputs());
}
friend bool operator==(const CompositeFunction& lhs,
const CompositeFunction& rhs) {
- return std::tie(lhs.property, lhs.stops, lhs.defaultValue)
- == std::tie(rhs.property, rhs.stops, rhs.defaultValue);
+ return *lhs.expression == *rhs.expression;
}
+ const expression::Expression& getExpression() const { return *expression; }
+
std::string property;
Stops stops;
optional<T> defaultValue;
bool useIntegerZoom = false;
-
+
private:
- T evaluateFinal(const CoveringRanges& ranges, const Value& value, T finalDefaultValue) const {
- auto eval = [&] (const auto& s) {
- return s.evaluate(value).value_or(defaultValue.value_or(finalDefaultValue));
- };
- return util::interpolate(
- ranges.coveringStopsRange.min.match(eval),
- ranges.coveringStopsRange.max.match(eval),
- util::interpolationFactor(1.0f, ranges.coveringZoomRange, ranges.zoom));
- }
+ std::shared_ptr<expression::Expression> expression;
+ const variant<const expression::InterpolateBase*, const expression::Step*> zoomCurve;
};
} // namespace style
diff --git a/include/mbgl/style/function/convert.hpp b/include/mbgl/style/function/convert.hpp
new file mode 100644
index 0000000000..401a81d52e
--- /dev/null
+++ b/include/mbgl/style/function/convert.hpp
@@ -0,0 +1,356 @@
+#pragma once
+
+#include <mbgl/style/expression/array_assertion.hpp>
+#include <mbgl/style/expression/assertion.hpp>
+#include <mbgl/style/expression/case.hpp>
+#include <mbgl/style/expression/coalesce.hpp>
+#include <mbgl/style/expression/compound_expression.hpp>
+#include <mbgl/style/expression/coercion.hpp>
+#include <mbgl/style/expression/interpolate.hpp>
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/expression/literal.hpp>
+#include <mbgl/style/expression/match.hpp>
+#include <mbgl/style/expression/step.hpp>
+
+#include <mbgl/style/function/exponential_stops.hpp>
+#include <mbgl/style/function/interval_stops.hpp>
+#include <mbgl/style/function/categorical_stops.hpp>
+#include <mbgl/style/function/composite_exponential_stops.hpp>
+#include <mbgl/style/function/composite_interval_stops.hpp>
+#include <mbgl/style/function/composite_categorical_stops.hpp>
+#include <mbgl/style/function/identity_stops.hpp>
+
+#include <mbgl/util/enum.hpp>
+#include <mbgl/style/types.hpp>
+
+#include <string>
+
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+namespace detail {
+
+class ErrorExpression : public Expression {
+public:
+ ErrorExpression(std::string message_) : Expression(type::Error), message(std::move(message_)) {}
+ void eachChild(const std::function<void(const Expression&)>&) const override {}
+
+ bool operator==(const Expression& e) const override {
+ return dynamic_cast<const ErrorExpression*>(&e);
+ }
+
+ EvaluationResult evaluate(const EvaluationContext&) const override {
+ return EvaluationError{message};
+ }
+
+ std::vector<optional<Value>> possibleOutputs() const override {
+ return {};
+ }
+
+ std::string getOperator() const override { return "error"; }
+private:
+ std::string message;
+};
+
+} // namespace detail
+
+
+// Create expressions representing 'classic' (i.e. stop-based) style functions
+
+struct Convert {
+ template <typename T>
+ static std::unique_ptr<Literal> makeLiteral(const T& value) {
+ return std::make_unique<Literal>(Value(toExpressionValue(value)));
+ }
+
+ static std::unique_ptr<Expression> makeGet(type::Type type, const std::string& property) {
+ ParsingContext ctx;
+ std::vector<std::unique_ptr<Expression>> getArgs;
+ getArgs.push_back(makeLiteral(property));
+ ParseResult get = createCompoundExpression("get", std::move(getArgs), ctx);
+ assert(get);
+ assert(ctx.getErrors().size() == 0);
+
+ std::vector<std::unique_ptr<Expression>> assertionArgs;
+ assertionArgs.push_back(std::move(*get));
+
+ return std::make_unique<Assertion>(type, std::move(assertionArgs));
+ }
+
+ static std::unique_ptr<Expression> makeZoom() {
+ ParsingContext ctx;
+ ParseResult zoom = createCompoundExpression("zoom", std::vector<std::unique_ptr<Expression>>(), ctx);
+ assert(zoom);
+ assert(ctx.getErrors().size() == 0);
+ return std::move(*zoom);
+ }
+
+ static std::unique_ptr<Expression> makeError(std::string message) {
+ return std::make_unique<detail::ErrorExpression>(message);
+ }
+
+ template <typename OutputType>
+ static ParseResult makeInterpolate(type::Type type,
+ std::unique_ptr<Expression> input,
+ std::map<double, std::unique_ptr<Expression>> convertedStops,
+ typename Interpolate<OutputType>::Interpolator interpolator)
+ {
+ ParseResult curve = ParseResult(std::make_unique<Interpolate<OutputType>>(
+ std::move(type),
+ std::move(interpolator),
+ std::move(input),
+ std::move(convertedStops)
+ ));
+ assert(curve);
+ return std::move(*curve);
+ }
+
+ template <typename Key>
+ static ParseResult makeMatch(type::Type type,
+ std::unique_ptr<Expression> input,
+ std::map<CategoricalValue, std::unique_ptr<Expression>> stops) {
+ // match expression
+ typename Match<Key>::Branches branches;
+ for(auto it = stops.begin(); it != stops.end(); it++) {
+ assert(it->first.template is<Key>());
+ Key key = it->first.template get<Key>();
+ branches.emplace(
+ std::move(key),
+ std::move(it->second)
+ );
+ }
+
+ return ParseResult(std::make_unique<Match<Key>>(std::move(type),
+ std::move(input),
+ std::move(branches),
+ makeError("No matching label")));
+ }
+
+ static ParseResult makeCase(type::Type type,
+ std::unique_ptr<Expression> input,
+ std::map<CategoricalValue, std::unique_ptr<Expression>> stops) {
+ // case expression
+ std::vector<typename Case::Branch> branches;
+
+ auto it = stops.find(true);
+ std::unique_ptr<Expression> true_case = it == stops.end() ?
+ makeError("No matching label") :
+ std::move(it->second);
+
+ it = stops.find(false);
+ std::unique_ptr<Expression> false_case = it == stops.end() ?
+ makeError("No matching label") :
+ std::move(it->second);
+
+ branches.push_back(std::make_pair(std::move(input), std::move(true_case)));
+ return ParseResult(std::make_unique<Case>(std::move(type), std::move(branches), std::move(false_case)));
+ }
+
+ template <typename T>
+ static ParseResult fromCategoricalStops(std::map<CategoricalValue, T> stops, const std::string& property) {
+ assert(stops.size() > 0);
+
+ std::map<CategoricalValue, std::unique_ptr<Expression>> convertedStops;
+ for(const std::pair<CategoricalValue, T>& stop : stops) {
+ convertedStops.emplace(
+ stop.first,
+ makeLiteral(stop.second)
+ );
+ }
+
+ type::Type type = valueTypeToExpressionType<T>();
+
+ const CategoricalValue& firstKey = stops.begin()->first;
+ return firstKey.match(
+ [&](bool) {
+ return makeCase(type, makeGet(type::Boolean, property), std::move(convertedStops));
+ },
+ [&](const std::string&) {
+ return makeMatch<std::string>(type, makeGet(type::String, property), std::move(convertedStops));
+ },
+ [&](int64_t) {
+ return makeMatch<int64_t>(type, makeGet(type::Number, property), std::move(convertedStops));
+ }
+ );
+ }
+
+ template <typename T>
+ static std::map<double, std::unique_ptr<Expression>> convertStops(const std::map<float, T>& stops) {
+ std::map<double, std::unique_ptr<Expression>> convertedStops;
+ for(const auto& stop : stops) {
+ convertedStops.emplace(
+ stop.first,
+ makeLiteral(stop.second)
+ );
+ }
+ return convertedStops;
+ }
+
+ template <typename T>
+ static std::unique_ptr<Expression> toExpression(const ExponentialStops<T>& stops)
+ {
+ ParseResult e = makeInterpolate<typename ValueConverter<T>::ExpressionType>(
+ valueTypeToExpressionType<T>(),
+ makeZoom(),
+ convertStops(stops.stops),
+ ExponentialInterpolator(stops.base));
+ assert(e);
+ return std::move(*e);
+ }
+
+ template <typename T>
+ static std::unique_ptr<Expression> toExpression(const IntervalStops<T>& stops)
+ {
+ ParseResult e(std::make_unique<Step>(valueTypeToExpressionType<T>(),
+ makeZoom(),
+ convertStops(stops.stops)));
+ assert(e);
+ return std::move(*e);
+ }
+
+ template <typename T>
+ static std::unique_ptr<Expression> toExpression(const std::string& property,
+ const ExponentialStops<T>& stops)
+ {
+ ParseResult e = makeInterpolate<typename ValueConverter<T>::ExpressionType>(valueTypeToExpressionType<T>(),
+ makeGet(type::Number, property),
+ convertStops(stops.stops),
+ ExponentialInterpolator(stops.base));
+ assert(e);
+ return std::move(*e);
+ }
+
+ template <typename T>
+ static std::unique_ptr<Expression> toExpression(const std::string& property,
+ const IntervalStops<T>& stops)
+ {
+ std::unique_ptr<Expression> get = makeGet(type::Number, property);
+ ParseResult e(std::make_unique<Step>(valueTypeToExpressionType<T>(),
+ std::move(get),
+ convertStops(stops.stops)));
+ assert(e);
+ return std::move(*e);
+ }
+
+ template <typename T>
+ static std::unique_ptr<Expression> toExpression(const std::string& property,
+ const CategoricalStops<T>& stops)
+ {
+ ParseResult expr = fromCategoricalStops(stops.stops, property);
+ assert(expr);
+ return std::move(*expr);
+ }
+
+ // interpolatable zoom curve
+ template <typename T>
+ static typename std::enable_if_t<util::Interpolatable<T>::value,
+ ParseResult> makeZoomCurve(std::map<double, std::unique_ptr<Expression>> stops) {
+ return makeInterpolate<typename ValueConverter<T>::ExpressionType>(valueTypeToExpressionType<T>(),
+ makeZoom(),
+ std::move(stops),
+ ExponentialInterpolator(1.0));
+ }
+
+ // non-interpolatable zoom curve
+ template <typename T>
+ static typename std::enable_if_t<!util::Interpolatable<T>::value,
+ ParseResult> makeZoomCurve(std::map<double, std::unique_ptr<Expression>> stops) {
+ return ParseResult(std::make_unique<Step>(valueTypeToExpressionType<T>(), makeZoom(), std::move(stops)));
+ }
+
+ template <typename T>
+ static std::unique_ptr<Expression> toExpression(const std::string& property,
+ const CompositeExponentialStops<T>& stops)
+ {
+ std::map<double, std::unique_ptr<Expression>> outerStops;
+ for (const std::pair<float, std::map<float, T>>& stop : stops.stops) {
+ std::unique_ptr<Expression> get = makeGet(type::Number, property);
+ ParseResult innerInterpolate = makeInterpolate<typename ValueConverter<T>::ExpressionType>(valueTypeToExpressionType<T>(),
+ std::move(get),
+ convertStops(stop.second),
+ ExponentialInterpolator(stops.base));
+ assert(innerInterpolate);
+ outerStops.emplace(stop.first, std::move(*innerInterpolate));
+ }
+
+ ParseResult zoomCurve = makeZoomCurve<T>(std::move(outerStops));
+ assert(zoomCurve);
+ return std::move(*zoomCurve);
+ }
+
+ template <typename T>
+ static std::unique_ptr<Expression> toExpression(const std::string& property,
+ const CompositeIntervalStops<T>& stops)
+ {
+ std::map<double, std::unique_ptr<Expression>> outerStops;
+ for (const std::pair<float, std::map<float, T>>& stop : stops.stops) {
+ std::unique_ptr<Expression> get = makeGet(type::Number, property);
+ ParseResult innerInterpolate(std::make_unique<Step>(valueTypeToExpressionType<T>(),
+ std::move(get),
+ convertStops(stop.second)));
+ assert(innerInterpolate);
+ outerStops.emplace(stop.first, std::move(*innerInterpolate));
+ }
+
+ ParseResult zoomCurve = makeZoomCurve<T>(std::move(outerStops));
+ assert(zoomCurve);
+ return std::move(*zoomCurve);
+ }
+
+ template <typename T>
+ static std::unique_ptr<Expression> toExpression(const std::string& property,
+ const CompositeCategoricalStops<T>& stops)
+ {
+ std::map<double, std::unique_ptr<Expression>> outerStops;
+ for (const std::pair<float, std::map<CategoricalValue, T>>& stop : stops.stops) {
+ ParseResult innerInterpolate = fromCategoricalStops(stop.second, property);
+ assert(innerInterpolate);
+ outerStops.emplace(stop.first, std::move(*innerInterpolate));
+ }
+
+ ParseResult zoomCurve = makeZoomCurve<T>(std::move(outerStops));
+ assert(zoomCurve);
+ return std::move(*zoomCurve);
+ }
+
+
+ static std::unique_ptr<Expression> fromIdentityFunction(type::Type type, const std::string& property)
+ {
+ std::unique_ptr<Expression> input = type.match(
+ [&] (const type::StringType&) {
+ return makeGet(type::String, property);
+ },
+ [&] (const type::NumberType&) {
+ return makeGet(type::Number, property);
+ },
+ [&] (const type::BooleanType&) {
+ return makeGet(type::Boolean, property);
+ },
+ [&] (const type::ColorType&) {
+ std::vector<std::unique_ptr<Expression>> args;
+ args.push_back(makeGet(type::String, property));
+ return std::make_unique<Coercion>(type::Color, std::move(args));
+ },
+ [&] (const type::Array& arr) {
+ std::vector<std::unique_ptr<Expression>> getArgs;
+ getArgs.push_back(makeLiteral(property));
+ ParsingContext ctx;
+ ParseResult get = createCompoundExpression("get", std::move(getArgs), ctx);
+ assert(get);
+ assert(ctx.getErrors().size() == 0);
+ return std::make_unique<ArrayAssertion>(arr, std::move(*get));
+ },
+ [&] (const auto&) -> std::unique_ptr<Expression> {
+ return makeLiteral(Null);
+ }
+ );
+
+ return input;
+ }
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/function/source_function.hpp b/include/mbgl/style/function/source_function.hpp
index 9c2ad101ec..d3caa90ee5 100644
--- a/include/mbgl/style/function/source_function.hpp
+++ b/include/mbgl/style/function/source_function.hpp
@@ -1,5 +1,7 @@
#pragma once
+#include <mbgl/style/expression/is_constant.hpp>
+#include <mbgl/style/function/convert.hpp>
#include <mbgl/style/function/exponential_stops.hpp>
#include <mbgl/style/function/interval_stops.hpp>
#include <mbgl/style/function/categorical_stops.hpp>
@@ -27,33 +29,54 @@ public:
CategoricalStops<T>,
IdentityStops<T>>>;
+ SourceFunction(std::unique_ptr<expression::Expression> expression_)
+ : 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_)),
- defaultValue(std::move(defaultValue_)) {
- }
+ defaultValue(std::move(defaultValue_)),
+ expression(stops.match([&] (const IdentityStops<T>&) {
+ return expression::Convert::fromIdentityFunction(expression::valueTypeToExpressionType<T>(), property);
+ }, [&] (const auto& s) {
+ return expression::Convert::toExpression(property, s);
+ }))
+ {}
template <class Feature>
T evaluate(const Feature& feature, T finalDefaultValue) const {
- optional<Value> v = feature.getValue(property);
- if (!v) {
- return defaultValue.value_or(finalDefaultValue);
+ const expression::EvaluationResult result = expression->evaluate(expression::EvaluationContext(&feature));
+ if (result) {
+ const optional<T> typed = expression::fromExpressionValue<T>(*result);
+ return typed ? *typed : defaultValue ? *defaultValue : finalDefaultValue;
}
- return stops.match([&] (const auto& s) -> T {
- return s.evaluate(*v).value_or(defaultValue.value_or(finalDefaultValue));
- });
+ return defaultValue ? *defaultValue : finalDefaultValue;
+ }
+
+ std::vector<optional<T>> possibleOutputs() const {
+ return expression::fromExpressionValues<T>(expression->possibleOutputs());
}
friend bool operator==(const SourceFunction& lhs,
const SourceFunction& rhs) {
- return std::tie(lhs.property, lhs.stops, lhs.defaultValue)
- == std::tie(rhs.property, rhs.stops, rhs.defaultValue);
+ return *lhs.expression == *rhs.expression;
}
+ bool useIntegerZoom = false;
+
+ const expression::Expression& getExpression() const { return *expression; }
+
+ // retained for compatibility with pre-expression function API
std::string property;
Stops stops;
optional<T> defaultValue;
- bool useIntegerZoom = false;
+
+private:
+ std::shared_ptr<expression::Expression> expression;
};
} // namespace style
diff --git a/include/mbgl/style/heatmap_color_property_value.hpp b/include/mbgl/style/heatmap_color_property_value.hpp
new file mode 100644
index 0000000000..130639c6e2
--- /dev/null
+++ b/include/mbgl/style/heatmap_color_property_value.hpp
@@ -0,0 +1,49 @@
+#pragma once
+
+#include <mbgl/util/variant.hpp>
+#include <mbgl/style/undefined.hpp>
+#include <mbgl/style/function/camera_function.hpp>
+
+namespace mbgl {
+namespace style {
+
+/*
+ * Special-case implementation of (a subset of) the PropertyValue<T> interface
+ * used for building the HeatmapColor paint property traits class.
+ */
+class HeatmapColorPropertyValue {
+private:
+ std::shared_ptr<expression::Expression> value;
+
+ friend bool operator==(const HeatmapColorPropertyValue& lhs, const HeatmapColorPropertyValue& rhs) {
+ return (lhs.isUndefined() && rhs.isUndefined()) || (lhs.value && rhs.value && *(lhs.value) == *(rhs.value));
+ }
+
+ friend bool operator!=(const HeatmapColorPropertyValue& lhs, const HeatmapColorPropertyValue& rhs) {
+ return !(lhs == rhs);
+ }
+
+public:
+ HeatmapColorPropertyValue() : value(nullptr) {}
+ HeatmapColorPropertyValue(std::shared_ptr<expression::Expression> value_) : value(std::move(value_)) {}
+
+ bool isUndefined() const { return value.get() == nullptr; }
+
+ // noop, needed for batch evaluation of paint property values to compile
+ template <typename Evaluator>
+ Color evaluate(const Evaluator&, TimePoint = {}) const { return {}; }
+
+ Color evaluate(double heatmapDensity) const {
+ const auto result = value->evaluate(expression::EvaluationContext({}, nullptr, {heatmapDensity}));
+ return *expression::fromExpressionValue<Color>(*result);
+ }
+
+ bool isDataDriven() const { return false; }
+ bool hasDataDrivenPropertyDifference(const HeatmapColorPropertyValue&) const { return false; }
+
+ const expression::Expression& getExpression() const { return *value; }
+};
+
+
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/layer.hpp b/include/mbgl/style/layer.hpp
index c6a3c0e735..12494f5387 100644
--- a/include/mbgl/style/layer.hpp
+++ b/include/mbgl/style/layer.hpp
@@ -1,7 +1,7 @@
#pragma once
#include <mbgl/util/noncopyable.hpp>
-#include <mbgl/util/any.hpp>
+#include <mbgl/util/unique_any.hpp>
#include <mbgl/util/immutable.hpp>
#include <mbgl/style/layer_type.hpp>
#include <mbgl/style/types.hpp>
@@ -19,9 +19,11 @@ class LineLayer;
class CircleLayer;
class SymbolLayer;
class RasterLayer;
+class HillshadeLayer;
class BackgroundLayer;
class CustomLayer;
class FillExtrusionLayer;
+class HeatmapLayer;
class LayerObserver;
/**
@@ -86,10 +88,14 @@ public:
return std::forward<V>(visitor)(*as<RasterLayer>());
case LayerType::Background:
return std::forward<V>(visitor)(*as<BackgroundLayer>());
+ case LayerType::Hillshade:
+ return std::forward<V>(visitor)(*as<HillshadeLayer>());
case LayerType::Custom:
return std::forward<V>(visitor)(*as<CustomLayer>());
case LayerType::FillExtrusion:
return std::forward<V>(visitor)(*as<FillExtrusionLayer>());
+ case LayerType::Heatmap:
+ return std::forward<V>(visitor)(*as<HeatmapLayer>());
}
@@ -126,7 +132,7 @@ public:
// For use in SDK bindings, which store a reference to a platform-native peer
// object here, so that separately-obtained references to this object share
// identical platform-native peers.
- any peer;
+ util::unique_any peer;
};
} // namespace style
diff --git a/include/mbgl/style/layer_type.hpp b/include/mbgl/style/layer_type.hpp
index 66ff834eee..0987ea4d0a 100644
--- a/include/mbgl/style/layer_type.hpp
+++ b/include/mbgl/style/layer_type.hpp
@@ -9,10 +9,12 @@ enum class LayerType {
Circle,
Symbol,
Raster,
+ Hillshade,
Background,
Custom,
FillExtrusion,
+ Heatmap,
};
} // namespace style
-} // namespace mbgl \ No newline at end of file
+} // namespace mbgl
diff --git a/include/mbgl/style/layers/heatmap_layer.hpp b/include/mbgl/style/layers/heatmap_layer.hpp
new file mode 100644
index 0000000000..33d927ad38
--- /dev/null
+++ b/include/mbgl/style/layers/heatmap_layer.hpp
@@ -0,0 +1,86 @@
+// This file is generated. Do not edit.
+
+#pragma once
+
+#include <mbgl/style/layer.hpp>
+#include <mbgl/style/filter.hpp>
+#include <mbgl/style/property_value.hpp>
+#include <mbgl/style/data_driven_property_value.hpp>
+#include <mbgl/style/heatmap_color_property_value.hpp>
+
+#include <mbgl/util/color.hpp>
+
+namespace mbgl {
+namespace style {
+
+class TransitionOptions;
+
+class HeatmapLayer : public Layer {
+public:
+ HeatmapLayer(const std::string& layerID, const std::string& sourceID);
+ ~HeatmapLayer() final;
+
+ // Source
+ const std::string& getSourceID() const;
+ const std::string& getSourceLayer() const;
+ void setSourceLayer(const std::string& sourceLayer);
+
+ void setFilter(const Filter&);
+ const Filter& getFilter() const;
+
+ // Visibility
+ void setVisibility(VisibilityType) final;
+
+ // Zoom range
+ void setMinZoom(float) final;
+ void setMaxZoom(float) final;
+
+ // Paint properties
+
+ static DataDrivenPropertyValue<float> getDefaultHeatmapRadius();
+ DataDrivenPropertyValue<float> getHeatmapRadius() const;
+ void setHeatmapRadius(DataDrivenPropertyValue<float>);
+ void setHeatmapRadiusTransition(const TransitionOptions&);
+ TransitionOptions getHeatmapRadiusTransition() const;
+
+ static DataDrivenPropertyValue<float> getDefaultHeatmapWeight();
+ DataDrivenPropertyValue<float> getHeatmapWeight() const;
+ void setHeatmapWeight(DataDrivenPropertyValue<float>);
+ void setHeatmapWeightTransition(const TransitionOptions&);
+ TransitionOptions getHeatmapWeightTransition() const;
+
+ static PropertyValue<float> getDefaultHeatmapIntensity();
+ PropertyValue<float> getHeatmapIntensity() const;
+ void setHeatmapIntensity(PropertyValue<float>);
+ void setHeatmapIntensityTransition(const TransitionOptions&);
+ TransitionOptions getHeatmapIntensityTransition() const;
+
+ static HeatmapColorPropertyValue getDefaultHeatmapColor();
+ HeatmapColorPropertyValue getHeatmapColor() const;
+ void setHeatmapColor(HeatmapColorPropertyValue);
+ void setHeatmapColorTransition(const TransitionOptions&);
+ TransitionOptions getHeatmapColorTransition() const;
+
+ static PropertyValue<float> getDefaultHeatmapOpacity();
+ PropertyValue<float> getHeatmapOpacity() const;
+ void setHeatmapOpacity(PropertyValue<float>);
+ void setHeatmapOpacityTransition(const TransitionOptions&);
+ TransitionOptions getHeatmapOpacityTransition() const;
+
+ // Private implementation
+
+ class Impl;
+ const Impl& impl() const;
+
+ Mutable<Impl> mutableImpl() const;
+ HeatmapLayer(Immutable<Impl>);
+ std::unique_ptr<Layer> cloneRef(const std::string& id) const final;
+};
+
+template <>
+inline bool Layer::is<HeatmapLayer>() const {
+ return getType() == LayerType::Heatmap;
+}
+
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/layers/hillshade_layer.hpp b/include/mbgl/style/layers/hillshade_layer.hpp
new file mode 100644
index 0000000000..35664da46c
--- /dev/null
+++ b/include/mbgl/style/layers/hillshade_layer.hpp
@@ -0,0 +1,86 @@
+// This file is generated. Do not edit.
+
+#pragma once
+
+#include <mbgl/style/layer.hpp>
+#include <mbgl/style/filter.hpp>
+#include <mbgl/style/property_value.hpp>
+#include <mbgl/style/data_driven_property_value.hpp>
+
+#include <mbgl/util/color.hpp>
+
+namespace mbgl {
+namespace style {
+
+class TransitionOptions;
+
+class HillshadeLayer : public Layer {
+public:
+ HillshadeLayer(const std::string& layerID, const std::string& sourceID);
+ ~HillshadeLayer() final;
+
+ // Source
+ const std::string& getSourceID() const;
+
+ // Visibility
+ void setVisibility(VisibilityType) final;
+
+ // Zoom range
+ void setMinZoom(float) final;
+ void setMaxZoom(float) final;
+
+ // Paint properties
+
+ static PropertyValue<float> getDefaultHillshadeIlluminationDirection();
+ PropertyValue<float> getHillshadeIlluminationDirection() const;
+ void setHillshadeIlluminationDirection(PropertyValue<float>);
+ void setHillshadeIlluminationDirectionTransition(const TransitionOptions&);
+ TransitionOptions getHillshadeIlluminationDirectionTransition() const;
+
+ static PropertyValue<HillshadeIlluminationAnchorType> getDefaultHillshadeIlluminationAnchor();
+ PropertyValue<HillshadeIlluminationAnchorType> getHillshadeIlluminationAnchor() const;
+ void setHillshadeIlluminationAnchor(PropertyValue<HillshadeIlluminationAnchorType>);
+ void setHillshadeIlluminationAnchorTransition(const TransitionOptions&);
+ TransitionOptions getHillshadeIlluminationAnchorTransition() const;
+
+ static PropertyValue<float> getDefaultHillshadeExaggeration();
+ PropertyValue<float> getHillshadeExaggeration() const;
+ void setHillshadeExaggeration(PropertyValue<float>);
+ void setHillshadeExaggerationTransition(const TransitionOptions&);
+ TransitionOptions getHillshadeExaggerationTransition() const;
+
+ static PropertyValue<Color> getDefaultHillshadeShadowColor();
+ PropertyValue<Color> getHillshadeShadowColor() const;
+ void setHillshadeShadowColor(PropertyValue<Color>);
+ void setHillshadeShadowColorTransition(const TransitionOptions&);
+ TransitionOptions getHillshadeShadowColorTransition() const;
+
+ static PropertyValue<Color> getDefaultHillshadeHighlightColor();
+ PropertyValue<Color> getHillshadeHighlightColor() const;
+ void setHillshadeHighlightColor(PropertyValue<Color>);
+ void setHillshadeHighlightColorTransition(const TransitionOptions&);
+ TransitionOptions getHillshadeHighlightColorTransition() const;
+
+ static PropertyValue<Color> getDefaultHillshadeAccentColor();
+ PropertyValue<Color> getHillshadeAccentColor() const;
+ void setHillshadeAccentColor(PropertyValue<Color>);
+ void setHillshadeAccentColorTransition(const TransitionOptions&);
+ TransitionOptions getHillshadeAccentColorTransition() const;
+
+ // Private implementation
+
+ class Impl;
+ const Impl& impl() const;
+
+ Mutable<Impl> mutableImpl() const;
+ HillshadeLayer(Immutable<Impl>);
+ std::unique_ptr<Layer> cloneRef(const std::string& id) const final;
+};
+
+template <>
+inline bool Layer::is<HillshadeLayer>() const {
+ return getType() == LayerType::Hillshade;
+}
+
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/layers/layer.hpp.ejs b/include/mbgl/style/layers/layer.hpp.ejs
index 4ee5545247..6d40405ccd 100644
--- a/include/mbgl/style/layers/layer.hpp.ejs
+++ b/include/mbgl/style/layers/layer.hpp.ejs
@@ -11,6 +11,9 @@
#include <mbgl/style/filter.hpp>
#include <mbgl/style/property_value.hpp>
#include <mbgl/style/data_driven_property_value.hpp>
+<% if (type === 'heatmap') { -%>
+#include <mbgl/style/heatmap_color_property_value.hpp>
+<% } -%>
#include <mbgl/util/color.hpp>
@@ -35,7 +38,7 @@ public:
<% if (type !== 'background') { -%>
// Source
const std::string& getSourceID() const;
-<% if (type !== 'raster') { -%>
+<% if (type !== 'raster' && type !== 'hillshade') { -%>
const std::string& getSourceLayer() const;
void setSourceLayer(const std::string& sourceLayer);
diff --git a/include/mbgl/style/layers/symbol_layer.hpp b/include/mbgl/style/layers/symbol_layer.hpp
index a72baa0b4e..f068e2d060 100644
--- a/include/mbgl/style/layers/symbol_layer.hpp
+++ b/include/mbgl/style/layers/symbol_layer.hpp
@@ -118,9 +118,9 @@ public:
DataDrivenPropertyValue<std::string> getTextField() const;
void setTextField(DataDrivenPropertyValue<std::string>);
- static PropertyValue<std::vector<std::string>> getDefaultTextFont();
- PropertyValue<std::vector<std::string>> getTextFont() const;
- void setTextFont(PropertyValue<std::vector<std::string>>);
+ static DataDrivenPropertyValue<std::vector<std::string>> getDefaultTextFont();
+ DataDrivenPropertyValue<std::vector<std::string>> getTextFont() const;
+ void setTextFont(DataDrivenPropertyValue<std::vector<std::string>>);
static DataDrivenPropertyValue<float> getDefaultTextSize();
DataDrivenPropertyValue<float> getTextSize() const;
diff --git a/include/mbgl/style/source.hpp b/include/mbgl/style/source.hpp
index cec9619451..2f2838ade8 100644
--- a/include/mbgl/style/source.hpp
+++ b/include/mbgl/style/source.hpp
@@ -2,7 +2,7 @@
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/optional.hpp>
-#include <mbgl/util/any.hpp>
+#include <mbgl/util/unique_any.hpp>
#include <mbgl/util/immutable.hpp>
#include <mbgl/style/types.hpp>
@@ -17,6 +17,7 @@ namespace style {
class VectorSource;
class RasterSource;
+class RasterDEMSource;
class GeoJSONSource;
class SourceObserver;
@@ -76,7 +77,7 @@ public:
// For use in SDK bindings, which store a reference to a platform-native peer
// object here, so that separately-obtained references to this object share
// identical platform-native peers.
- any peer;
+ util::unique_any peer;
};
} // namespace style
diff --git a/include/mbgl/style/sources/custom_geometry_source.hpp b/include/mbgl/style/sources/custom_geometry_source.hpp
new file mode 100644
index 0000000000..9daeeb3819
--- /dev/null
+++ b/include/mbgl/style/sources/custom_geometry_source.hpp
@@ -0,0 +1,58 @@
+#pragma once
+
+#include <mbgl/style/source.hpp>
+#include <mbgl/util/geo.hpp>
+#include <mbgl/util/geojson.hpp>
+#include <mbgl/util/range.hpp>
+#include <mbgl/util/constants.hpp>
+
+namespace mbgl {
+
+class OverscaledTileID;
+class CanonicalTileID;
+template <class T>
+class Actor;
+
+namespace style {
+
+using TileFunction = std::function<void(const CanonicalTileID&)>;
+
+class CustomTileLoader;
+
+class CustomGeometrySource : public Source {
+public:
+ struct TileOptions {
+ double tolerance = 0.375;
+ uint16_t tileSize = util::tileSize;
+ uint16_t buffer = 128;
+ bool clip = false;
+ bool wrap = false;
+ };
+
+ struct Options {
+ TileFunction fetchTileFunction;
+ TileFunction cancelTileFunction;
+ Range<uint8_t> zoomRange = { 0, 18};
+ TileOptions tileOptions;
+ };
+public:
+ CustomGeometrySource(std::string id, CustomGeometrySource::Options options);
+ ~CustomGeometrySource() final;
+ void loadDescription(FileSource&) final;
+ void setTileData(const CanonicalTileID&, const GeoJSON&);
+ void invalidateTile(const CanonicalTileID&);
+ void invalidateRegion(const LatLngBounds&);
+ // Private implementation
+ class Impl;
+ const Impl& impl() const;
+private:
+ std::unique_ptr<Actor<CustomTileLoader>> loader;
+};
+
+template <>
+inline bool Source::is<CustomGeometrySource>() const {
+ return getType() == SourceType::CustomVector;
+}
+
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/sources/geojson_source.hpp b/include/mbgl/style/sources/geojson_source.hpp
index 5bdf1ef957..372e7c7a78 100644
--- a/include/mbgl/style/sources/geojson_source.hpp
+++ b/include/mbgl/style/sources/geojson_source.hpp
@@ -3,6 +3,7 @@
#include <mbgl/style/source.hpp>
#include <mbgl/util/geojson.hpp>
#include <mbgl/util/optional.hpp>
+#include <mbgl/util/constants.hpp>
namespace mbgl {
@@ -14,6 +15,7 @@ struct GeoJSONOptions {
// GeoJSON-VT options
uint8_t minzoom = 0;
uint8_t maxzoom = 18;
+ uint16_t tileSize = util::tileSize;
uint16_t buffer = 128;
double tolerance = 0.375;
diff --git a/include/mbgl/style/sources/raster_dem_source.hpp b/include/mbgl/style/sources/raster_dem_source.hpp
new file mode 100644
index 0000000000..82588613bc
--- /dev/null
+++ b/include/mbgl/style/sources/raster_dem_source.hpp
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <mbgl/style/sources/raster_source.hpp>
+#include <mbgl/util/tileset.hpp>
+#include <mbgl/util/variant.hpp>
+
+namespace mbgl {
+
+class AsyncRequest;
+
+namespace style {
+
+class RasterDEMSource : public RasterSource {
+public:
+ RasterDEMSource(std::string id, variant<std::string, Tileset> urlOrTileset, uint16_t tileSize);
+
+};
+
+template <>
+inline bool Source::is<RasterDEMSource>() const {
+ return getType() == SourceType::RasterDEM;
+}
+
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/sources/raster_source.hpp b/include/mbgl/style/sources/raster_source.hpp
index 7f23a7ca4b..5aa81aa979 100644
--- a/include/mbgl/style/sources/raster_source.hpp
+++ b/include/mbgl/style/sources/raster_source.hpp
@@ -12,8 +12,8 @@ namespace style {
class RasterSource : public Source {
public:
- RasterSource(std::string id, variant<std::string, Tileset> urlOrTileset, uint16_t tileSize);
- ~RasterSource() final;
+ RasterSource(std::string id, variant<std::string, Tileset> urlOrTileset, uint16_t tileSize, SourceType sourceType = SourceType::Raster);
+ ~RasterSource() override;
const variant<std::string, Tileset>& getURLOrTileset() const;
optional<std::string> getURL() const;
diff --git a/include/mbgl/style/types.hpp b/include/mbgl/style/types.hpp
index ec7358de8c..693972a72f 100644
--- a/include/mbgl/style/types.hpp
+++ b/include/mbgl/style/types.hpp
@@ -4,18 +4,20 @@
namespace mbgl {
-// TODO: should be in public source.hpp header and style namespace
+namespace style {
+
+// TODO: should be in public source.hpp header
enum class SourceType : uint8_t {
Vector,
Raster,
+ RasterDEM,
GeoJSON,
Video,
Annotations,
- Image
+ Image,
+ CustomVector
};
-namespace style {
-
enum class VisibilityType : bool {
Visible,
None,
@@ -36,6 +38,11 @@ enum class LineJoinType : uint8_t {
FlipBevel
};
+enum class HillshadeIlluminationAnchorType : bool {
+ Map,
+ Viewport
+};
+
enum class TranslateAnchorType : bool {
Map,
Viewport
diff --git a/include/mbgl/tile/tile_id.hpp b/include/mbgl/tile/tile_id.hpp
index 0457dd3a07..11fb5ce537 100644
--- a/include/mbgl/tile/tile_id.hpp
+++ b/include/mbgl/tile/tile_id.hpp
@@ -30,9 +30,9 @@ public:
CanonicalTileID scaledTo(uint8_t z) const;
std::array<CanonicalTileID, 4> children() const;
- const uint8_t z;
- const uint32_t x;
- const uint32_t y;
+ uint8_t z;
+ uint32_t x;
+ uint32_t y;
};
::std::ostream& operator<<(::std::ostream& os, const CanonicalTileID& rhs);
diff --git a/include/mbgl/util/any.hpp b/include/mbgl/util/any.hpp
deleted file mode 100644
index eea64b188a..0000000000
--- a/include/mbgl/util/any.hpp
+++ /dev/null
@@ -1,10 +0,0 @@
-#pragma once
-
-#include <linb/any.hpp>
-
-namespace mbgl {
-
-using linb::any;
-using linb::any_cast;
-
-} // namespace mbgl
diff --git a/include/mbgl/util/enum.hpp b/include/mbgl/util/enum.hpp
index 369ca86bfd..608befd3c4 100644
--- a/include/mbgl/util/enum.hpp
+++ b/include/mbgl/util/enum.hpp
@@ -11,6 +11,7 @@ namespace mbgl {
template <typename T>
class Enum {
public:
+ using Type = T;
static const char * toString(T);
static optional<T> toEnum(const std::string&);
};
diff --git a/include/mbgl/util/indexed_tuple.hpp b/include/mbgl/util/indexed_tuple.hpp
index fd0b931d36..ea4fe74624 100644
--- a/include/mbgl/util/indexed_tuple.hpp
+++ b/include/mbgl/util/indexed_tuple.hpp
@@ -1,9 +1,9 @@
#pragma once
#include <mbgl/util/type_list.hpp>
+#include <mbgl/util/tuple.hpp>
#include <type_traits>
-#include <tuple>
namespace mbgl {
@@ -24,20 +24,20 @@ template <class...> class IndexedTuple;
// for motivation.
//
template <class... Is, class... Ts>
-class IndexedTuple<TypeList<Is...>, TypeList<Ts...>> : public std::tuple<Ts...> {
+class IndexedTuple<TypeList<Is...>, TypeList<Ts...>> : public tuple_polyfill<Ts...> {
public:
static_assert(sizeof...(Is) == sizeof...(Ts), "IndexedTuple size mismatch");
- using std::tuple<Ts...>::tuple;
+ using tuple_polyfill<Ts...>::tuple;
template <class I>
auto& get() {
- return std::get<TypeIndex<I, Is...>::value>(*this);
+ return get_polyfill<TypeIndex<I, Is...>::value>(*this);
}
template <class I>
const auto& get() const {
- return std::get<TypeIndex<I, Is...>::value>(*this);
+ return get_polyfill<TypeIndex<I, Is...>::value>(*this);
}
template <class... Js, class... Us>
diff --git a/include/mbgl/util/interpolate.hpp b/include/mbgl/util/interpolate.hpp
index 6738987598..aff730a0a2 100644
--- a/include/mbgl/util/interpolate.hpp
+++ b/include/mbgl/util/interpolate.hpp
@@ -3,6 +3,7 @@
#include <mbgl/util/color.hpp>
#include <mbgl/util/range.hpp>
#include <mbgl/style/position.hpp>
+#include <mbgl/style/expression/value.hpp>
#include <array>
#include <vector>
@@ -47,6 +48,36 @@ public:
}
};
+
+// In order to accept Array<Number, N> as an output value for Curve
+// expressions, we need to have an interpolatable std::vector type.
+// However, style properties like line-dasharray are represented using
+// std::vector<float>, and should NOT be considered interpolatable.
+// So, we use std::vector<Value> to represent expression array values,
+// asserting that (a) the vectors are the same size, and (b) they contain
+// only numeric values. (These invariants should be relatively safe,
+// being enforced by the expression type system.)
+template<>
+struct Interpolator<std::vector<style::expression::Value>> {
+ std::vector<style::expression::Value> operator()(const std::vector<style::expression::Value>& a,
+ const std::vector<style::expression::Value>& b,
+ const double t) const {
+ assert(a.size() == b.size());
+ if (a.size() == 0) return {};
+ std::vector<style::expression::Value> result;
+ for (std::size_t i = 0; i < a.size(); i++) {
+ assert(a[i].template is<double>());
+ assert(b[i].template is<double>());
+ style::expression::Value item = interpolate(
+ a[i].template get<double>(),
+ b[i].template get<double>(),
+ t);
+ result.push_back(item);
+ }
+ return result;
+ }
+};
+
template <>
struct Interpolator<style::Position> {
public:
@@ -101,5 +132,7 @@ struct Interpolatable
std::true_type,
std::false_type> {};
+
+
} // namespace util
} // namespace mbgl
diff --git a/include/mbgl/util/optional.hpp b/include/mbgl/util/optional.hpp
index a9374a1b53..abec02dca9 100644
--- a/include/mbgl/util/optional.hpp
+++ b/include/mbgl/util/optional.hpp
@@ -7,4 +7,7 @@ namespace mbgl {
template <typename T>
using optional = std::experimental::optional<T>;
+using nullopt_t = std::experimental::nullopt_t;
+constexpr nullopt_t nullopt = std::experimental::nullopt;
+
} // namespace mbgl
diff --git a/include/mbgl/util/projection.hpp b/include/mbgl/util/projection.hpp
index a9c12e595b..b4a34521a4 100644
--- a/include/mbgl/util/projection.hpp
+++ b/include/mbgl/util/projection.hpp
@@ -96,8 +96,8 @@ public:
const double t2z = tileSize * std::pow(2, zoom);
Point<double> pt = project_(point, t2z);
// Flip y coordinate
- auto x = std::round(std::min(pt.x, t2z));
- auto y = std::round(std::min(t2z - pt.y, t2z));
+ auto x = ::round(std::min(pt.x, t2z));
+ auto y = ::round(std::min(t2z - pt.y, t2z));
return { x, y };
}
private:
diff --git a/include/mbgl/util/run_loop.hpp b/include/mbgl/util/run_loop.hpp
index acbea80273..381e3ae213 100644
--- a/include/mbgl/util/run_loop.hpp
+++ b/include/mbgl/util/run_loop.hpp
@@ -26,6 +26,11 @@ public:
New,
};
+ enum class Priority : bool {
+ Default = false,
+ High = true,
+ };
+
enum class Event : uint8_t {
None = 0,
Read = 1,
@@ -49,9 +54,14 @@ public:
// Invoke fn(args...) on this RunLoop.
template <class Fn, class... Args>
+ void invoke(Priority priority, Fn&& fn, Args&&... args) {
+ push(priority, WorkTask::make(std::forward<Fn>(fn), std::forward<Args>(args)...));
+ }
+
+ // Invoke fn(args...) on this RunLoop.
+ template <class Fn, class... Args>
void invoke(Fn&& fn, Args&&... args) {
- std::shared_ptr<WorkTask> task = WorkTask::make(std::forward<Fn>(fn), std::forward<Args>(args)...);
- push(task);
+ invoke(Priority::Default, std::forward<Fn>(fn), std::forward<Args>(args)...);
}
// Post the cancellable work fn(args...) to this RunLoop.
@@ -59,7 +69,7 @@ public:
std::unique_ptr<AsyncRequest>
invokeCancellable(Fn&& fn, Args&&... args) {
std::shared_ptr<WorkTask> task = WorkTask::make(std::forward<Fn>(fn), std::forward<Args>(args)...);
- push(task);
+ push(Priority::Default, task);
return std::make_unique<WorkRequest>(task);
}
@@ -76,24 +86,42 @@ private:
using Queue = std::queue<std::shared_ptr<WorkTask>>;
- void push(std::shared_ptr<WorkTask>);
+ // Wakes up the RunLoop so that it starts processing items in the queue.
+ void wake();
- void withMutex(std::function<void()>&& fn) {
+ // Adds a WorkTask to the queue, and wakes it up.
+ void push(Priority priority, std::shared_ptr<WorkTask> task) {
std::lock_guard<std::mutex> lock(mutex);
- fn();
+ if (priority == Priority::High) {
+ highPriorityQueue.emplace(std::move(task));
+ } else {
+ defaultQueue.emplace(std::move(task));
+ }
+ wake();
}
void process() {
- Queue queue_;
- withMutex([&] { queue_.swap(queue); });
-
- while (!queue_.empty()) {
- (*(queue_.front()))();
- queue_.pop();
+ std::shared_ptr<WorkTask> task;
+ std::unique_lock<std::mutex> lock(mutex);
+ while (true) {
+ if (!highPriorityQueue.empty()) {
+ task = std::move(highPriorityQueue.front());
+ highPriorityQueue.pop();
+ } else if (!defaultQueue.empty()) {
+ task = std::move(defaultQueue.front());
+ defaultQueue.pop();
+ } else {
+ break;
+ }
+ lock.unlock();
+ (*task)();
+ task.reset();
+ lock.lock();
}
}
- Queue queue;
+ Queue defaultQueue;
+ Queue highPriorityQueue;
std::mutex mutex;
std::unique_ptr<Impl> impl;
diff --git a/include/mbgl/util/string.hpp b/include/mbgl/util/string.hpp
index 82d317c620..13498ccb92 100644
--- a/include/mbgl/util/string.hpp
+++ b/include/mbgl/util/string.hpp
@@ -25,6 +25,10 @@ inline int stoi(const std::string &str)
return atoi(str.c_str());
}
+inline float stof(const std::string &str) {
+ return static_cast<float>(atof(str.c_str()));
+}
+
} // namespace std
#endif
@@ -65,5 +69,9 @@ inline std::string toString(std::exception_ptr error) {
}
}
+inline float stof(const std::string& str) {
+ return std::stof(str);
+}
+
} // namespace util
} // namespace mbgl
diff --git a/include/mbgl/util/thread.hpp b/include/mbgl/util/thread.hpp
index 672eebf6db..74e722b02d 100644
--- a/include/mbgl/util/thread.hpp
+++ b/include/mbgl/util/thread.hpp
@@ -103,7 +103,7 @@ public:
auto pausing = paused->get_future();
- loop->invoke([this] {
+ loop->invoke(RunLoop::Priority::High, [this] {
auto resuming = resumed->get_future();
paused->set_value();
resuming.get();
@@ -128,26 +128,9 @@ private:
MBGL_STORE_THREAD(tid);
void schedule(std::weak_ptr<Mailbox> mailbox) override {
- {
- std::lock_guard<std::mutex> lock(mutex);
- queue.push(mailbox);
- }
-
- loop->invoke([this] { receive(); });
- }
-
- void receive() {
- std::unique_lock<std::mutex> lock(mutex);
-
- auto mailbox = queue.front();
- queue.pop();
- lock.unlock();
-
- Mailbox::maybeReceive(mailbox);
+ loop->schedule(mailbox);
}
- std::mutex mutex;
- std::queue<std::weak_ptr<Mailbox>> queue;
std::thread thread;
std::unique_ptr<Actor<Object>> object;
diff --git a/include/mbgl/util/tileset.hpp b/include/mbgl/util/tileset.hpp
index 7bef0e89ed..ed2b907647 100644
--- a/include/mbgl/util/tileset.hpp
+++ b/include/mbgl/util/tileset.hpp
@@ -14,22 +14,28 @@ namespace mbgl {
class Tileset {
public:
enum class Scheme : bool { XYZ, TMS };
+ enum class DEMEncoding : bool { Mapbox, Terrarium };
std::vector<std::string> tiles;
Range<uint8_t> zoomRange;
std::string attribution;
Scheme scheme;
+ // DEMEncoding is not supported by the TileJSON spec
+ DEMEncoding encoding;
optional<LatLngBounds> bounds;
+
Tileset(std::vector<std::string> tiles_ = std::vector<std::string>(),
Range<uint8_t> zoomRange_ = { 0, util::DEFAULT_MAX_ZOOM },
std::string attribution_ = {},
- Scheme scheme_ = Scheme::XYZ)
+ Scheme scheme_ = Scheme::XYZ,
+ DEMEncoding encoding_ = DEMEncoding::Mapbox)
: tiles(std::move(tiles_)),
zoomRange(std::move(zoomRange_)),
attribution(std::move(attribution_)),
scheme(scheme_),
- bounds() {}
+ encoding(encoding_),
+ bounds() {};
// TileJSON also includes center and zoom but they are not used by mbgl.
@@ -37,6 +43,10 @@ public:
return std::tie(lhs.tiles, lhs.zoomRange, lhs.attribution, lhs.scheme, lhs.bounds)
== std::tie(rhs.tiles, rhs.zoomRange, rhs.attribution, rhs.scheme, rhs.bounds);
}
+
+ friend bool operator!=(const Tileset& lhs, const Tileset& rhs) {
+ return !(lhs == rhs);
+ }
};
} // namespace mbgl
diff --git a/include/mbgl/util/tuple.hpp b/include/mbgl/util/tuple.hpp
new file mode 100644
index 0000000000..3f6e80a8c7
--- /dev/null
+++ b/include/mbgl/util/tuple.hpp
@@ -0,0 +1,19 @@
+#pragma once
+
+// Polyfill needed by Windows because MSVC STL
+// is not compatible with our IndexedTuple code
+#if defined(_WINDOWS)
+
+#include <tao/tuple/tuple.hpp>
+
+#define get_polyfill tao::get
+#define tuple_polyfill tao::tuple
+
+#else
+
+#include <tuple>
+
+#define get_polyfill std::get
+#define tuple_polyfill std::tuple
+
+#endif
diff --git a/include/mbgl/util/unique_any.hpp b/include/mbgl/util/unique_any.hpp
new file mode 100644
index 0000000000..d488930a03
--- /dev/null
+++ b/include/mbgl/util/unique_any.hpp
@@ -0,0 +1,275 @@
+#pragma once
+
+#include <typeinfo>
+#include <type_traits>
+#include <stdexcept>
+namespace mbgl {
+namespace util {
+
+class bad_any_cast : public std::bad_cast {
+public:
+ const char* what() const noexcept override {
+ return "bad any_cast<>()";
+ }
+};
+/**
+ * A variant of `std::any` for non-copyable types.
+ *
+ * Use `unique_any` for non-copyable types (e.g. `std::unique_ptr<T>`)
+ * or to ensure that no copies are made of copyable types that are
+ * moved in.
+ *
+ * `uniqe_any` differs from `std::any` in that it does not support copy construction
+ * or copy assignment. It also does not require the contained type to be copy
+ * constructible.
+ *
+ * The `any_cast<T>()` methods work similar to `std::any_cast<T>()` except that
+ * non-copyable types may only be cast to references.
+ *
+ * Example usage:
+ * unique_any u1(3);
+ * auto u2 = unique_any(std::move(u1)); // u1 is moved from
+ * int i = any_cast<int>(u2);
+ *
+ * unique_any u2;
+ * u2 = std::unique_ptr<int>(new int);
+ * std::unique_ptr<int> iPtr = any_cast<std::unique_ptr<int>>(std::move(u2));
+ *
+ * Inspired by linb::any (https://github.com/thelink2012/any) and the
+ * libc++ implementation (https://github.com/llvm-mirror/libcxx).
+ */
+class unique_any final
+{
+public:
+ unique_any() = default;
+
+ //Copy constructor (deleted)
+ unique_any(const unique_any& rhs) = delete;
+
+ unique_any(unique_any&& rhs) : vtable(rhs.vtable) {
+ if (vtable) {
+ vtable->move(std::move(rhs.storage), storage);
+ }
+ rhs.vtable = nullptr;
+ }
+
+ // Constructs with a direct-initilizated object of type ValueType
+ template <typename ValueType,
+ typename _Vt = std::decay_t<ValueType>,
+ typename = std::enable_if_t<!std::is_same<_Vt, unique_any>::value> >
+ unique_any(ValueType&& value) {
+ create(std::forward<ValueType>(value));
+ }
+
+ ~unique_any() {
+ reset();
+ }
+
+ unique_any& operator=(unique_any&& rhs) {
+ unique_any(std::move(rhs)).swap(*this);
+ return *this;
+ }
+
+ template <class ValueType,
+ typename = std::enable_if_t<!std::is_same<std::decay_t<ValueType>, unique_any>::value> >
+ unique_any& operator=(ValueType&& rhs) {
+ unique_any(std::forward<ValueType>(rhs)).swap(*this);
+ return *this;
+ }
+
+ void reset() {
+ if (vtable) {
+ vtable->destroy(storage);
+ vtable = nullptr;
+ }
+ }
+
+ void swap(unique_any& rhs) {
+ if (this == &rhs) {
+ return;
+ } else {
+ unique_any tmp(std::move(rhs));
+ rhs.vtable = vtable;
+ if (rhs.vtable) {
+ rhs.vtable->move(std::move(storage), rhs.storage);
+ }
+ vtable = tmp.vtable;
+ if (vtable) {
+ vtable->move(std::move(tmp.storage), storage);
+ }
+ }
+ }
+
+ const std::type_info& type() const {
+ return !has_value()? typeid(void) : vtable->type();
+ }
+
+ bool has_value() const {
+ return vtable != nullptr;
+ }
+
+private:
+
+ union Storage {
+ using StackStorage = std::aligned_storage_t<3*sizeof(void*), std::alignment_of<void*>::value>;
+ Storage() = default;
+
+ void * dynamic { nullptr };
+ StackStorage stack;
+ };
+
+ template<typename T>
+ struct AllocateOnStack : std::integral_constant<bool,
+ sizeof(T) <= sizeof(Storage::stack)
+ && std::alignment_of<T>::value <= std::alignment_of<Storage::StackStorage>::value
+ && std::is_nothrow_move_constructible<T>::value> {
+ };
+
+ struct VTable {
+ virtual ~VTable() = default;
+ virtual void move(Storage&& src, Storage& dest) = 0;
+ virtual void destroy(Storage&) = 0;
+ virtual const std::type_info& type() = 0;
+ };
+
+ template <typename ValueType>
+ struct VTableHeap : public VTable {
+ void move(Storage&& src, Storage& dest) override {
+ destroy(dest);
+ dest.dynamic = src.dynamic;
+ }
+
+ void destroy(Storage& s) override {
+ if (s.dynamic) {
+ delete reinterpret_cast<ValueType*>(s.dynamic);
+ }
+ s.dynamic = nullptr;
+ }
+
+ const std::type_info& type() override {
+ return typeid(ValueType);
+ }
+ };
+
+ template <typename ValueType>
+ struct VTableStack : public VTable {
+ void move(Storage&& src, Storage& dest) override {
+ auto srcValue = reinterpret_cast<ValueType&&>(src.stack);
+ new (static_cast<void*>(&dest.stack)) ValueType(std::move(srcValue));
+ srcValue.~ValueType();
+ }
+
+ void destroy(Storage& s) override {
+ reinterpret_cast<ValueType&>(s.stack).~ValueType();
+ }
+
+ const std::type_info& type() override {
+ return typeid(ValueType);
+ }
+ };
+
+ template <typename ValueType>
+ static VTable* vtableForType() {
+ using VTableType = std::conditional_t<AllocateOnStack<ValueType>::value, VTableStack<ValueType>, VTableHeap<ValueType> >;
+ static VTableType vtable;
+ return &vtable;
+ }
+
+ template <typename ValueType, typename _Vt>
+ std::enable_if_t<AllocateOnStack<_Vt>::value>
+ createStorage(ValueType&& value) {
+ new (static_cast<void*>(&storage.stack)) _Vt(std::forward<ValueType>(value));
+ }
+
+ template <typename ValueType, typename _Vt>
+ std::enable_if_t<!AllocateOnStack<_Vt>::value>
+ createStorage(ValueType&& value) {
+ storage.dynamic = static_cast<void*>(new _Vt(std::forward<ValueType>(value)));
+ }
+
+ template <typename ValueType>
+ void create(ValueType&& value) {
+ using _Vt = std::decay_t<ValueType>;
+ vtable = vtableForType<_Vt>();
+ createStorage<ValueType, _Vt>(std::forward<ValueType>(value));
+ }
+
+ VTable* vtable { nullptr };
+ Storage storage;
+
+protected:
+ template<class ValueType>
+ friend const ValueType* any_cast(const unique_any* operand) ;
+
+ template<class ValueType>
+ friend ValueType* any_cast(unique_any* operand) ;
+
+ template<typename ValueType, typename _Vt = std::decay_t<ValueType> >
+ ValueType* cast()
+ {
+ return reinterpret_cast<ValueType *>(
+ AllocateOnStack<_Vt>::value ? &storage.stack : storage.dynamic);
+ }
+};
+
+template<typename ValueType>
+inline const ValueType* any_cast(const unique_any* any)
+{
+ return any_cast<ValueType>(const_cast<unique_any *>(any));
+}
+
+template<typename ValueType>
+inline ValueType* any_cast(unique_any* any)
+{
+ if(any == nullptr || any->type() != typeid(ValueType))
+ return nullptr;
+ else
+ return any->cast<ValueType>();
+}
+
+template<typename ValueType, typename _Vt = std::decay_t<ValueType> >
+inline ValueType any_cast(const unique_any& any)
+{
+ static_assert(std::is_constructible<ValueType, const _Vt&>::value,
+ "any_cast type can't construct copy of contained object");
+ auto temp = any_cast<_Vt>(&any);
+ if (temp == nullptr) {
+ throw bad_any_cast();
+ }
+ return static_cast<ValueType>(*temp);
+}
+
+template<typename ValueType, typename _Vt = std::decay_t<ValueType> >
+inline ValueType any_cast(unique_any& any)
+{
+ static_assert(std::is_constructible<ValueType, const _Vt&>::value,
+ "any_cast type can't construct copy of contained object");
+ auto temp = any_cast<_Vt>(&any);
+ if (temp == nullptr) {
+ throw bad_any_cast();
+ }
+ return static_cast<ValueType>(*temp);
+}
+
+template<typename ValueType, typename _Vt = std::remove_cv_t<ValueType> >
+inline ValueType any_cast(unique_any&& any)
+{
+ auto temp = any_cast<_Vt>(&any);
+ if (temp == nullptr) {
+ throw bad_any_cast();
+ }
+ auto retValue = static_cast<ValueType>(std::move(*temp));
+ any.reset();
+ return std::move(retValue);
+}
+
+} // namespace util
+} // namespace mbgl
+
+namespace std {
+
+inline void swap(mbgl::util::unique_any& lhs, mbgl::util::unique_any& rhs) {
+ lhs.swap(rhs);
+}
+
+} // namespace std
diff --git a/include/mbgl/util/unitbezier.hpp b/include/mbgl/util/unitbezier.hpp
index 6e644e2d1f..56d2ab6ead 100644
--- a/include/mbgl/util/unitbezier.hpp
+++ b/include/mbgl/util/unitbezier.hpp
@@ -26,6 +26,7 @@
#pragma once
#include <cmath>
+#include <tuple>
namespace mbgl {
namespace util {
@@ -41,6 +42,17 @@ struct UnitBezier {
, ay(1.0 - (3.0 * p1y) - (3.0 * (p2y - p1y) - (3.0 * p1y))) {
}
+ std::pair<double, double> getP1() const {
+ return { cx / 3.0, cy / 3.0 };
+ }
+
+ std::pair<double, double> getP2() const {
+ return {
+ (bx + (3.0 * cx / 3.0) + cx) / 3.0,
+ (by + (3.0 * cy / 3.0) + cy) / 3.0,
+ };
+ }
+
double sampleCurveX(double t) const {
// `ax t^3 + bx t^2 + cx t' expanded using Horner's rule.
return ((ax * t + bx) * t + cx) * t;
@@ -102,6 +114,11 @@ struct UnitBezier {
double solve(double x, double epsilon) const {
return sampleCurveY(solveCurveX(x, epsilon));
}
+
+ bool operator==(const UnitBezier& rhs) const {
+ return std::tie(cx, bx, ax, cy, by, ay) ==
+ std::tie(rhs.cx, rhs.bx, rhs.ax, rhs.cy, rhs.by, rhs.ay);
+ }
private:
const double cx;
diff --git a/mapbox-gl-js b/mapbox-gl-js
-Subproject 3634d945477cb7dbbf1e0bebf4cf51542821895
+Subproject 8a19f6079933817fd73eed71159130b8ac53a00
diff --git a/common/bench-icon.svg b/misc/bench-icon.svg
index 28df6f600d..28df6f600d 100644
--- a/common/bench-icon.svg
+++ b/misc/bench-icon.svg
diff --git a/common/ca-bundle.crt b/misc/ca-bundle.crt
index 256da1ff70..29691b9733 100644
--- a/common/ca-bundle.crt
+++ b/misc/ca-bundle.crt
@@ -1,7 +1,7 @@
##
## Bundle of CA Root Certificates
##
-## Certificate data from Mozilla as of: Fri Jan 27 10:57:10 2017 GMT
+## Certificate data from Mozilla as of: Wed Jan 17 00:48:54 2018 GMT
##
## This is a bundle of X.509 certificates of public Certificate Authorities
## (CA). These were automatically extracted from Mozilla's root certificates
@@ -14,7 +14,7 @@
## Just configure this file as the SSLCACertificateFile.
##
## Conversion done with mk-ca-bundle.pl version 1.27.
-## SHA256: dffa79e6aa993f558e82884abf7bb54bf440ab66ee91d82a27a627f6f2a4ace4
+## SHA256: a3ac15b98179dd2f3c5de076d10b1d53048754372f7207c2f327510cdd78fbd8
##
@@ -130,30 +130,6 @@ Y71k5h+3zvDyny67G7fyUIhzksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9H
RCwBXbsdtTLSR9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp
-----END CERTIFICATE-----
-AddTrust Low-Value Services Root
-================================
------BEGIN CERTIFICATE-----
-MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJTRTEUMBIGA1UEChML
-QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRU
-cnVzdCBDbGFzcyAxIENBIFJvb3QwHhcNMDAwNTMwMTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQsw
-CQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBO
-ZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwggEiMA0GCSqGSIb3DQEB
-AQUAA4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwzexODcEyPNwTXH+9ZOEQpnXvUGW2ulCDtbKRY6
-54eyNAbFvAWlA3yCyykQruGIgb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6ntGO0/7Gcrjyvd7ZWxbWr
-oulpOj0OM3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyldI+Yrsj5wAYi56xz36Uu+1LcsRVlIPo1
-Zmne3yzxbrww2ywkEtvrNTVokMsAsJchPXQhI2U0K7t4WaPW4XY5mqRJjox0r26kmqPZm9I4XJui
-GMx1I4S+6+JNM3GOGvDC+Mcdoq0Dlyz4zyXG9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8w
-HQYDVR0OBBYEFJWxtPCUtr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8EBTAD
-AQH/MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBlMQswCQYDVQQGEwJT
-RTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEw
-HwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxt
-ZBsfzQ3duQH6lmM0MkhHma6X7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0Ph
-iVYrqW9yTkkz43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9tTEv2dB8Xfjea4MY
-eDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL/bscVjby/rK25Xa71SJlpz/+0WatC7xr
-mYbvP33zGDLKe8bjq2RGlfgmadlVg3sslgf/WSxEo8bl6ancoWOAWiFeIc9TVPC6b4nbqKqVz4vj
-ccweGyBECMB6tkD9xOQ14R0WHNC8K47Wcdk=
------END CERTIFICATE-----
-
AddTrust External Root
======================
-----BEGIN CERTIFICATE-----
@@ -178,54 +154,6 @@ e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEXc4g/VhsxOBi0cQ+azcgOno4u
G+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5amnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ=
-----END CERTIFICATE-----
-AddTrust Public Services Root
-=============================
------BEGIN CERTIFICATE-----
-MIIEFTCCAv2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJTRTEUMBIGA1UEChML
-QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSAwHgYDVQQDExdBZGRU
-cnVzdCBQdWJsaWMgQ0EgUm9vdDAeFw0wMDA1MzAxMDQxNTBaFw0yMDA1MzAxMDQxNTBaMGQxCzAJ
-BgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5l
-dHdvcmsxIDAeBgNVBAMTF0FkZFRydXN0IFB1YmxpYyBDQSBSb290MIIBIjANBgkqhkiG9w0BAQEF
-AAOCAQ8AMIIBCgKCAQEA6Rowj4OIFMEg2Dybjxt+A3S72mnTRqX4jsIMEZBRpS9mVEBV6tsfSlbu
-nyNu9DnLoblv8n75XYcmYZ4c+OLspoH4IcUkzBEMP9smcnrHAZcHF/nXGCwwfQ56HmIexkvA/X1i
-d9NEHif2P0tEs7c42TkfYNVRknMDtABp4/MUTu7R3AnPdzRGULD4EfL+OHn3Bzn+UZKXC1sIXzSG
-Aa2Il+tmzV7R/9x98oTaunet3IAIx6eH1lWfl2royBFkuucZKT8Rs3iQhCBSWxHveNCD9tVIkNAw
-HM+A+WD+eeSI8t0A65RF62WUaUC6wNW0uLp9BBGo6zEFlpROWCGOn9Bg/QIDAQABo4HRMIHOMB0G
-A1UdDgQWBBSBPjfYkrAfd59ctKtzquf2NGAv+jALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB
-/zCBjgYDVR0jBIGGMIGDgBSBPjfYkrAfd59ctKtzquf2NGAv+qFopGYwZDELMAkGA1UEBhMCU0Ux
-FDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRUcnVzdCBUVFAgTmV0d29yazEgMB4G
-A1UEAxMXQWRkVHJ1c3QgUHVibGljIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBAAP3FUr4
-JNojVhaTdt02KLmuG7jD8WS6IBh4lSknVwW8fCr0uVFV2ocC3g8WFzH4qnkuCRO7r7IgGRLlk/lL
-+YPoRNWyQSW/iHVv/xD8SlTQX/D67zZzfRs2RcYhbbQVuE7PnFylPVoAjgbjPGsye/Kf8Lb93/Ao
-GEjwxrzQvzSAlsJKsW2Ox5BF3i9nrEUEo3rcVZLJR2bYGozH7ZxOmuASu7VqTITh4SINhwBk/ox9
-Yjllpu9CtoAlEmEBqCQTcAARJl/6NVDFSMwGR+gn2HCNX2TmoUQmXiLsks3/QppEIW1cxeMiHV9H
-EufOX1362KqxMy3ZdvJOOjMMK7MtkAY=
------END CERTIFICATE-----
-
-AddTrust Qualified Certificates Root
-====================================
------BEGIN CERTIFICATE-----
-MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJTRTEUMBIGA1UEChML
-QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSMwIQYDVQQDExpBZGRU
-cnVzdCBRdWFsaWZpZWQgQ0EgUm9vdDAeFw0wMDA1MzAxMDQ0NTBaFw0yMDA1MzAxMDQ0NTBaMGcx
-CzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQ
-IE5ldHdvcmsxIzAhBgNVBAMTGkFkZFRydXN0IFF1YWxpZmllZCBDQSBSb290MIIBIjANBgkqhkiG
-9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5B6a/twJWoekn0e+EV+vhDTbYjx5eLfpMLXsDBwqxBb/4Oxx
-64r1EW7tTw2R0hIYLUkVAcKkIhPHEWT/IhKauY5cLwjPcWqzZwFZ8V1G87B4pfYOQnrjfxvM0PC3
-KP0q6p6zsLkEqv32x7SxuCqg+1jxGaBvcCV+PmlKfw8i2O+tCBGaKZnhqkRFmhJePp1tUvznoD1o
-L/BLcHwTOK28FSXx1s6rosAx1i+f4P8UWfyEk9mHfExUE+uf0S0R+Bg6Ot4l2ffTQO2kBhLEO+GR
-wVY18BTcZTYJbqukB8c10cIDMzZbdSZtQvESa0NvS3GU+jQd7RNuyoB/mC9suWXY6QIDAQABo4HU
-MIHRMB0GA1UdDgQWBBQ5lYtii1zJ1IC6WA+XPxUIQ8yYpzALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/
-BAUwAwEB/zCBkQYDVR0jBIGJMIGGgBQ5lYtii1zJ1IC6WA+XPxUIQ8yYp6FrpGkwZzELMAkGA1UE
-BhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRUcnVzdCBUVFAgTmV0d29y
-azEjMCEGA1UEAxMaQWRkVHJ1c3QgUXVhbGlmaWVkIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQAD
-ggEBABmrder4i2VhlRO6aQTvhsoToMeqT2QbPxj2qC0sVY8FtzDqQmodwCVRLae/DLPt7wh/bDxG
-GuoYQ992zPlmhpwsaPXpF/gxsxjE1kh9I0xowX67ARRvxdlu3rsEQmr49lx95dr6h+sNNVJn0J6X
-dgWTP5XHAeZpVTh/EGGZyeNfpso+gmNIquIISD6q8rKFYqa0p9m9N5xotS1WfbC3P6CxB9bpT9ze
-RXEwMn8bLgn5v1Kh7sKAPgZcLlVAwRv1cEWw3F369nJad9Jjzc9YiQBCYz95OdBEsIJuQRno3eDB
-iFrRHnGTHyQwdOUeqN48Jzd/g66ed8/wMLH/S5noxqE=
------END CERTIFICATE-----
-
Entrust Root Certification Authority
====================================
-----BEGIN CERTIFICATE-----
@@ -273,27 +201,6 @@ XE0zX5IJL4hmXXeXxx12E6nV5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvm
Mw==
-----END CERTIFICATE-----
-GeoTrust Global CA 2
-====================
------BEGIN CERTIFICATE-----
-MIIDZjCCAk6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBEMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN
-R2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFsIENBIDIwHhcNMDQwMzA0MDUw
-MDAwWhcNMTkwMzA0MDUwMDAwWjBEMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5j
-LjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFsIENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
-ggEKAoIBAQDvPE1APRDfO1MA4Wf+lGAVPoWI8YkNkMgoI5kF6CsgncbzYEbYwbLVjDHZ3CB5JIG/
-NTL8Y2nbsSpr7iFY8gjpeMtvy/wWUsiRxP89c96xPqfCfWbB9X5SJBri1WeR0IIQ13hLTytCOb1k
-LUCgsBDTOEhGiKEMuzozKmKY+wCdE1l/bztyqu6mD4b5BWHqZ38MN5aL5mkWRxHCJ1kDs6ZgwiFA
-Vvqgx306E+PsV8ez1q6diYD3Aecs9pYrEw15LNnA5IZ7S4wMcoKK+xfNAGw6EzywhIdLFnopsk/b
-HdQL82Y3vdj2V7teJHq4PIu5+pIaGoSe2HSPqht/XvT+RSIhAgMBAAGjYzBhMA8GA1UdEwEB/wQF
-MAMBAf8wHQYDVR0OBBYEFHE4NvICMVNHK266ZUapEBVYIAUJMB8GA1UdIwQYMBaAFHE4NvICMVNH
-K266ZUapEBVYIAUJMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQUFAAOCAQEAA/e1K6tdEPx7
-srJerJsOflN4WT5CBP51o62sgU7XAotexC3IUnbHLB/8gTKY0UvGkpMzNTEv/NgdRN3ggX+d6Yvh
-ZJFiCzkIjKx0nVnZellSlxG5FntvRdOW2TF9AjYPnDtuzywNA0ZF66D0f0hExghAzN4bcLUprbqL
-OzRldRtxIR0sFAqwlpW41uryZfspuk/qkZN0abby/+Ea0AzRdoXLiiW9l14sbxWZJue2Kf8i7MkC
-x1YAzUm5s2x7UwQa4qjJqhIFI8LO57sEAszAR6LkxCkvW0VXiVHuPOtSCP8HNR6fNWpHSlaY0VqF
-H4z1Ir+rzoPz4iIprn2DQKi6bA==
------END CERTIFICATE-----
-
GeoTrust Universal CA
=====================
-----BEGIN CERTIFICATE-----
@@ -376,25 +283,6 @@ YQa7FkKMcPcw++DbZqMAAb3mLNqRX6BGi01qnD093QVG/na/oAo85ADmJ7f/hC3euiInlhBx6yLt
398znM/jra6O1I7mT1GvFpLgXPYHDw==
-----END CERTIFICATE-----
-Certum Root CA
-==============
------BEGIN CERTIFICATE-----
-MIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBMMRswGQYDVQQK
-ExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBDQTAeFw0wMjA2MTExMDQ2Mzla
-Fw0yNzA2MTExMDQ2MzlaMD4xCzAJBgNVBAYTAlBMMRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8u
-by4xEjAQBgNVBAMTCUNlcnR1bSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6x
-wS7TT3zNJc4YPk/EjG+AanPIW1H4m9LcuwBcsaD8dQPugfCI7iNS6eYVM42sLQnFdvkrOYCJ5JdL
-kKWoePhzQ3ukYbDYWMzhbGZ+nPMJXlVjhNWo7/OxLjBos8Q82KxujZlakE403Daaj4GIULdtlkIJ
-89eVgw1BS7Bqa/j8D35in2fE7SZfECYPCE/wpFcozo+47UX2bu4lXapuOb7kky/ZR6By6/qmW6/K
-Uz/iDsaWVhFu9+lmqSbYf5VT7QqFiLpPKaVCjF62/IUgAKpoC6EahQGcxEZjgoi2IrHu/qpGWX7P
-NSzVttpd90gzFFS269lvzs2I1qsb2pY7HVkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkq
-hkiG9w0BAQUFAAOCAQEAuI3O7+cUus/usESSbLQ5PqKEbq24IXfS1HeCh+YgQYHu4vgRt2PRFze+
-GXYkHAQaTOs9qmdvLdTN/mUxcMUbpgIKumB7bVjCmkn+YzILa+M6wKyrO7Do0wlRjBCDxjTgxSvg
-GrZgFCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1qCjqTE5s7FCMTY5w/
-0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5xO/fIR/RpbxXyEV6DHpx8Uq79AtoS
-qFlnGNu8cN2bsWntgM6JQEhqDjXKKWYVIZQs6GAqm4VKQPNriiTsBhYscw==
------END CERTIFICATE-----
-
Comodo AAA Services root
========================
-----BEGIN CERTIFICATE-----
@@ -419,56 +307,6 @@ Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2G9w84FoVxp7Z
12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg==
-----END CERTIFICATE-----
-Comodo Secure Services root
-===========================
------BEGIN CERTIFICATE-----
-MIIEPzCCAyegAwIBAgIBATANBgkqhkiG9w0BAQUFADB+MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS
-R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg
-TGltaXRlZDEkMCIGA1UEAwwbU2VjdXJlIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAw
-MDAwMFoXDTI4MTIzMTIzNTk1OVowfjELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFu
-Y2hlc3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxJDAi
-BgNVBAMMG1NlY3VyZSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP
-ADCCAQoCggEBAMBxM4KK0HDrc4eCQNUd5MvJDkKQ+d40uaG6EfQlhfPMcm3ye5drswfxdySRXyWP
-9nQ95IDC+DwN879A6vfIUtFyb+/Iq0G4bi4XKpVpDM3SHpR7LZQdqnXXs5jLrLxkU0C8j6ysNstc
-rbvd4JQX7NFc0L/vpZXJkMWwrPsbQ996CF23uPJAGysnnlDOXmWCiIxe004MeuoIkbY2qitC++rC
-oznl2yY4rYsK7hljxxwk3wN42ubqwUcaCwtGCd0C/N7Lh1/XMGNooa7cMqG6vv5Eq2i2pRcV/b3V
-p6ea5EQz6YiO/O1R65NxTq0B50SOqy3LqP4BSUjwwN3HaNiS/j0CAwEAAaOBxzCBxDAdBgNVHQ4E
-FgQUPNiTiMLAggnMAZkGkyDpnnAJY08wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w
-gYEGA1UdHwR6MHgwO6A5oDeGNWh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL1NlY3VyZUNlcnRpZmlj
-YXRlU2VydmljZXMuY3JsMDmgN6A1hjNodHRwOi8vY3JsLmNvbW9kby5uZXQvU2VjdXJlQ2VydGlm
-aWNhdGVTZXJ2aWNlcy5jcmwwDQYJKoZIhvcNAQEFBQADggEBAIcBbSMdflsXfcFhMs+P5/OKlFlm
-4J4oqF7Tt/Q05qo5spcWxYJvMqTpjOev/e/C6LlLqqP05tqNZSH7uoDrJiiFGv45jN5bBAS0VPmj
-Z55B+glSzAVIqMk/IQQezkhr/IXownuvf7fM+F86/TXGDe+X3EyrEeFryzHRbPtIgKvcnDe4IRRL
-DXE97IMzbtFuMhbsmMcWi1mmNKsFVy2T96oTy9IT4rcuO81rUBcJaD61JlfutuC23bkpgHl9j6Pw
-pCikFcSF9CfUa7/lXORlAnZUtOM3ZiTTGWHIUhDlizeauan5Hb/qmZJhlv8BzaFfDbxxvA6sCx1H
-RR3B7Hzs/Sk=
------END CERTIFICATE-----
-
-Comodo Trusted Services root
-============================
------BEGIN CERTIFICATE-----
-MIIEQzCCAyugAwIBAgIBATANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS
-R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg
-TGltaXRlZDElMCMGA1UEAwwcVHJ1c3RlZCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0wNDAxMDEw
-MDAwMDBaFw0yODEyMzEyMzU5NTlaMH8xCzAJBgNVBAYTAkdCMRswGQYDVQQIDBJHcmVhdGVyIE1h
-bmNoZXN0ZXIxEDAOBgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoMEUNvbW9kbyBDQSBMaW1pdGVkMSUw
-IwYDVQQDDBxUcnVzdGVkIENlcnRpZmljYXRlIFNlcnZpY2VzMIIBIjANBgkqhkiG9w0BAQEFAAOC
-AQ8AMIIBCgKCAQEA33FvNlhTWvI2VFeAxHQIIO0Yfyod5jWaHiWsnOWWfnJSoBVC21ndZHoa0Lh7
-3TkVvFVIxO06AOoxEbrycXQaZ7jPM8yoMa+j49d/vzMtTGo87IvDktJTdyR0nAducPy9C1t2ul/y
-/9c3S0pgePfw+spwtOpZqqPOSC+pw7ILfhdyFgymBwwbOM/JYrc/oJOlh0Hyt3BAd9i+FHzjqMB6
-juljatEPmsbS9Is6FARW1O24zG71++IsWL1/T2sr92AkWCTOJu80kTrV44HQsvAEAtdbtz6SrGsS
-ivnkBbA7kUlcsutT6vifR4buv5XAwAaf0lteERv0xwQ1KdJVXOTt6wIDAQABo4HJMIHGMB0GA1Ud
-DgQWBBTFe1i97doladL3WRaoszLAeydb9DAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB
-/zCBgwYDVR0fBHwwejA8oDqgOIY2aHR0cDovL2NybC5jb21vZG9jYS5jb20vVHJ1c3RlZENlcnRp
-ZmljYXRlU2VydmljZXMuY3JsMDqgOKA2hjRodHRwOi8vY3JsLmNvbW9kby5uZXQvVHJ1c3RlZENl
-cnRpZmljYXRlU2VydmljZXMuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQDIk4E7ibSvuIQSTI3S8Ntw
-uleGFTQQuS9/HrCoiWChisJ3DFBKmwCL2Iv0QeLQg4pKHBQGsKNoBXAxMKdTmw7pSqBYaWcOrp32
-pSxBvzwGa+RZzG0Q8ZZvH9/0BAKkn0U+yNj6NkZEUD+Cl5EfKNsYEYwq5GWDVxISjBc/lDb+XbDA
-BHcTuPQV1T84zJQ6VdCsmPW6AF/ghhmBeC8owH7TzEIK9a5QoNE+xqFx7D+gIIxmOom0jtTYsU0l
-R+4viMi14QVFwL4Ucd56/Y57fU0IlqUSc/AtyjcndBInTMu2l+nZrghtWjlA3QVHdWpaIbOjGM9O
-9y5Xt5hwXsjEeLBi
------END CERTIFICATE-----
-
QuoVadis Root CA
================
-----BEGIN CERTIFICATE-----
@@ -608,32 +446,6 @@ EtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLH
llpwrN9M
-----END CERTIFICATE-----
-UTN USERFirst Hardware Root CA
-==============================
------BEGIN CERTIFICATE-----
-MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UE
-BhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhl
-IFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAd
-BgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgx
-OTIyWjCBlzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0
-eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVz
-ZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwggEiMA0GCSqGSIb3
-DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlI
-wrthdBKWHTxqctU8EGc6Oe0rE81m65UJM6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFd
-tqdt++BxF2uiiPsA3/4aMXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8
-i4fDidNdoI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqIDsjf
-Pe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9KsyoUhbAgMBAAGjgbkw
-gbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFKFyXyYbKJhDlV0HN9WF
-lp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNF
-UkZpcnN0LUhhcmR3YXJlLmNybDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUF
-BwMGBggrBgEFBQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM
-//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28GpgoiskliCE7/yMgUsogW
-XecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gECJChicsZUN/KHAG8HQQZexB2
-lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kn
-iCrVWFCVH/A7HFe7fRQ5YiuayZSSKqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67
-nfhmqA==
------END CERTIFICATE-----
-
Camerfirma Chambers of Commerce Root
====================================
-----BEGIN CERTIFICATE-----
@@ -760,47 +572,6 @@ KVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEYWQPJIrSPnNVeKtelttQKbfi3
QBFGmh95DmK/D5fs4C8fF5Q=
-----END CERTIFICATE-----
-StartCom Certification Authority
-================================
------BEGIN CERTIFICATE-----
-MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMN
-U3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmlu
-ZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0
-NjM2WhcNMzYwOTE3MTk0NjM2WjB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRk
-LjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMg
-U3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
-ggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZkpMyONvg45iPwbm2xPN1y
-o4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rfOQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/
-Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/CJi/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/d
-eMotHweXMAEtcnn6RtYTKqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt
-2PZE4XNiHzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMMAv+Z
-6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w+2OqqGwaVLRcJXrJ
-osmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/
-untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVc
-UjyJthkqcwEKDwOzEmDyei+B26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT
-37uMdBNSSwIDAQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE
-FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9jZXJ0LnN0YXJ0
-Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3JsLnN0YXJ0Y29tLm9yZy9zZnNj
-YS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFMBgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUH
-AgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRw
-Oi8vY2VydC5zdGFydGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYg
-U3RhcnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlhYmlsaXR5
-LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2YgdGhlIFN0YXJ0Q29tIENl
-cnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFpbGFibGUgYXQgaHR0cDovL2NlcnQuc3Rh
-cnRjb20ub3JnL3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilT
-dGFydENvbSBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOC
-AgEAFmyZ9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8jhvh
-3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUWFjgKXlf2Ysd6AgXm
-vB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJzewT4F+irsfMuXGRuczE6Eri8sxHk
-fY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3
-fsNrarnDy0RLrHiQi+fHLB5LEUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZ
-EoalHmdkrQYuL6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq
-yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuCO3NJo2pXh5Tl
-1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6Vum0ABj6y6koQOdjQK/W/7HW/
-lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkyShNOsF/5oirpt9P/FlUQqmMGqz9IgcgA38coro
-g14=
------END CERTIFICATE-----
-
Taiwan GRCA
===========
-----BEGIN CERTIFICATE-----
@@ -831,38 +602,6 @@ CZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDeLMDDav7v3Aun+kbfYNucpllQdSNpc5Oy
+fwC00fmcc4QAu4njIT/rEUNE1yDMuAlpYYsfPQS
-----END CERTIFICATE-----
-Swisscom Root CA 1
-==================
------BEGIN CERTIFICATE-----
-MIIF2TCCA8GgAwIBAgIQXAuFXAvnWUHfV8w/f52oNjANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQG
-EwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0YWwgQ2VydGlmaWNhdGUgU2Vy
-dmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3QgQ0EgMTAeFw0wNTA4MTgxMjA2MjBaFw0yNTA4
-MTgyMjA2MjBaMGQxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGln
-aXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAxMIIC
-IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0LmwqAzZuz8h+BvVM5OAFmUgdbI9m2BtRsiM
-MW8Xw/qabFbtPMWRV8PNq5ZJkCoZSx6jbVfd8StiKHVFXqrWW/oLJdihFvkcxC7mlSpnzNApbjyF
-NDhhSbEAn9Y6cV9Nbc5fuankiX9qUvrKm/LcqfmdmUc/TilftKaNXXsLmREDA/7n29uj/x2lzZAe
-AR81sH8A25Bvxn570e56eqeqDFdvpG3FEzuwpdntMhy0XmeLVNxzh+XTF3xmUHJd1BpYwdnP2IkC
-b6dJtDZd0KTeByy2dbcokdaXvij1mB7qWybJvbCXc9qukSbraMH5ORXWZ0sKbU/Lz7DkQnGMU3nn
-7uHbHaBuHYwadzVcFh4rUx80i9Fs/PJnB3r1re3WmquhsUvhzDdf/X/NTa64H5xD+SpYVUNFvJbN
-cA78yeNmuk6NO4HLFWR7uZToXTNShXEuT46iBhFRyePLoW4xCGQMwtI89Tbo19AOeCMgkckkKmUp
-WyL3Ic6DXqTz3kvTaI9GdVyDCW4pa8RwjPWd1yAv/0bSKzjCL3UcPX7ape8eYIVpQtPM+GP+HkM5
-haa2Y0EQs3MevNP6yn0WR+Kn1dCjigoIlmJWbjTb2QK5MHXjBNLnj8KwEUAKrNVxAmKLMb7dxiNY
-MUJDLXT5xp6mig/p/r+D5kNXJLrvRjSq1xIBOO0CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYw
-HQYDVR0hBBYwFDASBgdghXQBUwABBgdghXQBUwABMBIGA1UdEwEB/wQIMAYBAf8CAQcwHwYDVR0j
-BBgwFoAUAyUv3m+CATpcLNwroWm1Z9SM0/0wHQYDVR0OBBYEFAMlL95vggE6XCzcK6FptWfUjNP9
-MA0GCSqGSIb3DQEBBQUAA4ICAQA1EMvspgQNDQ/NwNurqPKIlwzfky9NfEBWMXrrpA9gzXrzvsMn
-jgM+pN0S734edAY8PzHyHHuRMSG08NBsl9Tpl7IkVh5WwzW9iAUPWxAaZOHHgjD5Mq2eUCzneAXQ
-MbFamIp1TpBcahQq4FJHgmDmHtqBsfsUC1rxn9KVuj7QG9YVHaO+htXbD8BJZLsuUBlL0iT43R4H
-VtA4oJVwIHaM190e3p9xxCPvgxNcoyQVTSlAPGrEqdi3pkSlDfTgnXceQHAm/NrZNuR55LU/vJtl
-vrsRls/bxig5OgjOR1tTWsWZ/l2p3e9M1MalrQLmjAcSHm8D0W+go/MpvRLHUKKwf4ipmXeascCl
-OS5cfGniLLDqN2qk4Vrh9VDlg++luyqI54zb/W1elxmofmZ1a3Hqv7HHb6D0jqTsNFFbjCYDcKF3
-1QESVwA12yPeDooomf2xEG9L/zgtYE4snOtnta1J7ksfrK/7DZBaZmBwXarNeNQk7shBoJMBkpxq
-nvy5JMWzFYJ+vq6VK+uxwNrjAWALXmmshFZhvnEX/h0TD/7Gh0Xp/jKgGg0TpJRVcaUWi7rKibCy
-x/yP2FS1k2Kdzs9Z+z0YzirLNRWCXf9UIltxUvu3yf5gmwBBZPCqKuy2QkPOiWaByIufOVQDJdMW
-NY6E0F/6MBr1mmz0DlP5OlvRHA==
------END CERTIFICATE-----
-
DigiCert Assured ID Root CA
===========================
-----BEGIN CERTIFICATE-----
@@ -1220,33 +959,6 @@ wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHNpGxlaKFJdlxD
ydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey
-----END CERTIFICATE-----
-WellsSecure Public Root Certificate Authority
-=============================================
------BEGIN CERTIFICATE-----
-MIIEvTCCA6WgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoM
-F1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYw
-NAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcN
-MDcxMjEzMTcwNzU0WhcNMjIxMjE0MDAwNzU0WjCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dl
-bGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYD
-VQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G
-CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDub7S9eeKPCCGeOARBJe+rWxxTkqxtnt3CxC5FlAM1
-iGd0V+PfjLindo8796jE2yljDpFoNoqXjopxaAkH5OjUDk/41itMpBb570OYj7OeUt9tkTmPOL13
-i0Nj67eT/DBMHAGTthP796EfvyXhdDcsHqRePGj4S78NuR4uNuip5Kf4D8uCdXw1LSLWwr8L87T8
-bJVhHlfXBIEyg1J55oNjz7fLY4sR4r1e6/aN7ZVyKLSsEmLpSjPmgzKuBXWVvYSV2ypcm44uDLiB
-K0HmOFafSZtsdvqKXfcBeYF8wYNABf5x/Qw/zE5gCQ5lRxAvAcAFP4/4s0HvWkJ+We/SlwxlAgMB
-AAGjggE0MIIBMDAPBgNVHRMBAf8EBTADAQH/MDkGA1UdHwQyMDAwLqAsoCqGKGh0dHA6Ly9jcmwu
-cGtpLndlbGxzZmFyZ28uY29tL3dzcHJjYS5jcmwwDgYDVR0PAQH/BAQDAgHGMB0GA1UdDgQWBBQm
-lRkQ2eihl5H/3BnZtQQ+0nMKajCBsgYDVR0jBIGqMIGngBQmlRkQ2eihl5H/3BnZtQQ+0nMKaqGB
-i6SBiDCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRww
-GgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMg
-Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCAQEwDQYJKoZIhvcNAQEFBQADggEBALkVsUSRzCPI
-K0134/iaeycNzXK7mQDKfGYZUMbVmO2rvwNa5U3lHshPcZeG1eMd/ZDJPHV3V3p9+N701NX3leZ0
-bh08rnyd2wIDBSxxSyU+B+NemvVmFymIGjifz6pBA4SXa5M4esowRBskRDPQ5NHcKDj0E0M1NSlj
-qHyita04pO2t/caaH/+Xc/77szWnk4bGdpEA5qxRFsQnMlzbc9qlk1eOPm01JghZ1edE13YgY+es
-E2fDbbFwRnzVlhE9iW9dqKHrjQrawx0zbKPqZxmamX9LPYNRKh3KL4YMon4QLSvUFpULB6ouFJJJ
-tylv2G0xffX8oRAHh84vWdw+WNs=
------END CERTIFICATE-----
-
COMODO ECC Certification Authority
==================================
-----BEGIN CERTIFICATE-----
@@ -1308,46 +1020,6 @@ hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZiFj4A4xylNoEY
okxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ/L7fCg0=
-----END CERTIFICATE-----
-Microsec e-Szigno Root CA
-=========================
------BEGIN CERTIFICATE-----
-MIIHqDCCBpCgAwIBAgIRAMy4579OKRr9otxmpRwsDxEwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UE
-BhMCSFUxETAPBgNVBAcTCEJ1ZGFwZXN0MRYwFAYDVQQKEw1NaWNyb3NlYyBMdGQuMRQwEgYDVQQL
-EwtlLVN6aWdubyBDQTEiMCAGA1UEAxMZTWljcm9zZWMgZS1Temlnbm8gUm9vdCBDQTAeFw0wNTA0
-MDYxMjI4NDRaFw0xNzA0MDYxMjI4NDRaMHIxCzAJBgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVz
-dDEWMBQGA1UEChMNTWljcm9zZWMgTHRkLjEUMBIGA1UECxMLZS1Temlnbm8gQ0ExIjAgBgNVBAMT
-GU1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
-AQDtyADVgXvNOABHzNuEwSFpLHSQDCHZU4ftPkNEU6+r+ICbPHiN1I2uuO/TEdyB5s87lozWbxXG
-d36hL+BfkrYn13aaHUM86tnsL+4582pnS4uCzyL4ZVX+LMsvfUh6PXX5qqAnu3jCBspRwn5mS6/N
-oqdNAoI/gqyFxuEPkEeZlApxcpMqyabAvjxWTHOSJ/FrtfX9/DAFYJLG65Z+AZHCabEeHXtTRbjc
-QR/Ji3HWVBTji1R4P770Yjtb9aPs1ZJ04nQw7wHb4dSrmZsqa/i9phyGI0Jf7Enemotb9HI6QMVJ
-PqW+jqpx62z69Rrkav17fVVA71hu5tnVvCSrwe+3AgMBAAGjggQ3MIIEMzBnBggrBgEFBQcBAQRb
-MFkwKAYIKwYBBQUHMAGGHGh0dHBzOi8vcmNhLmUtc3ppZ25vLmh1L29jc3AwLQYIKwYBBQUHMAKG
-IWh0dHA6Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNydDAPBgNVHRMBAf8EBTADAQH/MIIBcwYD
-VR0gBIIBajCCAWYwggFiBgwrBgEEAYGoGAIBAQEwggFQMCgGCCsGAQUFBwIBFhxodHRwOi8vd3d3
-LmUtc3ppZ25vLmh1L1NaU1ovMIIBIgYIKwYBBQUHAgIwggEUHoIBEABBACAAdABhAG4A+gBzAO0A
-dAB2AOEAbgB5ACAA6QByAHQAZQBsAG0AZQB6AOkAcwDpAGgAZQB6ACAA6QBzACAAZQBsAGYAbwBn
-AGEAZADhAHMA4QBoAG8AegAgAGEAIABTAHoAbwBsAGcA4QBsAHQAYQB0APMAIABTAHoAbwBsAGcA
-4QBsAHQAYQB0AOEAcwBpACAAUwB6AGEAYgDhAGwAeQB6AGEAdABhACAAcwB6AGUAcgBpAG4AdAAg
-AGsAZQBsAGwAIABlAGwAagDhAHIAbgBpADoAIABoAHQAdABwADoALwAvAHcAdwB3AC4AZQAtAHMA
-egBpAGcAbgBvAC4AaAB1AC8AUwBaAFMAWgAvMIHIBgNVHR8EgcAwgb0wgbqggbeggbSGIWh0dHA6
-Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNybIaBjmxkYXA6Ly9sZGFwLmUtc3ppZ25vLmh1L0NO
-PU1pY3Jvc2VjJTIwZS1Temlnbm8lMjBSb290JTIwQ0EsT1U9ZS1Temlnbm8lMjBDQSxPPU1pY3Jv
-c2VjJTIwTHRkLixMPUJ1ZGFwZXN0LEM9SFU/Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlzdDtiaW5h
-cnkwDgYDVR0PAQH/BAQDAgEGMIGWBgNVHREEgY4wgYuBEGluZm9AZS1zemlnbm8uaHWkdzB1MSMw
-IQYDVQQDDBpNaWNyb3NlYyBlLVN6aWduw7MgUm9vdCBDQTEWMBQGA1UECwwNZS1TemlnbsOzIEhT
-WjEWMBQGA1UEChMNTWljcm9zZWMgS2Z0LjERMA8GA1UEBxMIQnVkYXBlc3QxCzAJBgNVBAYTAkhV
-MIGsBgNVHSMEgaQwgaGAFMegSXUWYYTbMUuE0vE3QJDvTtz3oXakdDByMQswCQYDVQQGEwJIVTER
-MA8GA1UEBxMIQnVkYXBlc3QxFjAUBgNVBAoTDU1pY3Jvc2VjIEx0ZC4xFDASBgNVBAsTC2UtU3pp
-Z25vIENBMSIwIAYDVQQDExlNaWNyb3NlYyBlLVN6aWdubyBSb290IENBghEAzLjnv04pGv2i3Gal
-HCwPETAdBgNVHQ4EFgQUx6BJdRZhhNsxS4TS8TdAkO9O3PcwDQYJKoZIhvcNAQEFBQADggEBANMT
-nGZjWS7KXHAM/IO8VbH0jgdsZifOwTsgqRy7RlRw7lrMoHfqaEQn6/Ip3Xep1fvj1KcExJW4C+FE
-aGAHQzAxQmHl7tnlJNUb3+FKG6qfx1/4ehHqE5MAyopYse7tDk2016g2JnzgOsHVV4Lxdbb9iV/a
-86g4nzUGCM4ilb7N1fy+W955a9x6qWVmvrElWl/tftOsRm1M9DKHtCAE4Gx4sHfRhUZLphK3dehK
-yVZs15KrnfVJONJPU+NVkBHbmJbGSfI+9J8b4PeI3CVimUTYc78/MPMMNz7UwiiAc7EBt51alhQB
-S6kRnSlqLtBdgcDPsiBDxwPgN05dCtxZICU=
------END CERTIFICATE-----
-
Certigna
========
-----BEGIN CERTIFICATE-----
@@ -1445,34 +1117,6 @@ sP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTEW9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmOD
BCEIZ43ygknQW/2xzQ+DhNQ+IIX3Sj0rnP0qCglN6oH4EZw=
-----END CERTIFICATE-----
-T\xc3\x9c\x42\xC4\xB0TAK UEKAE K\xC3\xB6k Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1 - S\xC3\xBCr\xC3\xBCm 3
-=============================================================================================================================
------BEGIN CERTIFICATE-----
-MIIFFzCCA/+gAwIBAgIBETANBgkqhkiG9w0BAQUFADCCASsxCzAJBgNVBAYTAlRSMRgwFgYDVQQH
-DA9HZWJ6ZSAtIEtvY2FlbGkxRzBFBgNVBAoMPlTDvHJraXllIEJpbGltc2VsIHZlIFRla25vbG9q
-aWsgQXJhxZ90xLFybWEgS3VydW11IC0gVMOcQsSwVEFLMUgwRgYDVQQLDD9VbHVzYWwgRWxla3Ry
-b25payB2ZSBLcmlwdG9sb2ppIEFyYcWfdMSxcm1hIEVuc3RpdMO8c8O8IC0gVUVLQUUxIzAhBgNV
-BAsMGkthbXUgU2VydGlmaWthc3lvbiBNZXJrZXppMUowSAYDVQQDDEFUw5xCxLBUQUsgVUVLQUUg
-S8O2ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSAtIFPDvHLDvG0gMzAeFw0wNzA4
-MjQxMTM3MDdaFw0xNzA4MjExMTM3MDdaMIIBKzELMAkGA1UEBhMCVFIxGDAWBgNVBAcMD0dlYnpl
-IC0gS29jYWVsaTFHMEUGA1UECgw+VMO8cmtpeWUgQmlsaW1zZWwgdmUgVGVrbm9sb2ppayBBcmHF
-n3TEsXJtYSBLdXJ1bXUgLSBUw5xCxLBUQUsxSDBGBgNVBAsMP1VsdXNhbCBFbGVrdHJvbmlrIHZl
-IEtyaXB0b2xvamkgQXJhxZ90xLFybWEgRW5zdGl0w7xzw7wgLSBVRUtBRTEjMCEGA1UECwwaS2Ft
-dSBTZXJ0aWZpa2FzeW9uIE1lcmtlemkxSjBIBgNVBAMMQVTDnELEsFRBSyBVRUtBRSBLw7ZrIFNl
-cnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIC0gU8O8csO8bSAzMIIBIjANBgkqhkiG9w0B
-AQEFAAOCAQ8AMIIBCgKCAQEAim1L/xCIOsP2fpTo6iBkcK4hgb46ezzb8R1Sf1n68yJMlaCQvEhO
-Eav7t7WNeoMojCZG2E6VQIdhn8WebYGHV2yKO7Rm6sxA/OOqbLLLAdsyv9Lrhc+hDVXDWzhXcLh1
-xnnRFDDtG1hba+818qEhTsXOfJlfbLm4IpNQp81McGq+agV/E5wrHur+R84EpW+sky58K5+eeROR
-6Oqeyjh1jmKwlZMq5d/pXpduIF9fhHpEORlAHLpVK/swsoHvhOPc7Jg4OQOFCKlUAwUp8MmPi+oL
-hmUZEdPpCSPeaJMDyTYcIW7OjGbxmTDY17PDHfiBLqi9ggtm/oLL4eAagsNAgQIDAQABo0IwQDAd
-BgNVHQ4EFgQUvYiHyY/2pAoLquvF/pEjnatKijIwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF
-MAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAB18+kmPNOm3JpIWmgV050vQbTlswyb2zrgxvMTfvCr4
-N5EY3ATIZJkrGG2AA1nJrvhY0D7twyOfaTyGOBye79oneNGEN3GKPEs5z35FBtYt2IpNeBLWrcLT
-y9LQQfMmNkqblWwM7uXRQydmwYj3erMgbOqwaSvHIOgMA8RBBZniP+Rr+KCGgceExh/VS4ESshYh
-LBOhgLJeDEoTniDYYkCrkOpkSi+sDQESeUWoL4cZaMjihccwsnX5OD+ywJO0a+IDRM5noN+J1q2M
-dqMTw5RhK2vZbMEHCiIHhWyFJEapvj+LeISCfiQMnf2BN+MlqO02TpUsyZyQ2uypQjyttgI=
------END CERTIFICATE-----
-
certSIGN ROOT CA
================
-----BEGIN CERTIFICATE-----
@@ -1493,49 +1137,6 @@ vBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNwi/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7Nz
TogVZ96edhBiIL5VaZVDADlN9u6wWk5JRFRYX0KD
-----END CERTIFICATE-----
-CNNIC ROOT
-==========
------BEGIN CERTIFICATE-----
-MIIDVTCCAj2gAwIBAgIESTMAATANBgkqhkiG9w0BAQUFADAyMQswCQYDVQQGEwJDTjEOMAwGA1UE
-ChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1QwHhcNMDcwNDE2MDcwOTE0WhcNMjcwNDE2MDcw
-OTE0WjAyMQswCQYDVQQGEwJDTjEOMAwGA1UEChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1Qw
-ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDTNfc/c3et6FtzF8LRb+1VvG7q6KR5smzD
-o+/hn7E7SIX1mlwhIhAsxYLO2uOabjfhhyzcuQxauohV3/2q2x8x6gHx3zkBwRP9SFIhxFXf2tiz
-VHa6dLG3fdfA6PZZxU3Iva0fFNrfWEQlMhkqx35+jq44sDB7R3IJMfAw28Mbdim7aXZOV/kbZKKT
-VrdvmW7bCgScEeOAH8tjlBAKqeFkgjH5jCftppkA9nCTGPihNIaj3XrCGHn2emU1z5DrvTOTn1Or
-czvmmzQgLx3vqR1jGqCA2wMv+SYahtKNu6m+UjqHZ0gNv7Sg2Ca+I19zN38m5pIEo3/PIKe38zrK
-y5nLAgMBAAGjczBxMBEGCWCGSAGG+EIBAQQEAwIABzAfBgNVHSMEGDAWgBRl8jGtKvf33VKWCscC
-wQ7vptU7ETAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIB/jAdBgNVHQ4EFgQUZfIxrSr3991S
-lgrHAsEO76bVOxEwDQYJKoZIhvcNAQEFBQADggEBAEs17szkrr/Dbq2flTtLP1se31cpolnKOOK5
-Gv+e5m4y3R6u6jW39ZORTtpC4cMXYFDy0VwmuYK36m3knITnA3kXr5g9lNvHugDnuL8BV8F3RTIM
-O/G0HAiw/VGgod2aHRM2mm23xzy54cXZF/qD1T0VoDy7HgviyJA/qIYM/PmLXoXLT1tLYhFHxUV8
-BS9BsZ4QaRuZluBVeftOhpm4lNqGOGqTo+fLbuXf6iFViZx9fX+Y9QCJ7uOEwFyWtcVG6kbghVW2
-G8kS1sHNzYDzAgE8yGnLRUhj2JTQ7IUOO04RZfSCjKY9ri4ilAnIXOo8gV0WKgOXFlUJ24pBgp5m
-mxE=
------END CERTIFICATE-----
-
-ApplicationCA - Japanese Government
-===================================
------BEGIN CERTIFICATE-----
-MIIDoDCCAoigAwIBAgIBMTANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJKUDEcMBoGA1UEChMT
-SmFwYW5lc2UgR292ZXJubWVudDEWMBQGA1UECxMNQXBwbGljYXRpb25DQTAeFw0wNzEyMTIxNTAw
-MDBaFw0xNzEyMTIxNTAwMDBaMEMxCzAJBgNVBAYTAkpQMRwwGgYDVQQKExNKYXBhbmVzZSBHb3Zl
-cm5tZW50MRYwFAYDVQQLEw1BcHBsaWNhdGlvbkNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
-CgKCAQEAp23gdE6Hj6UG3mii24aZS2QNcfAKBZuOquHMLtJqO8F6tJdhjYq+xpqcBrSGUeQ3DnR4
-fl+Kf5Sk10cI/VBaVuRorChzoHvpfxiSQE8tnfWuREhzNgaeZCw7NCPbXCbkcXmP1G55IrmTwcrN
-wVbtiGrXoDkhBFcsovW8R0FPXjQilbUfKW1eSvNNcr5BViCH/OlQR9cwFO5cjFW6WY2H/CPek9AE
-jP3vbb3QesmlOmpyM8ZKDQUXKi17safY1vC+9D/qDihtQWEjdnjDuGWk81quzMKq2edY3rZ+nYVu
-nyoKb58DKTCXKB28t89UKU5RMfkntigm/qJj5kEW8DOYRwIDAQABo4GeMIGbMB0GA1UdDgQWBBRU
-WssmP3HMlEYNllPqa0jQk/5CdTAOBgNVHQ8BAf8EBAMCAQYwWQYDVR0RBFIwUKROMEwxCzAJBgNV
-BAYTAkpQMRgwFgYDVQQKDA/ml6XmnKzlm73mlL/lupwxIzAhBgNVBAsMGuOCouODl+ODquOCseOD
-vOOCt+ODp+ODs0NBMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADlqRHZ3ODrs
-o2dGD/mLBqj7apAxzn7s2tGJfHrrLgy9mTLnsCTWw//1sogJhyzjVOGjprIIC8CFqMjSnHH2HZ9g
-/DgzE+Ge3Atf2hZQKXsvcJEPmbo0NI2VdMV+eKlmXb3KIXdCEKxmJj3ekav9FfBv7WxfEPjzFvYD
-io+nEhEMy/0/ecGc/WLuo89UDNErXxc+4z6/wCs+CZv+iKZ+tJIX/COUgb1up8WMwusRRdv4QcmW
-dupwX3kSa+SjB1oF7ydJzyGfikwJcGapJsErEU4z0g781mzSDjJkaP+tBXhfAx2o45CsJOAPQKdL
-rosot4LKGAfmt1t06SAZf7IbiVQ=
------END CERTIFICATE-----
-
GeoTrust Primary Certification Authority - G3
=============================================
-----BEGIN CERTIFICATE-----
@@ -1763,37 +1364,6 @@ y8hSyn+B/tlr0/cR7SXf+Of5pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061
lgeLKBObjBmNQSdJQO7e5iNEOdyhIta6A/I=
-----END CERTIFICATE-----
-ACEDICOM Root
-=============
------BEGIN CERTIFICATE-----
-MIIFtTCCA52gAwIBAgIIYY3HhjsBggUwDQYJKoZIhvcNAQEFBQAwRDEWMBQGA1UEAwwNQUNFRElD
-T00gUm9vdDEMMAoGA1UECwwDUEtJMQ8wDQYDVQQKDAZFRElDT00xCzAJBgNVBAYTAkVTMB4XDTA4
-MDQxODE2MjQyMloXDTI4MDQxMzE2MjQyMlowRDEWMBQGA1UEAwwNQUNFRElDT00gUm9vdDEMMAoG
-A1UECwwDUEtJMQ8wDQYDVQQKDAZFRElDT00xCzAJBgNVBAYTAkVTMIICIjANBgkqhkiG9w0BAQEF
-AAOCAg8AMIICCgKCAgEA/5KV4WgGdrQsyFhIyv2AVClVYyT/kGWbEHV7w2rbYgIB8hiGtXxaOLHk
-WLn709gtn70yN78sFW2+tfQh0hOR2QetAQXW8713zl9CgQr5auODAKgrLlUTY4HKRxx7XBZXehuD
-YAQ6PmXDzQHe3qTWDLqO3tkE7hdWIpuPY/1NFgu3e3eM+SW10W2ZEi5PGrjm6gSSrj0RuVFCPYew
-MYWveVqc/udOXpJPQ/yrOq2lEiZmueIM15jO1FillUAKt0SdE3QrwqXrIhWYENiLxQSfHY9g5QYb
-m8+5eaA9oiM/Qj9r+hwDezCNzmzAv+YbX79nuIQZ1RXve8uQNjFiybwCq0Zfm/4aaJQ0PZCOrfbk
-HQl/Sog4P75n/TSW9R28MHTLOO7VbKvU/PQAtwBbhTIWdjPp2KOZnQUAqhbm84F9b32qhm2tFXTT
-xKJxqvQUfecyuB+81fFOvW8XAjnXDpVCOscAPukmYxHqC9FK/xidstd7LzrZlvvoHpKuE1XI2Sf2
-3EgbsCTBheN3nZqk8wwRHQ3ItBTutYJXCb8gWH8vIiPYcMt5bMlL8qkqyPyHK9caUPgn6C9D4zq9
-2Fdx/c6mUlv53U3t5fZvie27k5x2IXXwkkwp9y+cAS7+UEaeZAwUswdbxcJzbPEHXEUkFDWug/Fq
-TYl6+rPYLWbwNof1K1MCAwEAAaOBqjCBpzAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKaz
-4SsrSbbXc6GqlPUB53NlTKxQMA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUprPhKytJttdzoaqU
-9QHnc2VMrFAwRAYDVR0gBD0wOzA5BgRVHSAAMDEwLwYIKwYBBQUHAgEWI2h0dHA6Ly9hY2VkaWNv
-bS5lZGljb21ncm91cC5jb20vZG9jMA0GCSqGSIb3DQEBBQUAA4ICAQDOLAtSUWImfQwng4/F9tqg
-aHtPkl7qpHMyEVNEskTLnewPeUKzEKbHDZ3Ltvo/Onzqv4hTGzz3gvoFNTPhNahXwOf9jU8/kzJP
-eGYDdwdY6ZXIfj7QeQCM8htRM5u8lOk6e25SLTKeI6RF+7YuE7CLGLHdztUdp0J/Vb77W7tH1Pwk
-zQSulgUV1qzOMPPKC8W64iLgpq0i5ALudBF/TP94HTXa5gI06xgSYXcGCRZj6hitoocf8seACQl1
-ThCojz2GuHURwCRiipZ7SkXp7FnFvmuD5uHorLUwHv4FB4D54SMNUI8FmP8sX+g7tq3PgbUhh8oI
-KiMnMCArz+2UW6yyetLHKKGKC5tNSixthT8Jcjxn4tncB7rrZXtaAWPWkFtPF2Y9fwsZo5NjEFIq
-nxQWWOLcpfShFosOkYuByptZ+thrkQdlVV9SH686+5DdaaVbnG0OLLb6zqylfDJKZ0DcMDQj3dcE
-I2bw/FWAp/tmGYI1Z2JwOV5vx+qQQEQIHriy1tvuWacNGHk0vFQYXlPKNFHtRQrmjseCNj6nOGOp
-MCwXEGCSn1WHElkQwg9naRHMTh5+Spqtr0CodaxWkHS4oJyleW/c6RrIaQXpuvoDs3zk4E7Czp3o
-tkYNbn5XOmeUwssfnHdKZ05phkOTOPu220+DkdRgfks+KzgHVZhepA==
------END CERTIFICATE-----
-
Microsec e-Szigno Root CA 2009
==============================
-----BEGIN CERTIFICATE-----
@@ -2154,37 +1724,6 @@ Zt3hrvJBW8qYVoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI
03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw=
-----END CERTIFICATE-----
-Certinomis - Autorité Racine
-============================
------BEGIN CERTIFICATE-----
-MIIFnDCCA4SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJGUjETMBEGA1UEChMK
-Q2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxJjAkBgNVBAMMHUNlcnRpbm9taXMg
-LSBBdXRvcml0w6kgUmFjaW5lMB4XDTA4MDkxNzA4Mjg1OVoXDTI4MDkxNzA4Mjg1OVowYzELMAkG
-A1UEBhMCRlIxEzARBgNVBAoTCkNlcnRpbm9taXMxFzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMSYw
-JAYDVQQDDB1DZXJ0aW5vbWlzIC0gQXV0b3JpdMOpIFJhY2luZTCCAiIwDQYJKoZIhvcNAQEBBQAD
-ggIPADCCAgoCggIBAJ2Fn4bT46/HsmtuM+Cet0I0VZ35gb5j2CN2DpdUzZlMGvE5x4jYF1AMnmHa
-wE5V3udauHpOd4cN5bjr+p5eex7Ezyh0x5P1FMYiKAT5kcOrJ3NqDi5N8y4oH3DfVS9O7cdxbwly
-Lu3VMpfQ8Vh30WC8Tl7bmoT2R2FFK/ZQpn9qcSdIhDWerP5pqZ56XjUl+rSnSTV3lqc2W+HN3yNw
-2F1MpQiD8aYkOBOo7C+ooWfHpi2GR+6K/OybDnT0K0kCe5B1jPyZOQE51kqJ5Z52qz6WKDgmi92N
-jMD2AR5vpTESOH2VwnHu7XSu5DaiQ3XV8QCb4uTXzEIDS3h65X27uK4uIJPT5GHfceF2Z5c/tt9q
-c1pkIuVC28+BA5PY9OMQ4HL2AHCs8MF6DwV/zzRpRbWT5BnbUhYjBYkOjUjkJW+zeL9i9Qf6lSTC
-lrLooyPCXQP8w9PlfMl1I9f09bze5N/NgL+RiH2nE7Q5uiy6vdFrzPOlKO1Enn1So2+WLhl+HPNb
-xxaOu2B9d2ZHVIIAEWBsMsGoOBvrbpgT1u449fCfDu/+MYHB0iSVL1N6aaLwD4ZFjliCK0wi1F6g
-530mJ0jfJUaNSih8hp75mxpZuWW/Bd22Ql095gBIgl4g9xGC3srYn+Y3RyYe63j3YcNBZFgCQfna
-4NH4+ej9Uji29YnfAgMBAAGjWzBZMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G
-A1UdDgQWBBQNjLZh2kS40RR9w759XkjwzspqsDAXBgNVHSAEEDAOMAwGCiqBegFWAgIAAQEwDQYJ
-KoZIhvcNAQEFBQADggIBACQ+YAZ+He86PtvqrxyaLAEL9MW12Ukx9F1BjYkMTv9sov3/4gbIOZ/x
-WqndIlgVqIrTseYyCYIDbNc/CMf4uboAbbnW/FIyXaR/pDGUu7ZMOH8oMDX/nyNTt7buFHAAQCva
-R6s0fl6nVjBhK4tDrP22iCj1a7Y+YEq6QpA0Z43q619FVDsXrIvkxmUP7tCMXWY5zjKn2BCXwH40
-nJ+U8/aGH88bc62UeYdocMMzpXDn2NU4lG9jeeu/Cg4I58UvD0KgKxRA/yHgBcUn4YQRE7rWhh1B
-CxMjidPJC+iKunqjo3M3NYB9Ergzd0A4wPpeMNLytqOx1qKVl4GbUu1pTP+A5FPbVFsDbVRfsbjv
-JL1vnxHDx2TCDyhihWZeGnuyt++uNckZM6i4J9szVb9o4XVIRFb7zdNIu0eJOqxp9YDG5ERQL1TE
-qkPFMTFYvZbF6nVsmnWxTfj3l/+WFvKXTej28xH5On2KOG4Ey+HTRRWqpdEdnV1j6CTmNhTih60b
-WfVEm/vXd3wfAXBioSAaosUaKPQhA+4u2cGA6rnZgtZbdsLLO7XSAPCjDuGtbkD326C00EauFddE
-wk01+dIL8hf2rGbVJLJP0RyZwG71fet0BLj5TXcJ17TPBzAJ8bgAVtkXFhYKK4bfjwEZGuW7gmP/
-vgt2Fl43N+bYdJeimUV5
------END CERTIFICATE-----
-
TWCA Root Certification Authority
=================================
-----BEGIN CERTIFICATE-----
@@ -2333,75 +1872,6 @@ l/9D7S3B2l0pKoU/rGXuhg8FjZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYl
iB6XzCGcKQENZetX2fNXlrtIzYE=
-----END CERTIFICATE-----
-StartCom Certification Authority
-================================
------BEGIN CERTIFICATE-----
-MIIHhzCCBW+gAwIBAgIBLTANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMN
-U3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmlu
-ZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0
-NjM3WhcNMzYwOTE3MTk0NjM2WjB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRk
-LjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMg
-U3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
-ggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZkpMyONvg45iPwbm2xPN1y
-o4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rfOQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/
-Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/CJi/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/d
-eMotHweXMAEtcnn6RtYTKqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt
-2PZE4XNiHzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMMAv+Z
-6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w+2OqqGwaVLRcJXrJ
-osmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/
-untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVc
-UjyJthkqcwEKDwOzEmDyei+B26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT
-37uMdBNSSwIDAQABo4ICEDCCAgwwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD
-VR0OBBYEFE4L7xqkQFulF2mHMMo0aEPQQa7yMB8GA1UdIwQYMBaAFE4L7xqkQFulF2mHMMo0aEPQ
-Qa7yMIIBWgYDVR0gBIIBUTCCAU0wggFJBgsrBgEEAYG1NwEBATCCATgwLgYIKwYBBQUHAgEWImh0
-dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5wZGYwNAYIKwYBBQUHAgEWKGh0dHA6Ly93d3cu
-c3RhcnRzc2wuY29tL2ludGVybWVkaWF0ZS5wZGYwgc8GCCsGAQUFBwICMIHCMCcWIFN0YXJ0IENv
-bW1lcmNpYWwgKFN0YXJ0Q29tKSBMdGQuMAMCAQEagZZMaW1pdGVkIExpYWJpbGl0eSwgcmVhZCB0
-aGUgc2VjdGlvbiAqTGVnYWwgTGltaXRhdGlvbnMqIG9mIHRoZSBTdGFydENvbSBDZXJ0aWZpY2F0
-aW9uIEF1dGhvcml0eSBQb2xpY3kgYXZhaWxhYmxlIGF0IGh0dHA6Ly93d3cuc3RhcnRzc2wuY29t
-L3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBG
-cmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQsFAAOCAgEAjo/n3JR5
-fPGFf59Jb2vKXfuM/gTFwWLRfUKKvFO3lANmMD+x5wqnUCBVJX92ehQN6wQOQOY+2IirByeDqXWm
-N3PH/UvSTa0XQMhGvjt/UfzDtgUx3M2FIk5xt/JxXrAaxrqTi3iSSoX4eA+D/i+tLPfkpLst0OcN
-Org+zvZ49q5HJMqjNTbOx8aHmNrs++myziebiMMEofYLWWivydsQD032ZGNcpRJvkrKTlMeIFw6T
-tn5ii5B/q06f/ON1FE8qMt9bDeD1e5MNq6HPh+GlBEXoPBKlCcWw0bdT82AUuoVpaiF8H3VhFyAX
-e2w7QSlc4axa0c2Mm+tgHRns9+Ww2vl5GKVFP0lDV9LdJNUso/2RjSe15esUBppMeyG7Oq0wBhjA
-2MFrLH9ZXF2RsXAiV+uKa0hK1Q8p7MZAwC+ITGgBF3f0JBlPvfrhsiAhS90a2Cl9qrjeVOwhVYBs
-HvUwyKMQ5bLmKhQxw4UtjJixhlpPiVktucf3HMiKf8CdBUrmQk9io20ppB+Fq9vlgcitKj1MXVuE
-JnHEhV5xJMqlG2zYYdMa4FTbzrqpMrUi9nNBCV24F10OD5mQ1kfabwo6YigUZ4LZ8dCAWZvLMdib
-D4x3TrVoivJs9iQOLWxwxXPR3hTQcY+203sC9uO41Alua551hDnmfyWl8kgAwKQB2j8=
------END CERTIFICATE-----
-
-StartCom Certification Authority G2
-===================================
------BEGIN CERTIFICATE-----
-MIIFYzCCA0ugAwIBAgIBOzANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJJTDEWMBQGA1UEChMN
-U3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg
-RzIwHhcNMTAwMTAxMDEwMDAxWhcNMzkxMjMxMjM1OTAxWjBTMQswCQYDVQQGEwJJTDEWMBQGA1UE
-ChMNU3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3Jp
-dHkgRzIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2iTZbB7cgNr2Cu+EWIAOVeq8O
-o1XJJZlKxdBWQYeQTSFgpBSHO839sj60ZwNq7eEPS8CRhXBF4EKe3ikj1AENoBB5uNsDvfOpL9HG
-4A/LnooUCri99lZi8cVytjIl2bLzvWXFDSxu1ZJvGIsAQRSCb0AgJnooD/Uefyf3lLE3PbfHkffi
-Aez9lInhzG7TNtYKGXmu1zSCZf98Qru23QumNK9LYP5/Q0kGi4xDuFby2X8hQxfqp0iVAXV16iul
-Q5XqFYSdCI0mblWbq9zSOdIxHWDirMxWRST1HFSr7obdljKF+ExP6JV2tgXdNiNnvP8V4so75qbs
-O+wmETRIjfaAKxojAuuKHDp2KntWFhxyKrOq42ClAJ8Em+JvHhRYW6Vsi1g8w7pOOlz34ZYrPu8H
-vKTlXcxNnw3h3Kq74W4a7I/htkxNeXJdFzULHdfBR9qWJODQcqhaX2YtENwvKhOuJv4KHBnM0D4L
-nMgJLvlblnpHnOl68wVQdJVznjAJ85eCXuaPOQgeWeU1FEIT/wCc976qUM/iUUjXuG+v+E5+M5iS
-FGI6dWPPe/regjupuznixL0sAA7IF6wT700ljtizkC+p2il9Ha90OrInwMEePnWjFqmveiJdnxMa
-z6eg6+OGCtP95paV1yPIN93EfKo2rJgaErHgTuixO/XWb/Ew1wIDAQABo0IwQDAPBgNVHRMBAf8E
-BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUS8W0QGutHLOlHGVuRjaJhwUMDrYwDQYJ
-KoZIhvcNAQELBQADggIBAHNXPyzVlTJ+N9uWkusZXn5T50HsEbZH77Xe7XRcxfGOSeD8bpkTzZ+K
-2s06Ctg6Wgk/XzTQLwPSZh0avZyQN8gMjgdalEVGKua+etqhqaRpEpKwfTbURIfXUfEpY9Z1zRbk
-J4kd+MIySP3bmdCPX1R0zKxnNBFi2QwKN4fRoxdIjtIXHfbX/dtl6/2o1PXWT6RbdejF0mCy2wl+
-JYt7ulKSnj7oxXehPOBKc2thz4bcQ///If4jXSRK9dNtD2IEBVeC2m6kMyV5Sy5UGYvMLD0w6dEG
-/+gyRr61M3Z3qAFdlsHB1b6uJcDJHgoJIIihDsnzb02CVAAgp9KP5DlUFy6NHrgbuxu9mk47EDTc
-nIhT76IxW1hPkWLIwpqazRVdOKnWvvgTtZ8SafJQYqz7Fzf07rh1Z2AQ+4NQ+US1dZxAF7L+/Xld
-blhYXzD8AK6vM8EOTmy6p6ahfzLbOOCxchcKK5HsamMm7YnUeMx0HgX4a/6ManY5Ka5lIxKVCCIc
-l85bBu4M4ru8H0ST9tg4RQUh7eStqxK2A6RCLi3ECToDZ2mEmuFZkIoohdVddLHRDiBYmxOlsGOm
-7XtH/UVVMKTumtTm4ofvmMkyghEpIrwACjFeLQ/Ajulrso8uBtjRkcfGEvRM/TAXw8HaOFvjqerm
-obp573PYtlNXLfbQ4ddI
------END CERTIFICATE-----
-
Buypass Class 2 Root CA
=======================
-----BEGIN CERTIFICATE-----
@@ -2508,31 +1978,6 @@ uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIWiAYLtqZLICjU
dcGWxZ0=
-----END CERTIFICATE-----
-TURKTRUST Certificate Services Provider Root 2007
-=================================================
------BEGIN CERTIFICATE-----
-MIIEPTCCAyWgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvzE/MD0GA1UEAww2VMOcUktUUlVTVCBF
-bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEP
-MA0GA1UEBwwGQW5rYXJhMV4wXAYDVQQKDFVUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUg
-QmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgQXJhbMSxayAyMDA3MB4X
-DTA3MTIyNTE4MzcxOVoXDTE3MTIyMjE4MzcxOVowgb8xPzA9BgNVBAMMNlTDnFJLVFJVU1QgRWxl
-a3Ryb25payBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTELMAkGA1UEBhMCVFIxDzAN
-BgNVBAcMBkFua2FyYTFeMFwGA1UECgxVVMOcUktUUlVTVCBCaWxnaSDEsGxldGnFn2ltIHZlIEJp
-bGnFn2ltIEfDvHZlbmxpxJ9pIEhpem1ldGxlcmkgQS7Fni4gKGMpIEFyYWzEsWsgMjAwNzCCASIw
-DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKu3PgqMyKVYFeaK7yc9SrToJdPNM8Ig3BnuiD9N
-YvDdE3ePYakqtdTyuTFYKTsvP2qcb3N2Je40IIDu6rfwxArNK4aUyeNgsURSsloptJGXg9i3phQv
-KUmi8wUG+7RP2qFsmmaf8EMJyupyj+sA1zU511YXRxcw9L6/P8JorzZAwan0qafoEGsIiveGHtya
-KhUG9qPw9ODHFNRRf8+0222vR5YXm3dx2KdxnSQM9pQ/hTEST7ruToK4uT6PIzdezKKqdfcYbwnT
-rqdUKDT74eA7YH2gvnmJhsifLfkKS8RQouf9eRbHegsYz85M733WB2+Y8a+xwXrXgTW4qhe04MsC
-AwEAAaNCMEAwHQYDVR0OBBYEFCnFkKslrxHkYb+j/4hhkeYO/pyBMA4GA1UdDwEB/wQEAwIBBjAP
-BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAQDdr4Ouwo0RSVgrESLFF6QSU2TJ/s
-Px+EnWVUXKgWAkD6bho3hO9ynYYKVZ1WKKxmLNA6VpM0ByWtCLCPyA8JWcqdmBzlVPi5RX9ql2+I
-aE1KBiY3iAIOtsbWcpnOa3faYjGkVh+uX4132l32iPwa2Z61gfAyuOOI0JzzaqC5mxRZNTZPz/OO
-Xl0XrRWV2N2y1RVuAE6zS89mlOTgzbUF2mNXi+WzqtvALhyQRNsaXRik7r4EW5nVcV9VZWRi1aKb
-BFmGyGJ353yCRWo9F7/snXUMrqNvWtMvmDb08PUZqxFdyKbjKlhqQgnDvZImZjINXQhVdP+MmNAK
-poRq0Tl9
------END CERTIFICATE-----
-
D-TRUST Root Class 3 CA 2 2009
==============================
-----BEGIN CERTIFICATE-----
@@ -2582,141 +2027,6 @@ NCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqXKVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVv
w9y4AyHqnxbxLFS1
-----END CERTIFICATE-----
-PSCProcert
-==========
------BEGIN CERTIFICATE-----
-MIIJhjCCB26gAwIBAgIBCzANBgkqhkiG9w0BAQsFADCCAR4xPjA8BgNVBAMTNUF1dG9yaWRhZCBk
-ZSBDZXJ0aWZpY2FjaW9uIFJhaXogZGVsIEVzdGFkbyBWZW5lem9sYW5vMQswCQYDVQQGEwJWRTEQ
-MA4GA1UEBxMHQ2FyYWNhczEZMBcGA1UECBMQRGlzdHJpdG8gQ2FwaXRhbDE2MDQGA1UEChMtU2lz
-dGVtYSBOYWNpb25hbCBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9uaWNhMUMwQQYDVQQLEzpTdXBl
-cmludGVuZGVuY2lhIGRlIFNlcnZpY2lvcyBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9uaWNhMSUw
-IwYJKoZIhvcNAQkBFhZhY3JhaXpAc3VzY2VydGUuZ29iLnZlMB4XDTEwMTIyODE2NTEwMFoXDTIw
-MTIyNTIzNTk1OVowgdExJjAkBgkqhkiG9w0BCQEWF2NvbnRhY3RvQHByb2NlcnQubmV0LnZlMQ8w
-DQYDVQQHEwZDaGFjYW8xEDAOBgNVBAgTB01pcmFuZGExKjAoBgNVBAsTIVByb3ZlZWRvciBkZSBD
-ZXJ0aWZpY2Fkb3MgUFJPQ0VSVDE2MDQGA1UEChMtU2lzdGVtYSBOYWNpb25hbCBkZSBDZXJ0aWZp
-Y2FjaW9uIEVsZWN0cm9uaWNhMQswCQYDVQQGEwJWRTETMBEGA1UEAxMKUFNDUHJvY2VydDCCAiIw
-DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANW39KOUM6FGqVVhSQ2oh3NekS1wwQYalNo97BVC
-wfWMrmoX8Yqt/ICV6oNEolt6Vc5Pp6XVurgfoCfAUFM+jbnADrgV3NZs+J74BCXfgI8Qhd19L3uA
-3VcAZCP4bsm+lU/hdezgfl6VzbHvvnpC2Mks0+saGiKLt38GieU89RLAu9MLmV+QfI4tL3czkkoh
-RqipCKzx9hEC2ZUWno0vluYC3XXCFCpa1sl9JcLB/KpnheLsvtF8PPqv1W7/U0HU9TI4seJfxPmO
-EO8GqQKJ/+MMbpfg353bIdD0PghpbNjU5Db4g7ayNo+c7zo3Fn2/omnXO1ty0K+qP1xmk6wKImG2
-0qCZyFSTXai20b1dCl53lKItwIKOvMoDKjSuc/HUtQy9vmebVOvh+qBa7Dh+PsHMosdEMXXqP+UH
-0quhJZb25uSgXTcYOWEAM11G1ADEtMo88aKjPvM6/2kwLkDd9p+cJsmWN63nOaK/6mnbVSKVUyqU
-td+tFjiBdWbjxywbk5yqjKPK2Ww8F22c3HxT4CAnQzb5EuE8XL1mv6JpIzi4mWCZDlZTOpx+FIyw
-Bm/xhnaQr/2v/pDGj59/i5IjnOcVdo/Vi5QTcmn7K2FjiO/mpF7moxdqWEfLcU8UC17IAggmosvp
-r2uKGcfLFFb14dq12fy/czja+eevbqQ34gcnAgMBAAGjggMXMIIDEzASBgNVHRMBAf8ECDAGAQH/
-AgEBMDcGA1UdEgQwMC6CD3N1c2NlcnRlLmdvYi52ZaAbBgVghl4CAqASDBBSSUYtRy0yMDAwNDAz
-Ni0wMB0GA1UdDgQWBBRBDxk4qpl/Qguk1yeYVKIXTC1RVDCCAVAGA1UdIwSCAUcwggFDgBStuyId
-xuDSAaj9dlBSk+2YwU2u06GCASakggEiMIIBHjE+MDwGA1UEAxM1QXV0b3JpZGFkIGRlIENlcnRp
-ZmljYWNpb24gUmFpeiBkZWwgRXN0YWRvIFZlbmV6b2xhbm8xCzAJBgNVBAYTAlZFMRAwDgYDVQQH
-EwdDYXJhY2FzMRkwFwYDVQQIExBEaXN0cml0byBDYXBpdGFsMTYwNAYDVQQKEy1TaXN0ZW1hIE5h
-Y2lvbmFsIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25pY2ExQzBBBgNVBAsTOlN1cGVyaW50ZW5k
-ZW5jaWEgZGUgU2VydmljaW9zIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25pY2ExJTAjBgkqhkiG
-9w0BCQEWFmFjcmFpekBzdXNjZXJ0ZS5nb2IudmWCAQowDgYDVR0PAQH/BAQDAgEGME0GA1UdEQRG
-MESCDnByb2NlcnQubmV0LnZloBUGBWCGXgIBoAwMClBTQy0wMDAwMDKgGwYFYIZeAgKgEgwQUklG
-LUotMzE2MzUzNzMtNzB2BgNVHR8EbzBtMEagRKBChkBodHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52
-ZS9sY3IvQ0VSVElGSUNBRE8tUkFJWi1TSEEzODRDUkxERVIuY3JsMCOgIaAfhh1sZGFwOi8vYWNy
-YWl6LnN1c2NlcnRlLmdvYi52ZTA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9v
-Y3NwLnN1c2NlcnRlLmdvYi52ZTBBBgNVHSAEOjA4MDYGBmCGXgMBAjAsMCoGCCsGAQUFBwIBFh5o
-dHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52ZS9kcGMwDQYJKoZIhvcNAQELBQADggIBACtZ6yKZu4Sq
-T96QxtGGcSOeSwORR3C7wJJg7ODU523G0+1ng3dS1fLld6c2suNUvtm7CpsR72H0xpkzmfWvADmN
-g7+mvTV+LFwxNG9s2/NkAZiqlCxB3RWGymspThbASfzXg0gTB1GEMVKIu4YXx2sviiCtxQuPcD4q
-uxtxj7mkoP3YldmvWb8lK5jpY5MvYB7Eqvh39YtsL+1+LrVPQA3uvFd359m21D+VJzog1eWuq2w1
-n8GhHVnchIHuTQfiSLaeS5UtQbHh6N5+LwUeaO6/u5BlOsju6rEYNxxik6SgMexxbJHmpHmJWhSn
-FFAFTKQAVzAswbVhltw+HoSvOULP5dAssSS830DD7X9jSr3hTxJkhpXzsOfIt+FTvZLm8wyWuevo
-5pLtp4EJFAv8lXrPj9Y0TzYS3F7RNHXGRoAvlQSMx4bEqCaJqD8Zm4G7UaRKhqsLEQ+xrmNTbSjq
-3TNWOByyrYDT13K9mmyZY+gAu0F2BbdbmRiKw7gSXFbPVgx96OLP7bx0R/vu0xdOIk9W/1DzLuY5
-poLWccret9W6aAjtmcz9opLLabid+Qqkpj5PkygqYWwHJgD/ll9ohri4zspV4KuxPX+Y1zMOWj3Y
-eMLEYC/HYvBhkdI4sPaeVdtAgAUSM84dkpvRabP/v/GSCmE1P93+hvS84Bpxs2Km
------END CERTIFICATE-----
-
-China Internet Network Information Center EV Certificates Root
-==============================================================
------BEGIN CERTIFICATE-----
-MIID9zCCAt+gAwIBAgIESJ8AATANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMCQ04xMjAwBgNV
-BAoMKUNoaW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24gQ2VudGVyMUcwRQYDVQQDDD5D
-aGluYSBJbnRlcm5ldCBOZXR3b3JrIEluZm9ybWF0aW9uIENlbnRlciBFViBDZXJ0aWZpY2F0ZXMg
-Um9vdDAeFw0xMDA4MzEwNzExMjVaFw0zMDA4MzEwNzExMjVaMIGKMQswCQYDVQQGEwJDTjEyMDAG
-A1UECgwpQ2hpbmEgSW50ZXJuZXQgTmV0d29yayBJbmZvcm1hdGlvbiBDZW50ZXIxRzBFBgNVBAMM
-PkNoaW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24gQ2VudGVyIEVWIENlcnRpZmljYXRl
-cyBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm35z7r07eKpkQ0H1UN+U8i6y
-jUqORlTSIRLIOTJCBumD1Z9S7eVnAztUwYyZmczpwA//DdmEEbK40ctb3B75aDFk4Zv6dOtouSCV
-98YPjUesWgbdYavi7NifFy2cyjw1l1VxzUOFsUcW9SxTgHbP0wBkvUCZ3czY28Sf1hNfQYOL+Q2H
-klY0bBoQCxfVWhyXWIQ8hBouXJE0bhlffxdpxWXvayHG1VA6v2G5BY3vbzQ6sm8UY78WO5upKv23
-KzhmBsUs4qpnHkWnjQRmQvaPK++IIGmPMowUc9orhpFjIpryp9vOiYurXccUwVswah+xt54ugQEC
-7c+WXmPbqOY4twIDAQABo2MwYTAfBgNVHSMEGDAWgBR8cks5x8DbYqVPm6oYNJKiyoOCWTAPBgNV
-HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUfHJLOcfA22KlT5uqGDSSosqD
-glkwDQYJKoZIhvcNAQEFBQADggEBACrDx0M3j92tpLIM7twUbY8opJhJywyA6vPtI2Z1fcXTIWd5
-0XPFtQO3WKwMVC/GVhMPMdoG52U7HW8228gd+f2ABsqjPWYWqJ1MFn3AlUa1UeTiH9fqBk1jjZaM
-7+czV0I664zBechNdn3e9rG3geCg+aF4RhcaVpjwTj2rHO3sOdwHSPdj/gauwqRcalsyiMXHM4Ws
-ZkJHwlgkmeHlPuV1LI5D1l08eB6olYIpUNHRFrrvwb562bTYzB5MRuF3sTGrvSrIzo9uoV1/A3U0
-5K2JRVRevq4opbs/eHnrc7MKDf2+yfdWrPa37S+bISnHOLaVxATywy39FCqQmbkHzJ8=
------END CERTIFICATE-----
-
-Swisscom Root CA 2
-==================
------BEGIN CERTIFICATE-----
-MIIF2TCCA8GgAwIBAgIQHp4o6Ejy5e/DfEoeWhhntjANBgkqhkiG9w0BAQsFADBkMQswCQYDVQQG
-EwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0YWwgQ2VydGlmaWNhdGUgU2Vy
-dmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3QgQ0EgMjAeFw0xMTA2MjQwODM4MTRaFw0zMTA2
-MjUwNzM4MTRaMGQxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGln
-aXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAyMIIC
-IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAlUJOhJ1R5tMJ6HJaI2nbeHCOFvErjw0DzpPM
-LgAIe6szjPTpQOYXTKueuEcUMncy3SgM3hhLX3af+Dk7/E6J2HzFZ++r0rk0X2s682Q2zsKwzxNo
-ysjL67XiPS4h3+os1OD5cJZM/2pYmLcX5BtS5X4HAB1f2uY+lQS3aYg5oUFgJWFLlTloYhyxCwWJ
-wDaCFCE/rtuh/bxvHGCGtlOUSbkrRsVPACu/obvLP+DHVxxX6NZp+MEkUp2IVd3Chy50I9AU/SpH
-Wrumnf2U5NGKpV+GY3aFy6//SSj8gO1MedK75MDvAe5QQQg1I3ArqRa0jG6F6bYRzzHdUyYb3y1a
-SgJA/MTAtukxGggo5WDDH8SQjhBiYEQN7Aq+VRhxLKX0srwVYv8c474d2h5Xszx+zYIdkeNL6yxS
-NLCK/RJOlrDrcH+eOfdmQrGrrFLadkBXeyq96G4DsguAhYidDMfCd7Camlf0uPoTXGiTOmekl9Ab
-mbeGMktg2M7v0Ax/lZ9vh0+Hio5fCHyqW/xavqGRn1V9TrALacywlKinh/LTSlDcX3KwFnUey7QY
-Ypqwpzmqm59m2I2mbJYV4+by+PGDYmy7Velhk6M99bFXi08jsJvllGov34zflVEpYKELKeRcVVi3
-qPyZ7iVNTA6z00yPhOgpD/0QVAKFyPnlw4vP5w8CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYw
-HQYDVR0hBBYwFDASBgdghXQBUwIBBgdghXQBUwIBMBIGA1UdEwEB/wQIMAYBAf8CAQcwHQYDVR0O
-BBYEFE0mICKJS9PVpAqhb97iEoHF8TwuMB8GA1UdIwQYMBaAFE0mICKJS9PVpAqhb97iEoHF8Twu
-MA0GCSqGSIb3DQEBCwUAA4ICAQAyCrKkG8t9voJXiblqf/P0wS4RfbgZPnm3qKhyN2abGu2sEzsO
-v2LwnN+ee6FTSA5BesogpxcbtnjsQJHzQq0Qw1zv/2BZf82Fo4s9SBwlAjxnffUy6S8w5X2lejjQ
-82YqZh6NM4OKb3xuqFp1mrjX2lhIREeoTPpMSQpKwhI3qEAMw8jh0FcNlzKVxzqfl9NX+Ave5XLz
-o9v/tdhZsnPdTSpxsrpJ9csc1fV5yJmz/MFMdOO0vSk3FQQoHt5FRnDsr7p4DooqzgB53MBfGWcs
-a0vvaGgLQ+OswWIJ76bdZWGgr4RVSJFSHMYlkSrQwSIjYVmvRRGFHQEkNI/Ps/8XciATwoCqISxx
-OQ7Qj1zB09GOInJGTB2Wrk9xseEFKZZZ9LuedT3PDTcNYtsmjGOpI99nBjx8Oto0QuFmtEYE3saW
-mA9LSHokMnWRn6z3aOkquVVlzl1h0ydw2Df+n7mvoC5Wt6NlUe07qxS/TFED6F+KBZvuim6c779o
-+sjaC+NCydAXFJy3SuCvkychVSa1ZC+N8f+mQAWFBVzKBxlcCxMoTFh/wqXvRdpg065lYZ1Tg3TC
-rvJcwhbtkj6EPnNgiLx29CzP0H1907he0ZESEOnN3col49XtmS++dYFLJPlFRpTJKSFTnCZFqhMX
-5OfNeOI5wSsSnqaeG8XmDtkx2Q==
------END CERTIFICATE-----
-
-Swisscom Root EV CA 2
-=====================
------BEGIN CERTIFICATE-----
-MIIF4DCCA8igAwIBAgIRAPL6ZOJ0Y9ON/RAdBB92ylgwDQYJKoZIhvcNAQELBQAwZzELMAkGA1UE
-BhMCY2gxETAPBgNVBAoTCFN3aXNzY29tMSUwIwYDVQQLExxEaWdpdGFsIENlcnRpZmljYXRlIFNl
-cnZpY2VzMR4wHAYDVQQDExVTd2lzc2NvbSBSb290IEVWIENBIDIwHhcNMTEwNjI0MDk0NTA4WhcN
-MzEwNjI1MDg0NTA4WjBnMQswCQYDVQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsT
-HERpZ2l0YWwgQ2VydGlmaWNhdGUgU2VydmljZXMxHjAcBgNVBAMTFVN3aXNzY29tIFJvb3QgRVYg
-Q0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMT3HS9X6lds93BdY7BxUglgRCgz
-o3pOCvrY6myLURYaVa5UJsTMRQdBTxB5f3HSek4/OE6zAMaVylvNwSqD1ycfMQ4jFrclyxy0uYAy
-Xhqdk/HoPGAsp15XGVhRXrwsVgu42O+LgrQ8uMIkqBPHoCE2G3pXKSinLr9xJZDzRINpUKTk4Rti
-GZQJo/PDvO/0vezbE53PnUgJUmfANykRHvvSEaeFGHR55E+FFOtSN+KxRdjMDUN/rhPSays/p8Li
-qG12W0OfvrSdsyaGOx9/5fLoZigWJdBLlzin5M8J0TbDC77aO0RYjb7xnglrPvMyxyuHxuxenPaH
-Za0zKcQvidm5y8kDnftslFGXEBuGCxobP/YCfnvUxVFkKJ3106yDgYjTdLRZncHrYTNaRdHLOdAG
-alNgHa/2+2m8atwBz735j9m9W8E6X47aD0upm50qKGsaCnw8qyIL5XctcfaCNYGu+HuB5ur+rPQa
-m3Rc6I8k9l2dRsQs0h4rIWqDJ2dVSqTjyDKXZpBy2uPUZC5f46Fq9mDU5zXNysRojddxyNMkM3Ox
-bPlq4SjbX8Y96L5V5jcb7STZDxmPX2MYWFCBUWVv8p9+agTnNCRxunZLWB4ZvRVgRaoMEkABnRDi
-xzgHcgplwLa7JSnaFp6LNYth7eVxV4O1PHGf40+/fh6Bn0GXAgMBAAGjgYYwgYMwDgYDVR0PAQH/
-BAQDAgGGMB0GA1UdIQQWMBQwEgYHYIV0AVMCAgYHYIV0AVMCAjASBgNVHRMBAf8ECDAGAQH/AgED
-MB0GA1UdDgQWBBRF2aWBbj2ITY1x0kbBbkUe88SAnTAfBgNVHSMEGDAWgBRF2aWBbj2ITY1x0kbB
-bkUe88SAnTANBgkqhkiG9w0BAQsFAAOCAgEAlDpzBp9SSzBc1P6xXCX5145v9Ydkn+0UjrgEjihL
-j6p7jjm02Vj2e6E1CqGdivdj5eu9OYLU43otb98TPLr+flaYC/NUn81ETm484T4VvwYmneTwkLbU
-wp4wLh/vx3rEUMfqe9pQy3omywC0Wqu1kx+AiYQElY2NfwmTv9SoqORjbdlk5LgpWgi/UOGED1V7
-XwgiG/W9mR4U9s70WBCCswo9GcG/W6uqmdjyMb3lOGbcWAXH7WMaLgqXfIeTK7KK4/HsGOV1timH
-59yLGn602MnTihdsfSlEvoqq9X46Lmgxk7lq2prg2+kupYTNHAq4Sgj5nPFhJpiTt3tm7JFe3VE/
-23MPrQRYCd0EApUKPtN236YQHoA96M2kZNEzx5LH4k5E4wnJTsJdhw4Snr8PyQUQ3nqjsTzyP6Wq
-J3mtMX0f/fwZacXduT98zca0wjAefm6S139hdlqP65VNvBFuIXxZN5nQBrz5Bm0yFqXZaajh3DyA
-HmBR3NdUIR7KYndP+tiPsys6DXhyyWhBWkdKwqPrGtcKqzwyVcgKEZzfdNbwQBUdyLmPtTbFr/gi
-uMod89a2GQ+fYWVq6nTIfI/DT11lgh/ZDYnadXL77/FHZxOzyNEZiCcmmpl5fx7kLD977vHeTYuW
-l8PVP3wbI+2ksx0WckNLIOFZfsLorSa/ovc=
------END CERTIFICATE-----
-
CA Disig Root R1
================
-----BEGIN CERTIFICATE-----
@@ -3150,66 +2460,6 @@ G48BtieVU+i2iW1bvGjUI+iLUaJW+fCmgKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP
82Z+
-----END CERTIFICATE-----
-WoSign
-======
------BEGIN CERTIFICATE-----
-MIIFdjCCA16gAwIBAgIQXmjWEXGUY1BWAGjzPsnFkTANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQG
-EwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxKjAoBgNVBAMTIUNlcnRpZmljYXRpb24g
-QXV0aG9yaXR5IG9mIFdvU2lnbjAeFw0wOTA4MDgwMTAwMDFaFw0zOTA4MDgwMTAwMDFaMFUxCzAJ
-BgNVBAYTAkNOMRowGAYDVQQKExFXb1NpZ24gQ0EgTGltaXRlZDEqMCgGA1UEAxMhQ2VydGlmaWNh
-dGlvbiBBdXRob3JpdHkgb2YgV29TaWduMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA
-vcqNrLiRFVaXe2tcesLea9mhsMMQI/qnobLMMfo+2aYpbxY94Gv4uEBf2zmoAHqLoE1UfcIiePyO
-CbiohdfMlZdLdNiefvAA5A6JrkkoRBoQmTIPJYhTpA2zDxIIFgsDcSccf+Hb0v1naMQFXQoOXXDX
-2JegvFNBmpGN9J42Znp+VsGQX+axaCA2pIwkLCxHC1l2ZjC1vt7tj/id07sBMOby8w7gLJKA84X5
-KIq0VC6a7fd2/BVoFutKbOsuEo/Uz/4Mx1wdC34FMr5esAkqQtXJTpCzWQ27en7N1QhatH/YHGkR
-+ScPewavVIMYe+HdVHpRaG53/Ma/UkpmRqGyZxq7o093oL5d//xWC0Nyd5DKnvnyOfUNqfTq1+ez
-EC8wQjchzDBwyYaYD8xYTYO7feUapTeNtqwylwA6Y3EkHp43xP901DfA4v6IRmAR3Qg/UDaruHqk
-lWJqbrDKaiFaafPz+x1wOZXzp26mgYmhiMU7ccqjUu6Du/2gd/Tkb+dC221KmYo0SLwX3OSACCK2
-8jHAPwQ+658geda4BmRkAjHXqc1S+4RFaQkAKtxVi8QGRkvASh0JWzko/amrzgD5LkhLJuYwTKVY
-yrREgk/nkR4zw7CT/xH8gdLKH3Ep3XZPkiWvHYG3Dy+MwwbMLyejSuQOmbp8HkUff6oZRZb9/D0C
-AwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFOFmzw7R
-8bNLtwYgFP6HEtX2/vs+MA0GCSqGSIb3DQEBBQUAA4ICAQCoy3JAsnbBfnv8rWTjMnvMPLZdRtP1
-LOJwXcgu2AZ9mNELIaCJWSQBnfmvCX0KI4I01fx8cpm5o9dU9OpScA7F9dY74ToJMuYhOZO9sxXq
-T2r09Ys/L3yNWC7F4TmgPsc9SnOeQHrAK2GpZ8nzJLmzbVUsWh2eJXLOC62qx1ViC777Y7NhRCOj
-y+EaDveaBk3e1CNOIZZbOVtXHS9dCF4Jef98l7VNg64N1uajeeAz0JmWAjCnPv/So0M/BVoG6kQC
-2nz4SNAzqfkHx5Xh9T71XXG68pWpdIhhWeO/yloTunK0jF02h+mmxTwTv97QRCbut+wucPrXnbes
-5cVAWubXbHssw1abR80LzvobtCHXt2a49CUwi1wNuepnsvRtrtWhnk/Yn+knArAdBtaP4/tIEp9/
-EaEQPkxROpaw0RPxx9gmrjrKkcRpnd8BKWRRb2jaFOwIQZeQjdCygPLPwj2/kWjFgGcexGATVdVh
-mVd8upUPYUk6ynW8yQqTP2cOEvIo4jEbwFcW3wh8GcF+Dx+FHgo2fFt+J7x6v+Db9NpSvd4MVHAx
-kUOVyLzwPt0JfjBkUO1/AaQzZ01oT74V77D2AhGiGxMlOtzCWfHjXEa7ZywCRuoeSKbmW9m1vFGi
-kpbbqsY3Iqb+zCB0oy2pLmvLwIIRIbWTee5Ehr7XHuQe+w==
------END CERTIFICATE-----
-
-WoSign China
-============
------BEGIN CERTIFICATE-----
-MIIFWDCCA0CgAwIBAgIQUHBrzdgT/BtOOzNy0hFIjTANBgkqhkiG9w0BAQsFADBGMQswCQYDVQQG
-EwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxGzAZBgNVBAMMEkNBIOayg+mAmuagueiv
-geS5pjAeFw0wOTA4MDgwMTAwMDFaFw0zOTA4MDgwMTAwMDFaMEYxCzAJBgNVBAYTAkNOMRowGAYD
-VQQKExFXb1NpZ24gQ0EgTGltaXRlZDEbMBkGA1UEAwwSQ0Eg5rKD6YCa5qC56K+B5LmmMIICIjAN
-BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0EkhHiX8h8EqwqzbdoYGTufQdDTc7WU1/FDWiD+k
-8H/rD195L4mx/bxjWDeTmzj4t1up+thxx7S8gJeNbEvxUNUqKaqoGXqW5pWOdO2XCld19AXbbQs5
-uQF/qvbW2mzmBeCkTVL829B0txGMe41P/4eDrv8FAxNXUDf+jJZSEExfv5RxadmWPgxDT74wwJ85
-dE8GRV2j1lY5aAfMh09Qd5Nx2UQIsYo06Yms25tO4dnkUkWMLhQfkWsZHWgpLFbE4h4TV2TwYeO5
-Ed+w4VegG63XX9Gv2ystP9Bojg/qnw+LNVgbExz03jWhCl3W6t8Sb8D7aQdGctyB9gQjF+BNdeFy
-b7Ao65vh4YOhn0pdr8yb+gIgthhid5E7o9Vlrdx8kHccREGkSovrlXLp9glk3Kgtn3R46MGiCWOc
-76DbT52VqyBPt7D3h1ymoOQ3OMdc4zUPLK2jgKLsLl3Az+2LBcLmc272idX10kaO6m1jGx6KyX2m
-+Jzr5dVjhU1zZmkR/sgO9MHHZklTfuQZa/HpelmjbX7FF+Ynxu8b22/8DU0GAbQOXDBGVWCvOGU6
-yke6rCzMRh+yRpY/8+0mBe53oWprfi1tWFxK1I5nuPHa1UaKJ/kR8slC/k7e3x9cxKSGhxYzoacX
-GKUN5AXlK8IrC6KVkLn9YDxOiT7nnO4fuwECAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1Ud
-EwEB/wQFMAMBAf8wHQYDVR0OBBYEFOBNv9ybQV0T6GTwp+kVpOGBwboxMA0GCSqGSIb3DQEBCwUA
-A4ICAQBqinA4WbbaixjIvirTthnVZil6Xc1bL3McJk6jfW+rtylNpumlEYOnOXOvEESS5iVdT2H6
-yAa+Tkvv/vMx/sZ8cApBWNromUuWyXi8mHwCKe0JgOYKOoICKuLJL8hWGSbueBwj/feTZU7n85iY
-r83d2Z5AiDEoOqsuC7CsDCT6eiaY8xJhEPRdF/d+4niXVOKM6Cm6jBAyvd0zaziGfjk9DgNyp115
-j0WKWa5bIW4xRtVZjc8VX90xJc/bYNaBRHIpAlf2ltTW/+op2znFuCyKGo3Oy+dCMYYFaA6eFN0A
-kLppRQjbbpCBhqcqBT/mhDn4t/lXX0ykeVoQDF7Va/81XwVRHmyjdanPUIPTfPRm94KNPQx96N97
-qA4bLJyuQHCH2u2nFoJavjVsIE4iYdm8UXrNemHcSxH5/mc0zy4EZmFcV5cjjPOGG0jfKq+nwf/Y
-jj4Du9gqsPoUJbJRa4ZDhS4HIxaAjUz7tGM7zMN07RujHv41D198HRaG9Q7DlfEvr10lO1Hm13ZB
-ONFLAzkopR6RctR9q5czxNM+4Gm2KHmgCY0c0f9BckgG/Jou5yD5m6Leie2uPAmvylezkolwQOQv
-T8Jwg0DXJCxr5wkf09XHwQj02w47HAcLQxGEIYbpgNR12KvxAmLBsX5VYc8T1yaw15zLKYs4SgsO
-kI26oQ==
------END CERTIFICATE-----
-
COMODO RSA Certification Authority
==================================
-----BEGIN CERTIFICATE-----
@@ -3538,30 +2788,6 @@ lpKQd/Ct9JDpEXjXk4nAPQu6KfTomZ1yju2dL+6SfaHx/126M2CFYv4HAqGEVka+lgqaE9chTLd8
B59OTj+RdPsnnRHM3eaxynFNExc5JsUpISuTKWqW+qtB4Uu2NQvAmxU=
-----END CERTIFICATE-----
-TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı H6
-====================================================
------BEGIN CERTIFICATE-----
-MIIEJjCCAw6gAwIBAgIGfaHyZeyKMA0GCSqGSIb3DQEBCwUAMIGxMQswCQYDVQQGEwJUUjEPMA0G
-A1UEBwwGQW5rYXJhMU0wSwYDVQQKDERUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmls
-acWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLjFCMEAGA1UEAww5VMOcUktUUlVTVCBF
-bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIEg2MB4XDTEzMTIxODA5
-MDQxMFoXDTIzMTIxNjA5MDQxMFowgbExCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExTTBL
-BgNVBAoMRFTDnFJLVFJVU1QgQmlsZ2kgxLBsZXRpxZ9pbSB2ZSBCaWxpxZ9pbSBHw7x2ZW5sacSf
-aSBIaXptZXRsZXJpIEEuxZ4uMUIwQAYDVQQDDDlUw5xSS1RSVVNUIEVsZWt0cm9uaWsgU2VydGlm
-aWthIEhpem1ldCBTYcSfbGF5xLFjxLFzxLEgSDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
-AoIBAQCdsGjW6L0UlqMACprx9MfMkU1xeHe59yEmFXNRFpQJRwXiM/VomjX/3EsvMsew7eKC5W/a
-2uqsxgbPJQ1BgfbBOCK9+bGlprMBvD9QFyv26WZV1DOzXPhDIHiTVRZwGTLmiddk671IUP320EED
-wnS3/faAz1vFq6TWlRKb55cTMgPp1KtDWxbtMyJkKbbSk60vbNg9tvYdDjTu0n2pVQ8g9P0pu5Fb
-HH3GQjhtQiht1AH7zYiXSX6484P4tZgvsycLSF5W506jM7NE1qXyGJTtHB6plVxiSvgNZ1GpryHV
-+DKdeboaX+UEVU0TRv/yz3THGmNtwx8XEsMeED5gCLMxAgMBAAGjQjBAMB0GA1UdDgQWBBTdVRcT
-9qzoSCHK77Wv0QAy7Z6MtTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG
-9w0BAQsFAAOCAQEAb1gNl0OqFlQ+v6nfkkU/hQu7VtMMUszIv3ZnXuaqs6fvuay0EBQNdH49ba3R
-fdCaqaXKGDsCQC4qnFAUi/5XfldcEQlLNkVS9z2sFP1E34uXI9TDwe7UU5X+LEr+DXCqu4svLcsy
-o4LyVN/Y8t3XSHLuSqMplsNEzm61kod2pLv0kmzOLBQJZo6NrRa1xxsJYTvjIKIDgI6tflEATseW
-hvtDmHd9KMeP2Cpu54Rvl0EpABZeTeIT6lnAY2c6RPuY/ATTMHKm9ocJV612ph1jmv3XZch4gyt1
-O6VbuA1df74jrlZVlFjvH4GMKrLN5ptjnhi85WsGtAuYSyher4hYyw==
------END CERTIFICATE-----
-
Certinomis - Root CA
====================
-----BEGIN CERTIFICATE-----
@@ -3615,42 +2841,6 @@ HZeeevJuQHHfaPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02Ic
Nc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM=
-----END CERTIFICATE-----
-Certification Authority of WoSign G2
-====================================
------BEGIN CERTIFICATE-----
-MIIDfDCCAmSgAwIBAgIQayXaioidfLwPBbOxemFFRDANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQG
-EwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxLTArBgNVBAMTJENlcnRpZmljYXRpb24g
-QXV0aG9yaXR5IG9mIFdvU2lnbiBHMjAeFw0xNDExMDgwMDU4NThaFw00NDExMDgwMDU4NThaMFgx
-CzAJBgNVBAYTAkNOMRowGAYDVQQKExFXb1NpZ24gQ0EgTGltaXRlZDEtMCsGA1UEAxMkQ2VydGlm
-aWNhdGlvbiBBdXRob3JpdHkgb2YgV29TaWduIEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
-CgKCAQEAvsXEoCKASU+/2YcRxlPhuw+9YH+v9oIOH9ywjj2X4FA8jzrvZjtFB5sg+OPXJYY1kBai
-XW8wGQiHC38Gsp1ij96vkqVg1CuAmlI/9ZqD6TRay9nVYlzmDuDfBpgOgHzKtB0TiGsOqCR3A9Du
-W/PKaZE1OVbFbeP3PU9ekzgkyhjpJMuSA93MHD0JcOQg5PGurLtzaaNjOg9FD6FKmsLRY6zLEPg9
-5k4ot+vElbGs/V6r+kHLXZ1L3PR8du9nfwB6jdKgGlxNIuG12t12s9R23164i5jIFFTMaxeSt+BK
-v0mUYQs4kI9dJGwlezt52eJ+na2fmKEG/HgUYFf47oB3sQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMC
-AQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU+mCp62XF3RYUCE4MD42b4Pdkr2cwDQYJKoZI
-hvcNAQELBQADggEBAFfDejaCnI2Y4qtAqkePx6db7XznPWZaOzG73/MWM5H8fHulwqZm46qwtyeY
-P0nXYGdnPzZPSsvxFPpahygc7Y9BMsaV+X3avXtbwrAh449G3CE4Q3RM+zD4F3LBMvzIkRfEzFg3
-TgvMWvchNSiDbGAtROtSjFA9tWwS1/oJu2yySrHFieT801LYYRf+epSEj3m2M1m6D8QL4nCgS3gu
-+sif/a+RZQp4OBXllxcU3fngLDT4ONCEIgDAFFEYKwLcMFrw6AF8NTojrwjkr6qOKEJJLvD1mTS+
-7Q9LGOHSJDy7XUe3IfKN0QqZjuNuPq1w4I+5ysxugTH2e5x6eeRncRg=
------END CERTIFICATE-----
-
-CA WoSign ECC Root
-==================
------BEGIN CERTIFICATE-----
-MIICCTCCAY+gAwIBAgIQaEpYcIBr8I8C+vbe6LCQkDAKBggqhkjOPQQDAzBGMQswCQYDVQQGEwJD
-TjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxGzAZBgNVBAMTEkNBIFdvU2lnbiBFQ0MgUm9v
-dDAeFw0xNDExMDgwMDU4NThaFw00NDExMDgwMDU4NThaMEYxCzAJBgNVBAYTAkNOMRowGAYDVQQK
-ExFXb1NpZ24gQ0EgTGltaXRlZDEbMBkGA1UEAxMSQ0EgV29TaWduIEVDQyBSb290MHYwEAYHKoZI
-zj0CAQYFK4EEACIDYgAE4f2OuEMkq5Z7hcK6C62N4DrjJLnSsb6IOsq/Srj57ywvr1FQPEd1bPiU
-t5v8KB7FVMxjnRZLU8HnIKvNrCXSf4/CwVqCXjCLelTOA7WRf6qU0NGKSMyCBSah1VES1ns2o0Iw
-QDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUqv3VWqP2h4syhf3R
-MluARZPzA7gwCgYIKoZIzj0EAwMDaAAwZQIxAOSkhLCB1T2wdKyUpOgOPQB0TKGXa/kNUTyh2Tv0
-Daupn75OcsqF1NnstTJFGG+rrQIwfcf3aWMvoeGY7xMQ0Xk/0f7qO3/eVvSQsRUR2LIiFdAvwyYu
-a/GRspBl9JrmkO5K
------END CERTIFICATE-----
-
SZAFIR ROOT CA2
===============
-----BEGIN CERTIFICATE-----
@@ -4041,3 +3231,237 @@ TxgKqpAd60Ae36EeRJIQmvKN4dFLRp7oRUKX6kWZ8+xm1QL68qZKJKrezrnK+T+Tb/mjuuqlPpmt
7kGUnF4ZLvhFSZl0kbAEb+MEWrGrKqv+x9CWttrhSmQGbmBNvUJO/3jaJMobtNeWOWyu8Q6qp31I
iyBMz2TWuJdGsE7RKlY6oJO9r4Ak4Ap+58rVyuiFVdw2KuGUaJPHZnJED4AhMmwlxyOAgwrr
-----END CERTIFICATE-----
+
+TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1
+=============================================
+-----BEGIN CERTIFICATE-----
+MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIxGDAWBgNVBAcT
+D0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxpbXNlbCB2ZSBUZWtub2xvamlr
+IEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0wKwYDVQQLEyRLYW11IFNlcnRpZmlrYXN5b24g
+TWVya2V6aSAtIEthbXUgU00xNjA0BgNVBAMTLVRVQklUQUsgS2FtdSBTTSBTU0wgS29rIFNlcnRp
+ZmlrYXNpIC0gU3VydW0gMTAeFw0xMzExMjUwODI1NTVaFw00MzEwMjUwODI1NTVaMIHSMQswCQYD
+VQQGEwJUUjEYMBYGA1UEBxMPR2ViemUgLSBLb2NhZWxpMUIwQAYDVQQKEzlUdXJraXllIEJpbGlt
+c2VsIHZlIFRla25vbG9qaWsgQXJhc3Rpcm1hIEt1cnVtdSAtIFRVQklUQUsxLTArBgNVBAsTJEth
+bXUgU2VydGlmaWthc3lvbiBNZXJrZXppIC0gS2FtdSBTTTE2MDQGA1UEAxMtVFVCSVRBSyBLYW11
+IFNNIFNTTCBLb2sgU2VydGlmaWthc2kgLSBTdXJ1bSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAr3UwM6q7a9OZLBI3hNmNe5eA027n/5tQlT6QlVZC1xl8JoSNkvoBHToP4mQ4t4y8
+6Ij5iySrLqP1N+RAjhgleYN1Hzv/bKjFxlb4tO2KRKOrbEz8HdDc72i9z+SqzvBV96I01INrN3wc
+wv61A+xXzry0tcXtAA9TNypN9E8Mg/uGz8v+jE69h/mniyFXnHrfA2eJLJ2XYacQuFWQfw4tJzh0
+3+f92k4S400VIgLI4OD8D62K18lUUMw7D8oWgITQUVbDjlZ/iSIzL+aFCr2lqBs23tPcLG07xxO9
+WSMs5uWk99gL7eqQQESolbuT1dCANLZGeA4fAJNG4e7p+exPFwIDAQABo0IwQDAdBgNVHQ4EFgQU
+ZT/HiobGPN08VFw1+DrtUgxHV8gwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJ
+KoZIhvcNAQELBQADggEBACo/4fEyjq7hmFxLXs9rHmoJ0iKpEsdeV31zVmSAhHqT5Am5EM2fKifh
+AHe+SMg1qIGf5LgsyX8OsNJLN13qudULXjS99HMpw+0mFZx+CFOKWI3QSyjfwbPfIPP54+M638yc
+lNhOT8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4lzwDGrpDxpa5RXI4s6ehlj2R
+e37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X8W0j
+q5Rm+K37DwhuJi1/FwcJsoz7UMCflo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM=
+-----END CERTIFICATE-----
+
+GDCA TrustAUTH R5 ROOT
+======================
+-----BEGIN CERTIFICATE-----
+MIIFiDCCA3CgAwIBAgIIfQmX/vBH6nowDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCQ04xMjAw
+BgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZIENPLixMVEQuMR8wHQYDVQQD
+DBZHRENBIFRydXN0QVVUSCBSNSBST09UMB4XDTE0MTEyNjA1MTMxNVoXDTQwMTIzMTE1NTk1OVow
+YjELMAkGA1UEBhMCQ04xMjAwBgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZ
+IENPLixMVEQuMR8wHQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMIICIjANBgkqhkiG9w0B
+AQEFAAOCAg8AMIICCgKCAgEA2aMW8Mh0dHeb7zMNOwZ+Vfy1YI92hhJCfVZmPoiC7XJjDp6L3TQs
+AlFRwxn9WVSEyfFrs0yw6ehGXTjGoqcuEVe6ghWinI9tsJlKCvLriXBjTnnEt1u9ol2x8kECK62p
+OqPseQrsXzrj/e+APK00mxqriCZ7VqKChh/rNYmDf1+uKU49tm7srsHwJ5uu4/Ts765/94Y9cnrr
+pftZTqfrlYwiOXnhLQiPzLyRuEH3FMEjqcOtmkVEs7LXLM3GKeJQEK5cy4KOFxg2fZfmiJqwTTQJ
+9Cy5WmYqsBebnh52nUpmMUHfP/vFBu8btn4aRjb3ZGM74zkYI+dndRTVdVeSN72+ahsmUPI2JgaQ
+xXABZG12ZuGR224HwGGALrIuL4xwp9E7PLOR5G62xDtw8mySlwnNR30YwPO7ng/Wi64HtloPzgsM
+R6flPri9fcebNaBhlzpBdRfMK5Z3KpIhHtmVdiBnaM8Nvd/WHwlqmuLMc3GkL30SgLdTMEZeS1SZ
+D2fJpcjyIMGC7J0R38IC+xo70e0gmu9lZJIQDSri3nDxGGeCjGHeuLzRL5z7D9Ar7Rt2ueQ5Vfj4
+oR24qoAATILnsn8JuLwwoC8N9VKejveSswoAHQBUlwbgsQfZxw9cZX08bVlX5O2ljelAU58VS6Bx
+9hoh49pwBiFYFIeFd3mqgnkCAwEAAaNCMEAwHQYDVR0OBBYEFOLJQJ9NzuiaoXzPDj9lxSmIahlR
+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQDRSVfg
+p8xoWLoBDysZzY2wYUWsEe1jUGn4H3++Fo/9nesLqjJHdtJnJO29fDMylyrHBYZmDRd9FBUb1Ov9
+H5r2XpdptxolpAqzkT9fNqyL7FeoPueBihhXOYV0GkLH6VsTX4/5COmSdI31R9KrO9b7eGZONn35
+6ZLpBN79SWP8bfsUcZNnL0dKt7n/HipzcEYwv1ryL3ml4Y0M2fmyYzeMN2WFcGpcWwlyua1jPLHd
++PwyvzeG5LuOmCd+uh8W4XAR8gPfJWIyJyYYMoSf/wA6E7qaTfRPuBRwIrHKK5DOKcFw9C+df/KQ
+HtZa37dG/OaG+svgIHZ6uqbL9XzeYqWxi+7egmaKTjowHz+Ay60nugxe19CxVsp3cbK1daFQqUBD
+F8Io2c9Si1vIY9RCPqAzekYu9wogRlR+ak8x8YF+QnQ4ZXMn7sZ8uI7XpTrXmKGcjBBV09tL7ECQ
+8s1uV9JiDnxXk7Gnbc2dg7sq5+W2O3FYrf3RRbxake5TFW/TRQl1brqQXR4EzzffHqhmsYzmIGrv
+/EhOdJhCrylvLmrH+33RZjEizIYAfmaDDEL0vTSSwxrqT8p+ck0LcIymSLumoRT2+1hEmRSuqguT
+aaApJUqlyyvdimYHFngVV3Eb7PVHhPOeMTd61X8kreS8/f3MboPoDKi3QWwH3b08hpcv0g==
+-----END CERTIFICATE-----
+
+TrustCor RootCert CA-1
+======================
+-----BEGIN CERTIFICATE-----
+MIIEMDCCAxigAwIBAgIJANqb7HHzA7AZMA0GCSqGSIb3DQEBCwUAMIGkMQswCQYDVQQGEwJQQTEP
+MA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3Ig
+U3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5UcnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3Jp
+dHkxHzAdBgNVBAMMFlRydXN0Q29yIFJvb3RDZXJ0IENBLTEwHhcNMTYwMjA0MTIzMjE2WhcNMjkx
+MjMxMTcyMzE2WjCBpDELMAkGA1UEBhMCUEExDzANBgNVBAgMBlBhbmFtYTEUMBIGA1UEBwwLUGFu
+YW1hIENpdHkxJDAiBgNVBAoMG1RydXN0Q29yIFN5c3RlbXMgUy4gZGUgUi5MLjEnMCUGA1UECwwe
+VHJ1c3RDb3IgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MR8wHQYDVQQDDBZUcnVzdENvciBSb290Q2Vy
+dCBDQS0xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv463leLCJhJrMxnHQFgKq1mq
+jQCj/IDHUHuO1CAmujIS2CNUSSUQIpidRtLByZ5OGy4sDjjzGiVoHKZaBeYei0i/mJZ0PmnK6bV4
+pQa81QBeCQryJ3pS/C3Vseq0iWEk8xoT26nPUu0MJLq5nux+AHT6k61sKZKuUbS701e/s/OojZz0
+JEsq1pme9J7+wH5COucLlVPat2gOkEz7cD+PSiyU8ybdY2mplNgQTsVHCJCZGxdNuWxu72CVEY4h
+gLW9oHPY0LJ3xEXqWib7ZnZ2+AYfYW0PVcWDtxBWcgYHpfOxGgMFZA6dWorWhnAbJN7+KIor0Gqw
+/Hqi3LJ5DotlDwIDAQABo2MwYTAdBgNVHQ4EFgQU7mtJPHo/DeOxCbeKyKsZn3MzUOcwHwYDVR0j
+BBgwFoAU7mtJPHo/DeOxCbeKyKsZn3MzUOcwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
+AYYwDQYJKoZIhvcNAQELBQADggEBACUY1JGPE+6PHh0RU9otRCkZoB5rMZ5NDp6tPVxBb5UrJKF5
+mDo4Nvu7Zp5I/5CQ7z3UuJu0h3U/IJvOcs+hVcFNZKIZBqEHMwwLKeXx6quj7LUKdJDHfXLy11yf
+ke+Ri7fc7Waiz45mO7yfOgLgJ90WmMCV1Aqk5IGadZQ1nJBfiDcGrVmVCrDRZ9MZyonnMlo2HD6C
+qFqTvsbQZJG2z9m2GM/bftJlo6bEjhcxwft+dtvTheNYsnd6djtsL1Ac59v2Z3kf9YKVmgenFK+P
+3CghZwnS1k1aHBkcjndcw5QkPTJrS37UeJSDvjdNzl/HHk484IkzlQsPpTLWPFp5LBk=
+-----END CERTIFICATE-----
+
+TrustCor RootCert CA-2
+======================
+-----BEGIN CERTIFICATE-----
+MIIGLzCCBBegAwIBAgIIJaHfyjPLWQIwDQYJKoZIhvcNAQELBQAwgaQxCzAJBgNVBAYTAlBBMQ8w
+DQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5MSQwIgYDVQQKDBtUcnVzdENvciBT
+eXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRydXN0Q29yIENlcnRpZmljYXRlIEF1dGhvcml0
+eTEfMB0GA1UEAwwWVHJ1c3RDb3IgUm9vdENlcnQgQ0EtMjAeFw0xNjAyMDQxMjMyMjNaFw0zNDEy
+MzExNzI2MzlaMIGkMQswCQYDVQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5h
+bWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5U
+cnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAdBgNVBAMMFlRydXN0Q29yIFJvb3RDZXJ0
+IENBLTIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCnIG7CKqJiJJWQdsg4foDSq8Gb
+ZQWU9MEKENUCrO2fk8eHyLAnK0IMPQo+QVqedd2NyuCb7GgypGmSaIwLgQ5WoD4a3SwlFIIvl9Nk
+RvRUqdw6VC0xK5mC8tkq1+9xALgxpL56JAfDQiDyitSSBBtlVkxs1Pu2YVpHI7TYabS3OtB0PAx1
+oYxOdqHp2yqlO/rOsP9+aij9JxzIsekp8VduZLTQwRVtDr4uDkbIXvRR/u8OYzo7cbrPb1nKDOOb
+XUm4TOJXsZiKQlecdu/vvdFoqNL0Cbt3Nb4lggjEFixEIFapRBF37120Hapeaz6LMvYHL1cEksr1
+/p3C6eizjkxLAjHZ5DxIgif3GIJ2SDpxsROhOdUuxTTCHWKF3wP+TfSvPd9cW436cOGlfifHhi5q
+jxLGhF5DUVCcGZt45vz27Ud+ez1m7xMTiF88oWP7+ayHNZ/zgp6kPwqcMWmLmaSISo5uZk3vFsQP
+eSghYA2FFn3XVDjxklb9tTNMg9zXEJ9L/cb4Qr26fHMC4P99zVvh1Kxhe1fVSntb1IVYJ12/+Ctg
+rKAmrhQhJ8Z3mjOAPF5GP/fDsaOGM8boXg25NSyqRsGFAnWAoOsk+xWq5Gd/bnc/9ASKL3x74xdh
+8N0JqSDIvgmk0H5Ew7IwSjiqqewYmgeCK9u4nBit2uBGF6zPXQIDAQABo2MwYTAdBgNVHQ4EFgQU
+2f4hQG6UnrybPZx9mCAZ5YwwYrIwHwYDVR0jBBgwFoAU2f4hQG6UnrybPZx9mCAZ5YwwYrIwDwYD
+VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBAJ5Fngw7tu/h
+Osh80QA9z+LqBrWyOrsGS2h60COXdKcs8AjYeVrXWoSK2BKaG9l9XE1wxaX5q+WjiYndAfrs3fnp
+kpfbsEZC89NiqpX+MWcUaViQCqoL7jcjx1BRtPV+nuN79+TMQjItSQzL/0kMmx40/W5ulop5A7Zv
+2wnL/V9lFDfhOPXzYRZY5LVtDQsEGz9QLX+zx3oaFoBg+Iof6Rsqxvm6ARppv9JYx1RXCI/hOWB3
+S6xZhBqI8d3LT3jX5+EzLfzuQfogsL7L9ziUwOHQhQ+77Sxzq+3+knYaZH9bDTMJBzN7Bj8RpFxw
+PIXAz+OQqIN3+tvmxYxoZxBnpVIt8MSZj3+/0WvitUfW2dCFmU2Umw9Lje4AWkcdEQOsQRivh7dv
+DDqPys/cA8GiCcjl/YBeyGBCARsaU1q7N6a3vLqE6R5sGtRk2tRD/pOLS/IseRYQ1JMLiI+h2IYU
+RpFHmygk71dSTlxCnKr3Sewn6EAes6aJInKc9Q0ztFijMDvd1GpUk74aTfOTlPf8hAs/hCBcNANE
+xdqtvArBAs8e5ZTZ845b2EzwnexhF7sUMlQMAimTHpKG9n/v55IFDlndmQguLvqcAFLTxWYp5KeX
+RKQOKIETNcX2b2TmQcTVL8w0RSXPQQCWPUouwpaYT05KnJe32x+SMsj/D1Fu1uwJ
+-----END CERTIFICATE-----
+
+TrustCor ECA-1
+==============
+-----BEGIN CERTIFICATE-----
+MIIEIDCCAwigAwIBAgIJAISCLF8cYtBAMA0GCSqGSIb3DQEBCwUAMIGcMQswCQYDVQQGEwJQQTEP
+MA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3Ig
+U3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5UcnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3Jp
+dHkxFzAVBgNVBAMMDlRydXN0Q29yIEVDQS0xMB4XDTE2MDIwNDEyMzIzM1oXDTI5MTIzMTE3Mjgw
+N1owgZwxCzAJBgNVBAYTAlBBMQ8wDQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5
+MSQwIgYDVQQKDBtUcnVzdENvciBTeXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRydXN0Q29y
+IENlcnRpZmljYXRlIEF1dGhvcml0eTEXMBUGA1UEAwwOVHJ1c3RDb3IgRUNBLTEwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDPj+ARtZ+odnbb3w9U73NjKYKtR8aja+3+XzP4Q1HpGjOR
+MRegdMTUpwHmspI+ap3tDvl0mEDTPwOABoJA6LHip1GnHYMma6ve+heRK9jGrB6xnhkB1Zem6g23
+xFUfJ3zSCNV2HykVh0A53ThFEXXQmqc04L/NyFIduUd+Dbi7xgz2c1cWWn5DkR9VOsZtRASqnKmc
+p0yJF4OuowReUoCLHhIlERnXDH19MURB6tuvsBzvgdAsxZohmz3tQjtQJvLsznFhBmIhVE5/wZ0+
+fyCMgMsq2JdiyIMzkX2woloPV+g7zPIlstR8L+xNxqE6FXrntl019fZISjZFZtS6mFjBAgMBAAGj
+YzBhMB0GA1UdDgQWBBREnkj1zG1I1KBLf/5ZJC+Dl5mahjAfBgNVHSMEGDAWgBREnkj1zG1I1KBL
+f/5ZJC+Dl5mahjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsF
+AAOCAQEABT41XBVwm8nHc2FvcivUwo/yQ10CzsSUuZQRg2dd4mdsdXa/uwyqNsatR5Nj3B5+1t4u
+/ukZMjgDfxT2AHMsWbEhBuH7rBiVDKP/mZb3Kyeb1STMHd3BOuCYRLDE5D53sXOpZCz2HAF8P11F
+hcCF5yWPldwX8zyfGm6wyuMdKulMY/okYWLW2n62HGz1Ah3UKt1VkOsqEUc8Ll50soIipX1TH0Xs
+J5F95yIW6MBoNtjG8U+ARDL54dHRHareqKucBK+tIA5kmE2la8BIWJZpTdwHjFGTot+fDz2LYLSC
+jaoITmJF4PkL0uDgPFveXHEnJcLmA4GLEFPjx1WitJ/X5g==
+-----END CERTIFICATE-----
+
+SSL.com Root Certification Authority RSA
+========================================
+-----BEGIN CERTIFICATE-----
+MIIF3TCCA8WgAwIBAgIIeyyb0xaAMpkwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxDjAM
+BgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRpb24x
+MTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBSU0EwHhcNMTYw
+MjEyMTczOTM5WhcNNDEwMjEyMTczOTM5WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMx
+EDAOBgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NM
+LmNvbSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFJTQTCCAiIwDQYJKoZIhvcNAQEBBQAD
+ggIPADCCAgoCggIBAPkP3aMrfcvQKv7sZ4Wm5y4bunfh4/WvpOz6Sl2RxFdHaxh3a3by/ZPkPQ/C
+Fp4LZsNWlJ4Xg4XOVu/yFv0AYvUiCVToZRdOQbngT0aXqhvIuG5iXmmxX9sqAn78bMrzQdjt0Oj8
+P2FI7bADFB0QDksZ4LtO7IZl/zbzXmcCC52GVWH9ejjt/uIZALdvoVBidXQ8oPrIJZK0bnoix/ge
+oeOy3ZExqysdBP+lSgQ36YWkMyv94tZVNHwZpEpox7Ko07fKoZOI68GXvIz5HdkihCR0xwQ9aqkp
+k8zruFvh/l8lqjRYyMEjVJ0bmBHDOJx+PYZspQ9AhnwC9FwCTyjLrnGfDzrIM/4RJTXq/LrFYD3Z
+fBjVsqnTdXgDciLKOsMf7yzlLqn6niy2UUb9rwPW6mBo6oUWNmuF6R7As93EJNyAKoFBbZQ+yODJ
+gUEAnl6/f8UImKIYLEJAs/lvOCdLToD0PYFH4Ih86hzOtXVcUS4cK38acijnALXRdMbX5J+tB5O2
+UzU1/Dfkw/ZdFr4hc96SCvigY2q8lpJqPvi8ZVWb3vUNiSYE/CUapiVpy8JtynziWV+XrOvvLsi8
+1xtZPCvM8hnIk2snYxnP/Okm+Mpxm3+T/jRnhE6Z6/yzeAkzcLpmpnbtG3PrGqUNxCITIJRWCk4s
+bE6x/c+cCbqiM+2HAgMBAAGjYzBhMB0GA1UdDgQWBBTdBAkHovV6fVJTEpKV7jiAJQ2mWTAPBgNV
+HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFN0ECQei9Xp9UlMSkpXuOIAlDaZZMA4GA1UdDwEB/wQE
+AwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAIBgRlCn7Jp0cHh5wYfGVcpNxJK1ok1iOMq8bs3AD/CUr
+dIWQPXhq9LmLpZc7tRiRux6n+UBbkflVma8eEdBcHadm47GUBwwyOabqG7B52B2ccETjit3E+ZUf
+ijhDPwGFpUenPUayvOUiaPd7nNgsPgohyC0zrL/FgZkxdMF1ccW+sfAjRfSda/wZY52jvATGGAsl
+u1OJD7OAUN5F7kR/q5R4ZJjT9ijdh9hwZXT7DrkT66cPYakylszeu+1jTBi7qUD3oFRuIIhxdRjq
+erQ0cuAjJ3dctpDqhiVAq+8zD8ufgr6iIPv2tS0a5sKFsXQP+8hlAqRSAUfdSSLBv9jra6x+3uxj
+MxW3IwiPxg+NQVrdjsW5j+VFP3jbutIbQLH+cU0/4IGiul607BXgk90IH37hVZkLId6Tngr75qNJ
+vTYw/ud3sqB1l7UtgYgXZSD32pAAn8lSzDLKNXz1PQ/YK9f1JmzJBjSWFupwWRoyeXkLtoh/D1JI
+Pb9s2KJELtFOt3JY04kTlf5Eq/jXixtunLwsoFvVagCvXzfh1foQC5ichucmj87w7G6KVwuA406y
+wKBjYZC6VWg3dGq2ktufoYYitmUnDuy2n0Jg5GfCtdpBC8TTi2EbvPofkSvXRAdeuims2cXp71NI
+WuuA8ShYIc2wBlX7Jz9TkHCpBB5XJ7k=
+-----END CERTIFICATE-----
+
+SSL.com Root Certification Authority ECC
+========================================
+-----BEGIN CERTIFICATE-----
+MIICjTCCAhSgAwIBAgIIdebfy8FoW6gwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMCVVMxDjAMBgNV
+BAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRpb24xMTAv
+BgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEy
+MTgxNDAzWhcNNDEwMjEyMTgxNDAzWjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAO
+BgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNv
+bSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuBBAAiA2IA
+BEVuqVDEpiM2nl8ojRfLliJkP9x6jh3MCLOicSS6jkm5BBtHllirLZXI7Z4INcgn64mMU1jrYor+
+8FsPazFSY0E7ic3s7LaNGdM0B9y7xgZ/wkWV7Mt/qCPgCemB+vNH06NjMGEwHQYDVR0OBBYEFILR
+hXMw5zUE044CkvvlpNHEIejNMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUgtGFczDnNQTT
+jgKS++Wk0cQh6M0wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2cAMGQCMG/n61kRpGDPYbCW
+e+0F+S8Tkdzt5fxQaxFGRrMcIQBiu77D5+jNB5n5DQtdcj7EqgIwH7y6C+IwJPt8bYBVCpk+gA0z
+5Wajs6O7pdWLjwkspl1+4vAHCGht0nxpbl/f5Wpl
+-----END CERTIFICATE-----
+
+SSL.com EV Root Certification Authority RSA R2
+==============================================
+-----BEGIN CERTIFICATE-----
+MIIF6zCCA9OgAwIBAgIIVrYpzTS8ePYwDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNVBAYTAlVTMQ4w
+DAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9u
+MTcwNQYDVQQDDC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIy
+MB4XDTE3MDUzMTE4MTQzN1oXDTQyMDUzMDE4MTQzN1owgYIxCzAJBgNVBAYTAlVTMQ4wDAYDVQQI
+DAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMTcwNQYD
+VQQDDC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIyMIICIjAN
+BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjzZlQOHWTcDXtOlG2mvqM0fNTPl9fb69LT3w23jh
+hqXZuglXaO1XPqDQCEGD5yhBJB/jchXQARr7XnAjssufOePPxU7Gkm0mxnu7s9onnQqG6YE3Bf7w
+cXHswxzpY6IXFJ3vG2fThVUCAtZJycxa4bH3bzKfydQ7iEGonL3Lq9ttewkfokxykNorCPzPPFTO
+Zw+oz12WGQvE43LrrdF9HSfvkusQv1vrO6/PgN3B0pYEW3p+pKk8OHakYo6gOV7qd89dAFmPZiw+
+B6KjBSYRaZfqhbcPlgtLyEDhULouisv3D5oi53+aNxPN8k0TayHRwMwi8qFG9kRpnMphNQcAb9Zh
+CBHqurj26bNg5U257J8UZslXWNvNh2n4ioYSA0e/ZhN2rHd9NCSFg83XqpyQGp8hLH94t2S42Oim
+9HizVcuE0jLEeK6jj2HdzghTreyI/BXkmg3mnxp3zkyPuBQVPWKchjgGAGYS5Fl2WlPAApiiECto
+RHuOec4zSnaqW4EWG7WK2NAAe15itAnWhmMOpgWVSbooi4iTsjQc2KRVbrcc0N6ZVTsj9CLg+Slm
+JuwgUHfbSguPvuUCYHBBXtSuUDkiFCbLsjtzdFVHB3mBOagwE0TlBIqulhMlQg+5U8Sb/M3kHN48
++qvWBkofZ6aYMBzdLNvcGJVXZsb/XItW9XcCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNV
+HSMEGDAWgBT5YLvU49U09rj1BoAlp3PbRmmonjAdBgNVHQ4EFgQU+WC71OPVNPa49QaAJadz20Zp
+qJ4wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQBWs47LCp1Jjr+kxJG7ZhcFUZh1
+++VQLHqe8RT6q9OKPv+RKY9ji9i0qVQBDb6Thi/5Sm3HXvVX+cpVHBK+Rw82xd9qt9t1wkclf7nx
+Y/hoLVUE0fKNsKTPvDxeH3jnpaAgcLAExbf3cqfeIg29MyVGjGSSJuM+LmOW2puMPfgYCdcDzH2G
+guDKBAdRUNf/ktUM79qGn5nX67evaOI5JpS6aLe/g9Pqemc9YmeuJeVy6OLk7K4S9ksrPJ/psEDz
+OFSz/bdoyNrGj1E8svuR3Bznm53htw1yj+KkxKl4+esUrMZDBcJlOSgYAsOCsp0FvmXtll9ldDz7
+CTUue5wT/RsPXcdtgTpWD8w74a8CLyKsRspGPKAcTNZEtF4uXBVmCeEmKf7GUmG6sXP/wwyc5Wxq
+lD8UykAWlYTzWamsX0xhk23RO8yilQwipmdnRC652dKKQbNmC1r7fSOl8hqw/96bg5Qu0T/fkreR
+rwU7ZcegbLHNYhLDkBvjJc40vG93drEQw/cFGsDWr3RiSBd3kmmQYRzelYB0VI8YHMPzA9C/pEN1
+hlMYegouCRw2n5H9gooiS9EOUCXdywMMF8mDAAhONU2Ki+3wApRmLER/y5UnlhetCTCstnEXbosX
+9hwJ1C07mKVx01QT2WDz9UtmT/rx7iASjbSsV7FFY6GsdqnC+w==
+-----END CERTIFICATE-----
+
+SSL.com EV Root Certification Authority ECC
+===========================================
+-----BEGIN CERTIFICATE-----
+MIIClDCCAhqgAwIBAgIILCmcWxbtBZUwCgYIKoZIzj0EAwIwfzELMAkGA1UEBhMCVVMxDjAMBgNV
+BAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRpb24xNDAy
+BgNVBAMMK1NTTC5jb20gRVYgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYw
+MjEyMTgxNTIzWhcNNDEwMjEyMTgxNTIzWjB/MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMx
+EDAOBgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrU1NM
+LmNvbSBFViBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuB
+BAAiA2IABKoSR5CYG/vvw0AHgyBO8TCCogbR8pKGYfL2IWjKAMTH6kMAVIbc/R/fALhBYlzccBYy
+3h+Z1MzFB8gIH2EWB1E9fVwHU+M1OIzfzZ/ZLg1KthkuWnBaBu2+8KGwytAJKaNjMGEwHQYDVR0O
+BBYEFFvKXuXe0oGqzagtZFG22XKbl+ZPMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUW8pe
+5d7SgarNqC1kUbbZcpuX5k8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2gAMGUCMQCK5kCJ
+N+vp1RPZytRrJPOwPYdGWBrssd9v+1a6cGvHOMzosYxPD/fxZ3YOg9AeUY8CMD32IygmTMZgh5Mm
+m7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg==
+-----END CERTIFICATE-----
diff --git a/cloudformation/travis.template b/misc/cloudformation.template
index db4e59eb7e..db4e59eb7e 100644
--- a/cloudformation/travis.template
+++ b/misc/cloudformation.template
diff --git a/common/mb-icon-blue-circle.svg b/misc/mb-icon-blue-circle.svg
index 93f8c86a50..93f8c86a50 100644
--- a/common/mb-icon-blue-circle.svg
+++ b/misc/mb-icon-blue-circle.svg
diff --git a/common/mb-icon-blue-square.png b/misc/mb-icon-blue-square.png
index 0a9bc1cdea..0a9bc1cdea 100644
--- a/common/mb-icon-blue-square.png
+++ b/misc/mb-icon-blue-square.png
Binary files differ
diff --git a/common/mb-icon-blue-square.svg b/misc/mb-icon-blue-square.svg
index de531b179b..de531b179b 100644
--- a/common/mb-icon-blue-square.svg
+++ b/misc/mb-icon-blue-square.svg
diff --git a/proto/binary_program.proto b/misc/proto/binary_program.proto
index 9d06a209c3..9d06a209c3 100644
--- a/proto/binary_program.proto
+++ b/misc/proto/binary_program.proto
diff --git a/proto/glyphs.proto b/misc/proto/glyphs.proto
index 6930b47a2b..6930b47a2b 100644
--- a/proto/glyphs.proto
+++ b/misc/proto/glyphs.proto
diff --git a/proto/style.proto b/misc/proto/style.proto
index 90e5b65061..90e5b65061 100644
--- a/proto/style.proto
+++ b/misc/proto/style.proto
diff --git a/proto/vector_tile.proto b/misc/proto/vector_tile.proto
index 1cde2c7aa9..1cde2c7aa9 100644
--- a/proto/vector_tile.proto
+++ b/misc/proto/vector_tile.proto
diff --git a/package.json b/package.json
index fb62bc63ed..4ff4b32735 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@mapbox/mapbox-gl-native",
- "version": "3.5.6",
+ "version": "3.5.8",
"description": "Renders map tiles with Mapbox GL",
"keywords": [
"mapbox",
@@ -13,8 +13,8 @@
},
"license": "BSD-2-Clause",
"dependencies": {
- "nan": "^2.4.0",
- "node-pre-gyp": "^0.6.36",
+ "nan": "^2.6.2",
+ "node-pre-gyp": "^0.6.37",
"npm-run-all": "^4.0.2"
},
"devDependencies": {
@@ -23,6 +23,7 @@
"ejs": "^2.4.1",
"express": "^4.11.1",
"flow-remove-types": "^1.2.1",
+ "json-stringify-pretty-compact": "^1.0.4",
"lodash": "^4.16.4",
"mapbox-gl-styles": "2.0.2",
"pixelmatch": "^4.0.2",
@@ -38,7 +39,8 @@
"install": "node-pre-gyp install --fallback-to-build=false || make node",
"test": "tape platform/node/test/js/**/*.test.js",
"test-memory": "node --expose-gc platform/node/test/memory.test.js",
- "test-suite": "run-s test-render test-query",
+ "test-suite": "run-s test-render test-query test-expressions",
+ "test-expressions": "node platform/node/test/expression.test.js",
"test-render": "node platform/node/test/render.test.js",
"test-query": "node platform/node/test/query.test.js"
},
diff --git a/platform/android/.gitignore b/platform/android/.gitignore
index 4abd458378..3d6d48dc02 100644
--- a/platform/android/.gitignore
+++ b/platform/android/.gitignore
@@ -16,7 +16,7 @@ MapboxGLAndroidSDK/src/main/assets/
# Local settings
local.properties
-/configuration.gradle
+gradle/configuration.gradle
# Token file
MapboxGLAndroidSDKTestApp/src/main/res/values/developer-config.xml
@@ -30,3 +30,5 @@ captures/
# Generated test cases
MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/gen/
+# Generated list files from code generation
+/scripts/generate-style-code.list
diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md
index 7827bc129f..fa9b1d29cc 100644
--- a/platform/android/CHANGELOG.md
+++ b/platform/android/CHANGELOG.md
@@ -2,6 +2,20 @@
Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started.
+## master
+
+ - HeatmapLayer [#11046](https://github.com/mapbox/mapbox-gl-native/pull/11046)
+
+## 6.0.0-beta.2 - February 13, 2018
+ - Deprecate LocationEngine [#11185](https://github.com/mapbox/mapbox-gl-native/pull/11185)
+ - Remove LOST from SDK [11186](https://github.com/mapbox/mapbox-gl-native/pull/11186)
+ - Transparent surface configuration on TextureView [#11065](https://github.com/mapbox/mapbox-gl-native/pull/11065)
+ - Constrained setLatLng documentation, expose setLatLngZoom method [#11184](https://github.com/mapbox/mapbox-gl-native/pull/11184)
+ - Integration of new events library [#10999](https://github.com/mapbox/mapbox-gl-native/pull/10999)
+ - AddImage performance improvement [#11111](https://github.com/mapbox/mapbox-gl-native/pull/11111)
+ - Migrate MAS to 3.0.0, refactor GeoJson integration [#11149](https://github.com/mapbox/mapbox-gl-native/pull/11149)
+ - Remove @jar and @aar dependency suffixes [#11161](https://github.com/mapbox/mapbox-gl-native/pull/11161)
+
## 5.4.1 - February 9, 2018
- Don't recreate TextureView surface as part of view resizing, solves OOM crashes [#11148](https://github.com/mapbox/mapbox-gl-native/pull/11148)
- Don't invoke OnLowMemory before map is ready, solves startup crash on low memory devices [#11109](https://github.com/mapbox/mapbox-gl-native/pull/11109)
@@ -12,7 +26,7 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to
## 5.4.0 - January 30, 2018
- Blacklist Adreno 2xx GPU for VAO support [#11047](https://github.com/mapbox/mapbox-gl-native/pull/11047)
- Bearing tracking mode GPS_NORTH_FACING [#11095](https://github.com/mapbox/mapbox-gl-native/pull/11095)
- - Disable logging missing location permissons [#11084](https://github.com/mapbox/mapbox-gl-native/pull/11084)
+ - Disable logging for missing location permissions when location is disabled [#11084](https://github.com/mapbox/mapbox-gl-native/pull/11084)
- Create offline handler using the main thread looper [#11021](https://github.com/mapbox/mapbox-gl-native/pull/11021)
## 6.0.0-beta.1 - January 26, 2018
@@ -26,7 +40,17 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to
- Expose attribution manager as public API [#10942](https://github.com/mapbox/mapbox-gl-native/pull/10942)
- Replace Mapzen vector source example with Mapillary [#10931](https://github.com/mapbox/mapbox-gl-native/pull/10931)
- Add Hebrew localization [#10967](https://github.com/mapbox/mapbox-gl-native/pull/10967)
-
+ - Cleanup gradle configuration files [#10903](https://github.com/mapbox/mapbox-gl-native/pull/10903)
+ - Send double tap event only once [#10855](https://github.com/mapbox/mapbox-gl-native/pull/10855)
+ - Parameter validation for LatLngBounds#from [#10831](https://github.com/mapbox/mapbox-gl-native/pull/10831)
+ - Replace JSON parsing [#10815](https://github.com/mapbox/mapbox-gl-native/pull/10815)
+ - Orientation change regression test [#10814](https://github.com/mapbox/mapbox-gl-native/pull/10814)
+ - Max & min LatLng constants [#10780](https://github.com/mapbox/mapbox-gl-native/pull/10780)
+ - LatLng#wrap return new instance of LatLng [#10769](https://github.com/mapbox/mapbox-gl-native/pull/10769)
+ - Custom library loader [#10733](https://github.com/mapbox/mapbox-gl-native/pull/10733)
+ - Inconsistent parameters for LatLngBounds.union [#10728](https://github.com/mapbox/mapbox-gl-native/pull/10728)
+ - Gradle 4.1 / AS 3.0 [#10549](https://github.com/mapbox/mapbox-gl-native/pull/10549)
+
## 5.3.2 - January 22, 2018
- Validate surface creation before destroying [#10890](https://github.com/mapbox/mapbox-gl-native/pull/10890)
- Add filesource activation ot OfflineRegion [#10904](https://github.com/mapbox/mapbox-gl-native/pull/10904)
@@ -46,6 +70,7 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to
- RTL support [#10828](https://github.com/mapbox/mapbox-gl-native/pull/10828)
- Allow configuring Http url logging when a request fails [#10830](https://github.com/mapbox/mapbox-gl-native/pull/10830)
- Don't send double tap event multiple times for telemetry [#10854](https://github.com/mapbox/mapbox-gl-native/pull/10854)
+ - Fix code generation [#10856](https://github.com/mapbox/mapbox-gl-native/pull/10856)
- Use the correct cancelable callback after posting cancel [#10871](https://github.com/mapbox/mapbox-gl-native/pull/10871)
## 5.3.0 - December 20, 2017
@@ -117,9 +142,9 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to
## 5.2.0-beta.3 - October 26, 2017
-- Reorganize dependencies [#10268](https://github.com/mapbox/mapbox-gl-native/pull/10268)
-- Blacklist VAO usage on adreno 3xx [#10291](https://github.com/mapbox/mapbox-gl-native/pull/10291)
-- On stop null check [#10259](https://github.com/mapbox/mapbox-gl-native/pull/10259)
+- Reorganize dependencies [#10268](https://github.com/mapbox/mapbox-gl-native/pull/10268)
+- Blacklist VAO usage on adreno 3xx [#10291](https://github.com/mapbox/mapbox-gl-native/pull/10291)
+- On stop null check [#10259](https://github.com/mapbox/mapbox-gl-native/pull/10259)
## 5.2.0-beta.2 - October 19, 2017
@@ -132,13 +157,13 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to
- Add an UnsatisfiedLinkError safeguard [#10180](https://github.com/mapbox/mapbox-gl-native/pull/10180)
- Hold off handling hover events untill map has been created [#10142](https://github.com/mapbox/mapbox-gl-native/pull/10142)
- Added `MapboxMap.getCameraForGeometry()` to get a camera with zoom level and center coordinate computed to fit a shape [#10107](https://github.com/mapbox/mapbox-gl-native/pull/10107)
-- Fine tune gesture zoom & rotation [#10134](https://github.com/mapbox/mapbox-gl-native/pull/10134)
+- Fine tune gesture zoom & rotation [#10134](https://github.com/mapbox/mapbox-gl-native/pull/10134)
## 5.2.0-beta.1 - October 6, 2017
-* Allow multiple listeners for camera events, deprecate old API [#10141](https://github.com/mapbox/mapbox-gl-native/pull/10141)
-* Update symbol layer example with location [#10092](https://github.com/mapbox/mapbox-gl-native/pull/10092)
-* Make OfflineTilePyramidRegionDefinition parceable [#10080](https://github.com/mapbox/mapbox-gl-native/pull/10080)
+- Allow multiple listeners for camera events, deprecate old API [#10141](https://github.com/mapbox/mapbox-gl-native/pull/10141)
+- Update symbol layer example with location [#10092](https://github.com/mapbox/mapbox-gl-native/pull/10092)
+- Make OfflineTilePyramidRegionDefinition parceable [#10080](https://github.com/mapbox/mapbox-gl-native/pull/10080)
- Fix 5.2.0-SNAPSHOT CI build failing [#10079](https://github.com/mapbox/mapbox-gl-native/pull/10079)
- Deprecate MarkerView [#9782](https://github.com/mapbox/mapbox-gl-native/pull/9782)
- Hide overlain views on initalisation [#10068](https://github.com/mapbox/mapbox-gl-native/pull/10068)
@@ -185,10 +210,17 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to
- Fix javadoc comment for public setOfflineMapboxTileCountLimit method [#9454](https://github.com/mapbox/mapbox-gl-native/pull/9454)
- add Map change & visibility test activities [#9425](https://github.com/mapbox/mapbox-gl-native/pull/9425)
- build release package once during ci build [#9351](https://github.com/mapbox/mapbox-gl-native/pull/9351)
-* Add support for ImageSource [#9110](https://github.com/mapbox/mapbox-gl-native/pull/9110)
-* Increased the default maximum zoom level from 20 to 22. [#9835](https://github.com/mapbox/mapbox-gl-native/pull/9835)
+- Add support for ImageSource [#9110](https://github.com/mapbox/mapbox-gl-native/pull/9110)
+- Increased the default maximum zoom level from 20 to 22. [#9835](https://github.com/mapbox/mapbox-gl-native/pull/9835)
-### 5.1.4 - September 25, 2017
+## 5.1.5 - October 31, 2017
+
+* Remove obsolete terminate context/display calls [#10162](https://github.com/mapbox/mapbox-gl-native/pull/10162)
+* Determine need for clip ID based on actual layers/tiles [#10216](https://github.com/mapbox/mapbox-gl-native/pull/10216)
+* Correctly alter sprite URLs [#10217](https://github.com/mapbox/mapbox-gl-native/pull/10217)
+* Russian and Ukrainian localizations [#9945](https://github.com/mapbox/mapbox-gl-native/pull/9945)
+
+## 5.1.4 - September 25, 2017
* Update translations [#10033](https://github.com/mapbox/mapbox-gl-native/pull/10033) & [#9945](https://github.com/mapbox/mapbox-gl-native/pull/9945)
* Continue rendering tiles despite erros [#10012](https://github.com/mapbox/mapbox-gl-native/pull/10012)
@@ -281,7 +313,7 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to
* Infinite location animation updates [#9194](https://github.com/mapbox/mapbox-gl-native/pull/9194)
* Invoke callback with valid fling gestures [#9192](https://github.com/mapbox/mapbox-gl-native/pull/9192)
* Keep location tracking after screen rotation [#9187](https://github.com/mapbox/mapbox-gl-native/pull/9187)
-* Update components with camera values when animating [#9174](https://github.com/mapbox/mapbox-gl-native/pull/9174)
+* Update components with camera values when animating [#9174](https://github.com/mapbox/mapbox-gl-native/pull/9174)
* Validate if gestures should execute [#9173](https://github.com/mapbox/mapbox-gl-native/pull/9173)
* Custom location source and LOST integration [#9142](https://github.com/mapbox/mapbox-gl-native/pull/9142)
@@ -683,12 +715,6 @@ Mapbox Android 4.0.0 is the most ambitious Android release to date with 3 major
- Satellite Streets Style ([#2739](https://github.com/mapbox/mapbox-gl-native/issues/2739))
- **RESOLVED** Black Screen On Ice Cream Sandwich and Jelly Bean devices ([#2802](https://github.com/mapbox/mapbox-gl-native/issues/2802))
-
## 2.1.0 - October 21, 2015
- Initial Android release.
-
-Known issues:
-
-- Black Screen On Ice Cream Sandwich and Jelly Bean devices ([#2802](https://github.com/mapbox/mapbox-gl-native/issues/2802))
- - Resolved in 2.2.0
diff --git a/platform/android/DISTRIBUTE.md b/platform/android/DISTRIBUTE.md
index 648f26ce25..1c61748322 100644
--- a/platform/android/DISTRIBUTE.md
+++ b/platform/android/DISTRIBUTE.md
@@ -13,7 +13,7 @@ BUILDTYPE=Debug or BUILDTYPE=Release
##### Creating an Android Archive file that supports all ABIs
```sh
-BUILDTYPE=RELEASE make apackage
+BUILDTYPE=Release make apackage
```
This will build native libraries to support following ABIs:
diff --git a/platform/android/MapboxGLAndroidSDK/.gitignore b/platform/android/MapboxGLAndroidSDK/.gitignore
deleted file mode 100644
index cec211fe81..0000000000
--- a/platform/android/MapboxGLAndroidSDK/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-lint-baseline.xml
-lint/lint-baseline-local.xml \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/build.gradle b/platform/android/MapboxGLAndroidSDK/build.gradle
index f70f987271..e2e0881857 100644
--- a/platform/android/MapboxGLAndroidSDK/build.gradle
+++ b/platform/android/MapboxGLAndroidSDK/build.gradle
@@ -1,37 +1,32 @@
apply plugin: 'com.android.library'
dependencies {
- compile rootProject.ext.dep.supportAnnotations
- compile rootProject.ext.dep.supportFragmentV4
- compile rootProject.ext.dep.timber
- compile rootProject.ext.dep.okhttp3
- provided(rootProject.ext.dep.lost) {
- exclude group: 'com.android.support'
- }
- testCompile rootProject.ext.dep.junit
- testCompile rootProject.ext.dep.mockito
- testCompile rootProject.ext.dep.robolectric
-
- // Mapbox Android Services (GeoJSON support)
- compile(rootProject.ext.dep.mapboxJavaGeoJSON) {
- transitive = true
- }
-
- // Mapbox Android Services (Telemetry support)
- compile(rootProject.ext.dep.mapboxAndroidTelemetry) {
- transitive = true
+ api dependenciesList.mapboxAndroidTelemetry
+ api dependenciesList.mapboxJavaGeoJSON
+ implementation dependenciesList.supportAnnotations
+ implementation dependenciesList.supportFragmentV4
+ implementation dependenciesList.timber
+ implementation dependenciesList.okhttp3
+ compileOnly(dependenciesList.lost) {
+ exclude group: 'com.google.guava'
exclude group: 'com.android.support'
}
+ testImplementation dependenciesList.lost
+ testImplementation dependenciesList.junit
+ testImplementation dependenciesList.mockito
+ testImplementation dependenciesList.robolectric
}
android {
- compileSdkVersion rootProject.ext.compileSdkVersion
- buildToolsVersion rootProject.ext.buildToolsVersion
+ compileSdkVersion androidVersions.compileSdkVersion
+ buildToolsVersion androidVersions.buildToolsVersion
defaultConfig {
- minSdkVersion rootProject.ext.minSdkVersion
- targetSdkVersion rootProject.ext.targetSdkVersion
+ minSdkVersion androidVersions.minSdkVersion
+ targetSdkVersion androidVersions.targetSdkVersion
buildConfigField "String", "GIT_REVISION_SHORT", String.format("\"%s\"", getGitRevision())
+ buildConfigField "String", "MAPBOX_SDK_IDENTIFIER", String.format("\"%s\"", "mapbox-maps-android")
+ buildConfigField "String", "MAPBOX_SDK_VERSION", String.format("\"%s\"", project.VERSION_NAME)
buildConfigField "String", "MAPBOX_VERSION_STRING", String.format("\"Mapbox/%s\"", project.VERSION_NAME)
buildConfigField "String", "MAPBOX_EVENTS_USER_AGENT", String.format("\"MapboxEventsAndroid/%s\"", project.VERSION_NAME)
}
@@ -71,12 +66,12 @@ android {
arguments "-DANDROID_CPP_FEATURES=rtti;exceptions"
arguments "-DMBGL_PLATFORM=android"
arguments "-DMASON_PLATFORM=android"
- arguments "-DNodeJS_EXECUTABLE=" + rootProject.ext.node
- arguments "-Dnpm_EXECUTABLE=" + rootProject.ext.npm
+ arguments "-DNodeJS_EXECUTABLE=" + node
+ arguments "-Dnpm_EXECUTABLE=" + npm
// Enable ccache if the user has installed it.
- if (rootProject.ext.ccache?.trim()) {
- arguments "-DANDROID_CCACHE=" + rootProject.ext.ccache
+ if (ccache?.trim()) {
+ arguments "-DANDROID_CCACHE=" + ccache
// ccache splits up the compile command until multiple invocations and uses -E
// with one of them, and clang doesn't like unused arguments in that case.
cFlags "-Qunused-arguments"
@@ -115,20 +110,19 @@ android {
}
compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_7
- targetCompatibility JavaVersion.VERSION_1_7
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
}
lintOptions {
- disable 'MissingTranslation', 'TypographyQuotes'
- baseline file("lint-baseline-local.xml")
+ disable 'MissingTranslation', 'TypographyQuotes', 'ObsoleteLintCustomCheck', 'MissingPermission'
checkAllWarnings true
warningsAsErrors false
}
testOptions {
unitTests {
- returnDefaultValues = true
+ returnDefaultValues true
}
}
@@ -136,15 +130,10 @@ android {
debug {
jniDebuggable true
}
-
- release {
- // aar proguard configuration
- jniDebuggable false
- }
}
}
-def getGitRevision() {
+def static getGitRevision() {
def cmd = "git rev-parse --short HEAD"
def proc = cmd.execute()
def ref = proc.text.trim()
@@ -156,8 +145,7 @@ configurations {
all*.exclude group: 'commons-collections', module: 'commons-collections'
}
-apply from: 'gradle-javadoc.gradle'
-apply from: 'gradle-publish.gradle'
-apply from: 'gradle-checkstyle.gradle'
-apply from: 'gradle-tests-staticblockremover.gradle'
-apply from: '../gradle-lint.gradle'
+apply from: "${rootDir}/gradle/gradle-javadoc.gradle"
+apply from: "${rootDir}/gradle/gradle-publish.gradle"
+apply from: "${rootDir}/gradle/gradle-checkstyle.gradle"
+apply from: "${rootDir}/gradle/gradle-tests-staticblockremover.gradle"
diff --git a/platform/android/MapboxGLAndroidSDK/gradle.properties b/platform/android/MapboxGLAndroidSDK/gradle.properties
index 8839f21d8f..3796173fd9 100644
--- a/platform/android/MapboxGLAndroidSDK/gradle.properties
+++ b/platform/android/MapboxGLAndroidSDK/gradle.properties
@@ -1,5 +1,5 @@
GROUP=com.mapbox.mapboxsdk
-VERSION_NAME=5.4.2-SNAPSHOT
+VERSION_NAME=7.0.0-SNAPSHOT
POM_DESCRIPTION=Mapbox GL Android SDK
POM_URL=https://github.com/mapbox/mapbox-gl-native
diff --git a/platform/android/MapboxGLAndroidSDK/lint-baseline-local.xml b/platform/android/MapboxGLAndroidSDK/lint-baseline-local.xml
deleted file mode 100644
index 0a76f53505..0000000000
--- a/platform/android/MapboxGLAndroidSDK/lint-baseline-local.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="4" by="lint 2.3.1">
-
- <issue
- id="MissingTranslation"
- message="&quot;`mapbox_attributionErrorNoBrowser`&quot; is not translated in &quot;ca&quot; (Catalan), &quot;es&quot; (Spanish), &quot;lt&quot; (Lithuanian), &quot;nl&quot; (Dutch), &quot;sv&quot; (Swedish), &quot;vi&quot; (Vietnamese)"
- errorLine1=" &lt;string name=&quot;mapbox_attributionErrorNoBrowser&quot;>No web browser installed on device, can\&apos;t open web page.&lt;/string>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/values/strings.xml"
- line="13"
- column="13"/>
- </issue>
-
- <issue
- id="MissingTranslation"
- message="&quot;`mapbox_telemetrySettings`&quot; is not translated in &quot;ca&quot; (Catalan), &quot;es&quot; (Spanish), &quot;lt&quot; (Lithuanian), &quot;nl&quot; (Dutch), &quot;sv&quot; (Swedish), &quot;vi&quot; (Vietnamese)"
- errorLine1=" &lt;string name=&quot;mapbox_telemetrySettings&quot;>Telemetry Settings&lt;/string>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/values/strings.xml"
- line="15"
- column="13"/>
- </issue>
-
- <issue
- id="TypographyQuotes"
- message="Replace straight quotes (&apos;&apos;) with directional quotes (‘’, &amp;#8216; and &amp;#8217;) ?"
- errorLine1=" &lt;string name=&quot;mapbox_attributionTelemetryMessage&quot;>Estàs ajudant a millorar els mapes d\&apos;OpenStreetMap i de Mapbox aportant dades d\&apos;ús anònimes.&lt;/string>"
- errorLine2=" ^">
- <location
- file="src/main/res/values-ca/strings.xml"
- line="9"
- column="55"/>
- </issue>
-
-</issues>
diff --git a/platform/android/MapboxGLAndroidSDK/lint/lint-baseline-ci.xml b/platform/android/MapboxGLAndroidSDK/lint/lint-baseline-ci.xml
deleted file mode 100644
index fd65c9f627..0000000000
--- a/platform/android/MapboxGLAndroidSDK/lint/lint-baseline-ci.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- REMEMBER! First you run Lint locally you'll need to move lint-baseline-local.xml.xml file
- generated into the lint folder and called it lint-baseline-local.xml
- If you remove any error when running Lint locally, you'll get a warning from the command
- line advising you to remove it from the baseline. If you remove it (remember to remove it
- from lint-baseline-local.xml file) you should remove it too from
- lint-baseline-ci.xml (THIS FILE) which is the only one included in the repo.
- Eventually, it'll be removed (when we remove all current lint errors included). -->
-<issues format="4" by="lint 2.3.1">
-
- <issue
- id="MissingTranslation"
- message="&quot;`mapbox_attributionErrorNoBrowser`&quot; is not translated in &quot;ca&quot; (Catalan), &quot;es&quot; (Spanish), &quot;lt&quot; (Lithuanian), &quot;nl&quot; (Dutch), &quot;sv&quot; (Swedish), &quot;vi&quot; (Vietnamese)"
- errorLine1=" &lt;string name=&quot;mapbox_attributionErrorNoBrowser&quot;>No web browser installed on device, can\&apos;t open web page.&lt;/string>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/values/strings.xml"
- line="13"
- column="13"/>
- </issue>
-
- <issue
- id="MissingTranslation"
- message="&quot;`mapbox_telemetrySettings`&quot; is not translated in &quot;ca&quot; (Catalan), &quot;es&quot; (Spanish), &quot;lt&quot; (Lithuanian), &quot;nl&quot; (Dutch), &quot;sv&quot; (Swedish), &quot;vi&quot; (Vietnamese)"
- errorLine1=" &lt;string name=&quot;mapbox_telemetrySettings&quot;>Telemetry Settings&lt;/string>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/values/strings.xml"
- line="15"
- column="13"/>
- </issue>
-
- <issue
- id="TypographyQuotes"
- message="Replace straight quotes (&apos;&apos;) with directional quotes (‘’, &amp;#8216; and &amp;#8217;) ?"
- errorLine1=" &lt;string name=&quot;mapbox_attributionTelemetryMessage&quot;>Estàs ajudant a millorar els mapes d\&apos;OpenStreetMap i de Mapbox aportant dades d\&apos;ús anònimes.&lt;/string>"
- errorLine2=" ^">
- <location
- file="src/main/res/values-ca/strings.xml"
- line="9"
- column="55"/>
- </issue>
-
-</issues>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/EmptyLocationSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/EmptyLocationSource.java
deleted file mode 100644
index 8ea7e61eee..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/EmptyLocationSource.java
+++ /dev/null
@@ -1,107 +0,0 @@
-package com.mapbox.mapboxsdk;
-
-
-import android.location.Location;
-import android.support.annotation.Nullable;
-
-import com.mapbox.mapboxsdk.location.LocationSource;
-import com.mapbox.services.android.telemetry.location.LocationEngine;
-import com.mapbox.services.android.telemetry.location.LocationEngineListener;
-
-class EmptyLocationSource extends LocationSource {
-
- /**
- * Activate the location engine which will connect whichever location provider you are using. You'll need to call
- * this before requesting user location updates using {@link LocationEngine#requestLocationUpdates()}.
- */
- @Override
- public void activate() {
- // Intentionally left empty
- }
-
- /**
- * Disconnect the location engine which is useful when you no longer need location updates or requesting the users
- * {@link LocationEngine#getLastLocation()}. Before deactivating, you'll need to stop request user location updates
- * using {@link LocationEngine#removeLocationUpdates()}.
- */
- @Override
- public void deactivate() {
- // Intentionally left empty
- }
-
- /**
- * Check if your location provider has been activated/connected. This is mainly used internally but is also useful in
- * the rare case when you'd like to know if your location engine is connected or not.
- *
- * @return boolean true if the location engine has been activated/connected, else false.
- */
- @Override
- public boolean isConnected() {
- return false;
- }
-
- /**
- * Returns the Last known location is the location provider is connected and location permissions are granted.
- *
- * @return the last known location
- */
- @Override
- @Nullable
- public Location getLastLocation() {
- return null;
- }
-
- /**
- * Request location updates to the location provider.
- */
- @Override
- public void requestLocationUpdates() {
- // Intentionally left empty
- }
-
- /**
- * Dismiss ongoing location update to the location provider.
- */
- @Override
- public void removeLocationUpdates() {
- // Intentionally left empty
- }
-
- /**
- * Invoked when the Location has changed.
- *
- * @param location the new location
- */
- @Override
- public void onLocationChanged(Location location) {
- // Intentionally left empty
- }
-
- /**
- * Useful when you'd like to add a location listener to handle location connections and update events. It is important
- * to note, that the callback will continue getting called even when your application isn't in the foreground.
- * Therefore, it is a good idea to use {@link LocationEngine#removeLocationEngineListener(LocationEngineListener)}
- * inside your activities {@code onStop()} method.
- *
- * @param listener A {@link LocationEngineListener} which you'd like to add to your location engine.
- * @since 2.0.0
- */
- @Override
- public void addLocationEngineListener(LocationEngineListener listener) {
- // Intentionally left empty
- }
-
- /**
- * If you no longer need your {@link LocationEngineListener} to be invoked with every location update, use this
- * method to remove it. It's also important to remove your listeners before the activity is destroyed to prevent any
- * potential memory leaks.
- *
- * @param listener the {@link LocationEngineListener} you'd like to remove from this {@link LocationEngine}.
- * @return true.
- * @since 2.0.0
- */
- @Override
- public boolean removeLocationEngineListener(LocationEngineListener listener) {
- return true;
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/LibraryLoader.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/LibraryLoader.java
index a024f0ab70..6633d5d952 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/LibraryLoader.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/LibraryLoader.java
@@ -3,18 +3,46 @@ package com.mapbox.mapboxsdk;
import timber.log.Timber;
/**
- * Centralises the knowledge about "mapbox-gl" library loading.
+ * Loads the mapbox-gl shared library
+ * <p>
+ * By default uses the {@link System#loadLibrary(String)},
+ * use {@link #setLibraryLoader(LibraryLoader)} to provide an alternative library loading hook.
+ * </p>
*/
-public class LibraryLoader {
+public abstract class LibraryLoader {
+
+ private static final LibraryLoader DEFAULT = new LibraryLoader() {
+ @Override
+ public void load(String name) {
+ System.loadLibrary(name);
+ }
+ };
+
+ private static volatile LibraryLoader loader = DEFAULT;
+
+ /**
+ * Set the library loader that loads the shared library.
+ *
+ * @param libraryLoader the library loader
+ */
+ public static void setLibraryLoader(LibraryLoader libraryLoader) {
+ loader = libraryLoader;
+ }
/**
* Loads "libmapbox-gl.so" native shared library.
+ * <p>
+ * Catches UnsatisfiedLinkErrors and prints a warning to logcat.
+ * </p>
*/
public static void load() {
try {
- System.loadLibrary("mapbox-gl");
+ loader.load("mapbox-gl");
} catch (UnsatisfiedLinkError error) {
Timber.e(error, "Failed to load native shared library.");
}
}
+
+ public abstract void load(String name);
}
+
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 357bcd5f99..853ea1c11b 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java
@@ -8,16 +8,12 @@ import android.support.annotation.NonNull;
import android.support.annotation.UiThread;
import android.text.TextUtils;
+import com.mapbox.android.core.location.LocationEngine;
+import com.mapbox.android.core.location.LocationEnginePriority;
+import com.mapbox.android.core.location.LocationEngineProvider;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.exceptions.MapboxConfigurationException;
-import com.mapbox.mapboxsdk.location.LocationSource;
import com.mapbox.mapboxsdk.net.ConnectivityReceiver;
-import com.mapbox.services.android.telemetry.location.LocationEngine;
-import com.mapbox.services.android.telemetry.location.LocationEnginePriority;
-import com.mapbox.services.android.telemetry.location.LocationEngineProvider;
-import com.mapbox.services.android.telemetry.MapboxTelemetry;
-
-import timber.log.Timber;
/**
* The entry point to initialize the Mapbox Android SDK.
@@ -56,15 +52,9 @@ public final class Mapbox {
INSTANCE = new Mapbox(appContext, accessToken, locationEngine);
locationEngine.setPriority(LocationEnginePriority.NO_POWER);
- try {
- MapboxTelemetry.getInstance().initialize(
- appContext, accessToken, BuildConfig.MAPBOX_EVENTS_USER_AGENT, locationEngine);
- } catch (Exception exception) {
- Timber.e(exception, "Unable to instantiate Mapbox telemetry");
- }
-
ConnectivityReceiver.instance(appContext);
}
+
return INSTANCE;
}
@@ -146,22 +136,11 @@ public final class Mapbox {
}
/**
- * Returns a location source instance with empty methods.
- *
- * @return an empty location source implementation
- * @deprecated Replaced by {@link Mapbox#getLocationEngine()}
- */
- @Deprecated
- public static LocationSource getLocationSource() {
- return new EmptyLocationSource();
- }
-
-
- /**
* Returns the location engine used by the SDK.
*
* @return the location engine configured
*/
+ // TODO Do we need to expose this?
public static LocationEngine getLocationEngine() {
return INSTANCE.locationEngine;
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java
index c2f19072db..e732b2525f 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java
@@ -8,7 +8,7 @@ import android.support.annotation.FloatRange;
import com.mapbox.mapboxsdk.R;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.services.android.telemetry.utils.MathUtils;
+import com.mapbox.mapboxsdk.utils.MathUtils;
/**
* Resembles the position, angle, zoom and tilt of the user's viewpoint.
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/GeometryConstants.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/GeometryConstants.java
new file mode 100644
index 0000000000..7a17e500ca
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/GeometryConstants.java
@@ -0,0 +1,77 @@
+package com.mapbox.mapboxsdk.constants;
+
+/**
+ * Contains constants used throughout the sdk classes.
+ *
+ * @since 6.0.0
+ */
+public class GeometryConstants {
+
+ /**
+ * The <a href='http://en.wikipedia.org/wiki/Earth_radius#Equatorial_radius'>equatorial radius</a>
+ * value in meters
+ *
+ * @since 6.0.0
+ */
+ public static final int RADIUS_EARTH_METERS = 6378137;
+
+ /**
+ * This constant represents the lowest longitude value available to represent a geolocation.
+ *
+ * @since 6.0.0
+ */
+ public static final double MIN_LONGITUDE = -180;
+
+ /**
+ * This constant represents the highest longitude value available to represent a geolocation.
+ *
+ * @since 6.0.0
+ */
+ public static final double MAX_LONGITUDE = 180;
+
+ /**
+ * This constant represents the lowest latitude value available to represent a geolocation.
+ *
+ * @since 6.0.0
+ */
+ public static final double MIN_LATITUDE = -90;
+
+ /**
+ * This constant represents the latitude span when representing a geolocation.
+ *
+ * @since 6.0.0
+ */
+ public static final double LATITUDE_SPAN = 180;
+
+ /**
+ * This constant represents the longitude span when representing a geolocation.
+ *
+ * @since 6.0.0
+ */
+ public static final double LONGITUDE_SPAN = 360;
+
+ /**
+ * This constant represents the highest latitude value available to represent a geolocation.
+ *
+ * @since 6.0.0
+ */
+ public static final double MAX_LATITUDE = 90;
+
+ /**
+ * Maximum latitude value in Mercator projection.
+ *
+ * @since 6.0.0
+ */
+ public static final double MAX_MERCATOR_LATITUDE = 85.05112877980659;
+
+ /**
+ * Minimum latitude value in Mercator projection.
+ *
+ * @since 6.0.0
+ */
+ public static final double MIN_MERCATOR_LATITUDE = -85.05112877980659;
+
+ private GeometryConstants() {
+ // Private constructor to prevent initializing of this class.
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLng.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLng.java
index eb57241196..79023e2fd9 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLng.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLng.java
@@ -5,8 +5,8 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.FloatRange;
-import com.mapbox.services.android.telemetry.constants.GeoConstants;
-import com.mapbox.services.android.telemetry.utils.MathUtils;
+import com.mapbox.mapboxsdk.constants.GeometryConstants;
+
/**
* A geographical location which contains a single latitude, longitude pair, with
@@ -105,20 +105,20 @@ public class LatLng implements ILatLng, Parcelable {
/**
* Set the latitude, in degrees.
* <p>
- * This value is in the range of [-85.05112878, 85.05112878], see {@link GeoConstants#MIN_LATITUDE} and
- * {@link GeoConstants#MAX_LATITUDE}
+ * This value is in the range of [-90, 90], see {@link GeometryConstants#MIN_LATITUDE} and
+ * {@link GeometryConstants#MAX_LATITUDE}
* </p>
*
* @param latitude the latitude value in degrees
- * @see GeoConstants#MIN_LATITUDE
- * @see GeoConstants#MAX_LATITUDE
+ * @see GeometryConstants#MIN_LATITUDE
+ * @see GeometryConstants#MAX_LATITUDE
*/
public void setLatitude(
- @FloatRange(from = GeoConstants.MIN_LATITUDE, to = GeoConstants.MAX_LATITUDE) double latitude) {
+ @FloatRange(from = GeometryConstants.MIN_LATITUDE, to = GeometryConstants.MAX_LATITUDE) double latitude) {
if (Double.isNaN(latitude)) {
throw new IllegalArgumentException("latitude must not be NaN");
}
- if (Math.abs(latitude) > 90.0) {
+ if (Math.abs(latitude) > GeometryConstants.MAX_LATITUDE) {
throw new IllegalArgumentException("latitude must be between -90 and 90");
}
this.latitude = latitude;
@@ -127,13 +127,13 @@ public class LatLng implements ILatLng, Parcelable {
/**
* Get the latitude, in degrees.
* <p>
- * This value is in the range of [-85.05112878, 85.05112878], see {@link GeoConstants#MIN_LATITUDE} and
- * {@link GeoConstants#MAX_LATITUDE}
+ * This value is in the range of [-90, 90], see {@link GeometryConstants#MIN_LATITUDE} and
+ * {@link GeometryConstants#MAX_LATITUDE}
* </p>
*
* @return the latitude value in degrees
- * @see GeoConstants#MIN_LATITUDE
- * @see GeoConstants#MAX_LATITUDE
+ * @see GeometryConstants#MIN_LATITUDE
+ * @see GeometryConstants#MAX_LATITUDE
*/
@Override
public double getLatitude() {
@@ -143,15 +143,15 @@ public class LatLng implements ILatLng, Parcelable {
/**
* Set the longitude, in degrees.
* <p>
- * This value is in the range of [-180, 180], see {@link GeoConstants#MIN_LONGITUDE} and
- * {@link GeoConstants#MAX_LONGITUDE}
+ * This value is in the range of [-180, 180], see {@link GeometryConstants#MIN_LONGITUDE} and
+ * {@link GeometryConstants#MAX_LONGITUDE}
* </p>
*
* @param longitude the longitude value in degrees
- * @see GeoConstants#MIN_LONGITUDE
- * @see GeoConstants#MAX_LONGITUDE
+ * @see GeometryConstants#MIN_LONGITUDE
+ * @see GeometryConstants#MAX_LONGITUDE
*/
- public void setLongitude(@FloatRange(from = GeoConstants.MIN_LONGITUDE, to = GeoConstants.MAX_LONGITUDE)
+ public void setLongitude(@FloatRange(from = GeometryConstants.MIN_LONGITUDE, to = GeometryConstants.MAX_LONGITUDE)
double longitude) {
if (Double.isNaN(longitude)) {
throw new IllegalArgumentException("longitude must not be NaN");
@@ -165,13 +165,13 @@ public class LatLng implements ILatLng, Parcelable {
/**
* Get the longitude, in degrees.
* <p>
- * This value is in the range of [-180, 180], see {@link GeoConstants#MIN_LONGITUDE} and
- * {@link GeoConstants#MAX_LONGITUDE}
+ * This value is in the range of [-180, 180], see {@link GeometryConstants#MIN_LONGITUDE} and
+ * {@link GeometryConstants#MAX_LONGITUDE}
* </p>
*
* @return the longitude value in degrees
- * @see GeoConstants#MIN_LONGITUDE
- * @see GeoConstants#MAX_LONGITUDE
+ * @see GeometryConstants#MIN_LONGITUDE
+ * @see GeometryConstants#MAX_LONGITUDE
*/
@Override
public double getLongitude() {
@@ -204,8 +204,33 @@ public class LatLng implements ILatLng, Parcelable {
* @return new LatLng object with wrapped Longitude
*/
public LatLng wrap() {
- longitude = MathUtils.wrap(longitude, GeoConstants.MIN_LONGITUDE, GeoConstants.MAX_LONGITUDE);
- return this;
+ return new LatLng(latitude, wrap(longitude, GeometryConstants.MIN_LONGITUDE, GeometryConstants.MAX_LONGITUDE));
+ }
+
+
+ /**
+ * Constrains value to the given range (including min & max) via modular arithmetic.
+ * <p>
+ * Same formula as used in Core GL (wrap.hpp)
+ * std::fmod((std::fmod((value - min), d) + d), d) + min;
+ *
+ * Multiples of max value will be wrapped to max.
+ *
+ * @param value Value to wrap
+ * @param min Minimum value
+ * @param max Maximum value
+ * @return Wrapped value
+ */
+ static double wrap(double value, double min, double max) {
+ double delta = max - min;
+
+ double firstMod = (value - min) % delta;
+ double secondMod = (firstMod + delta) % delta;
+
+ if (value >= max && secondMod == 0) {
+ return max;
+ }
+ return secondMod + min;
}
/**
@@ -305,6 +330,6 @@ public class LatLng implements ILatLng, Parcelable {
final double t3 = Math.sin(a1) * Math.sin(b1);
final double tt = Math.acos(t1 + t2 + t3);
- return GeoConstants.RADIUS_EARTH_METERS * tt;
+ return GeometryConstants.RADIUS_EARTH_METERS * tt;
}
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java
index 4fcb91033c..fc8d2ec8f0 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java
@@ -2,11 +2,12 @@ package com.mapbox.mapboxsdk.geometry;
import android.os.Parcel;
import android.os.Parcelable;
+import android.support.annotation.FloatRange;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import com.mapbox.mapboxsdk.constants.GeometryConstants;
import com.mapbox.mapboxsdk.exceptions.InvalidLatLngBoundsException;
-import com.mapbox.services.android.telemetry.constants.GeoConstants;
import java.util.ArrayList;
import java.util.List;
@@ -28,6 +29,10 @@ public class LatLngBounds implements Parcelable {
* Construct a new LatLngBounds based on its corners, given in NESW
* order.
*
+ * If eastern longitude is smaller than the western one, bounds will include antimeridian.
+ * For example, if the NE point is (10, -170) and the SW point is (-10, 170), then bounds will span over 20 degrees
+ * and cross the antimeridian.
+ *
* @param northLatitude Northern Latitude
* @param eastLongitude Eastern Longitude
* @param southLatitude Southern Latitude
@@ -47,10 +52,9 @@ public class LatLngBounds implements Parcelable {
* @return the bounds representing the world
*/
public static LatLngBounds world() {
- return new LatLngBounds.Builder()
- .include(new LatLng(GeoConstants.MAX_LATITUDE, GeoConstants.MAX_LONGITUDE))
- .include(new LatLng(GeoConstants.MIN_LATITUDE, GeoConstants.MIN_LONGITUDE))
- .build();
+ return LatLngBounds.from(
+ GeometryConstants.MAX_LATITUDE, GeometryConstants.MAX_LONGITUDE,
+ GeometryConstants.MIN_LATITUDE, GeometryConstants.MIN_LONGITUDE);
}
/**
@@ -60,8 +64,21 @@ public class LatLngBounds implements Parcelable {
* @return LatLng center of this LatLngBounds
*/
public LatLng getCenter() {
- return new LatLng((this.latitudeNorth + this.latitudeSouth) / 2,
- (this.longitudeEast + this.longitudeWest) / 2);
+ double latCenter = (this.latitudeNorth + this.latitudeSouth) / 2.0;
+ double longCenter;
+
+ if (this.longitudeEast > this.longitudeWest) {
+ longCenter = (this.longitudeEast + this.longitudeWest) / 2;
+ } else {
+ double halfSpan = (GeometryConstants.LONGITUDE_SPAN + this.longitudeEast - this.longitudeWest) / 2.0;
+ longCenter = this.longitudeWest + halfSpan;
+ if (longCenter >= GeometryConstants.MAX_LONGITUDE) {
+ longCenter = this.longitudeEast - halfSpan;
+ }
+ return new LatLng(latCenter, longCenter);
+ }
+
+ return new LatLng(latCenter, longCenter);
}
/**
@@ -162,10 +179,26 @@ public class LatLngBounds implements Parcelable {
* @return Span distance
*/
public double getLongitudeSpan() {
- return Math.abs(this.longitudeEast - this.longitudeWest);
+ double longSpan = Math.abs(this.longitudeEast - this.longitudeWest);
+ if (this.longitudeEast > this.longitudeWest) {
+ return longSpan;
+ }
+
+ // shortest span contains antimeridian
+ return GeometryConstants.LONGITUDE_SPAN - longSpan;
}
+ static double getLongitudeSpan(final double longEast, final double longWest) {
+ double longSpan = Math.abs(longEast - longWest);
+ if (longEast > longWest) {
+ return longSpan;
+ }
+
+ // shortest span contains antimeridian
+ return GeometryConstants.LONGITUDE_SPAN - longSpan;
+ }
+
/**
* Validate if LatLngBounds is empty, determined if absolute distance is
*
@@ -194,22 +227,45 @@ public class LatLngBounds implements Parcelable {
* @return LatLngBounds
*/
static LatLngBounds fromLatLngs(final List<? extends ILatLng> latLngs) {
- double minLat = 90;
- double minLon = 180;
- double maxLat = -90;
- double maxLon = -180;
+ double minLat = GeometryConstants.MAX_LATITUDE;
+ double maxLat = GeometryConstants.MIN_LATITUDE;
+
+ double eastLon = latLngs.get(0).getLongitude();
+ double westLon = latLngs.get(1).getLongitude();
+ double lonSpan = Math.abs(eastLon - westLon);
+ if (lonSpan < GeometryConstants.LONGITUDE_SPAN / 2) {
+ if (eastLon < westLon) {
+ double temp = eastLon;
+ eastLon = westLon;
+ westLon = temp;
+ }
+ } else {
+ lonSpan = GeometryConstants.LONGITUDE_SPAN - lonSpan;
+ if (westLon < eastLon) {
+ double temp = eastLon;
+ eastLon = westLon;
+ westLon = temp;
+ }
+ }
for (final ILatLng gp : latLngs) {
final double latitude = gp.getLatitude();
- final double longitude = gp.getLongitude();
-
minLat = Math.min(minLat, latitude);
- minLon = Math.min(minLon, longitude);
maxLat = Math.max(maxLat, latitude);
- maxLon = Math.max(maxLon, longitude);
+
+ final double longitude = gp.getLongitude();
+ if (!containsLongitude(eastLon, westLon, longitude)) {
+ final double eastSpan = getLongitudeSpan(longitude, westLon);
+ final double westSpan = getLongitudeSpan(eastLon, longitude);
+ if (eastSpan <= westSpan) {
+ eastLon = longitude;
+ } else {
+ westLon = longitude;
+ }
+ }
}
- return new LatLngBounds(maxLat, maxLon, minLat, minLon);
+ return new LatLngBounds(maxLat, eastLon, minLat, westLon);
}
/**
@@ -223,14 +279,69 @@ public class LatLngBounds implements Parcelable {
/**
* Constructs a LatLngBounds from doubles representing a LatLng pair.
+ *
+ * This values of latNorth and latSouth should be in the range of [-90, 90],
+ * see {@link GeometryConstants#MIN_LATITUDE} and {@link GeometryConstants#MAX_LATITUDE},
+ * otherwise IllegalArgumentException will be thrown.
* <p>
* This method doesn't recalculate most east or most west boundaries.
+ * Note that lonEast and lonWest will be wrapped to be in the range of [-180, 180],
+ * see {@link GeometryConstants#MIN_LONGITUDE} and {@link GeometryConstants#MAX_LONGITUDE}
* </p>
*/
- public static LatLngBounds from(double latNorth, double lonEast, double latSouth, double lonWest) {
+ public static LatLngBounds from(
+ @FloatRange(from = GeometryConstants.MIN_LATITUDE, to = GeometryConstants.MAX_LATITUDE) double latNorth,
+ double lonEast,
+ @FloatRange(from = GeometryConstants.MIN_LATITUDE, to = GeometryConstants.MAX_LATITUDE) double latSouth,
+ double lonWest) {
+
+ if (Double.isNaN(latNorth) || Double.isNaN(latSouth)) {
+ throw new IllegalArgumentException("latitude must not be NaN");
+ }
+
+ if (Double.isNaN(lonEast) || Double.isNaN(lonWest)) {
+ throw new IllegalArgumentException("longitude must not be NaN");
+ }
+
+ if (Double.isInfinite(lonEast) || Double.isInfinite(lonWest)) {
+ throw new IllegalArgumentException("longitude must not be infinite");
+ }
+
+ if (latNorth > GeometryConstants.MAX_LATITUDE || latNorth < GeometryConstants.MIN_LATITUDE
+ || latSouth > GeometryConstants.MAX_LATITUDE || latSouth < GeometryConstants.MIN_LATITUDE) {
+ throw new IllegalArgumentException("latitude must be between -90 and 90");
+ }
+
+ lonEast = LatLng.wrap(lonEast, GeometryConstants.MIN_LONGITUDE, GeometryConstants.MAX_LONGITUDE);
+ lonWest = LatLng.wrap(lonWest, GeometryConstants.MIN_LONGITUDE, GeometryConstants.MAX_LONGITUDE);
+
return new LatLngBounds(latNorth, lonEast, latSouth, lonWest);
}
+ private static double lat_(int z, int y) {
+ double n = Math.PI - 2.0 * Math.PI * y / Math.pow(2.0, z);
+ return Math.toDegrees(Math.atan(0.5 * (Math.exp(n) - Math.exp(-n))));
+ }
+
+ private static double lon_(int z, int x) {
+ return x / Math.pow(2.0, z) * 360.0 - GeometryConstants.MAX_LONGITUDE;
+ }
+
+ /**
+ * Constructs a LatLngBounds from a Tile identifier.
+ *
+ * Returned bounds will have latitude in the range of Mercator projection.
+ * @see GeometryConstants#MIN_MERCATOR_LATITUDE
+ * @see GeometryConstants#MAX_MERCATOR_LATITUDE
+ *
+ * @param z Tile zoom level.
+ * @param x Tile X coordinate.
+ * @param y Tile Y coordinate.
+ */
+ public static LatLngBounds from(int z, int x, int y) {
+ return new LatLngBounds(lat_(z, y), lon_(z, x + 1), lat_(z, y + 1), lon_(z, x));
+ }
+
/**
* Constructs a LatLngBounds from current bounds with an additional latitude-longitude pair.
*
@@ -266,6 +377,24 @@ public class LatLngBounds implements Parcelable {
return false;
}
+
+ private boolean containsLatitude(final double latitude) {
+ return (latitude <= this.latitudeNorth)
+ && (latitude >= this.latitudeSouth);
+ }
+
+ private boolean containsLongitude(final double longitude) {
+ return containsLongitude(this.longitudeEast, this.longitudeWest, longitude);
+ }
+
+ static boolean containsLongitude(final double eastLon, final double westLon, final double longitude) {
+ if (eastLon > westLon) {
+ return (longitude <= eastLon)
+ && (longitude >= westLon);
+ }
+ return (longitude < eastLon) || (longitude > westLon);
+ }
+
/**
* Determines whether this LatLngBounds contains a point.
*
@@ -273,12 +402,8 @@ public class LatLngBounds implements Parcelable {
* @return true, if the point is contained within the bounds
*/
public boolean contains(final ILatLng latLng) {
- final double latitude = latLng.getLatitude();
- final double longitude = latLng.getLongitude();
- return ((latitude <= this.latitudeNorth)
- && (latitude >= this.latitudeSouth))
- && ((longitude <= this.longitudeEast)
- && (longitude >= this.longitudeWest));
+ return containsLatitude(latLng.getLatitude())
+ && containsLongitude(latLng.getLongitude());
}
/**
@@ -288,7 +413,8 @@ public class LatLngBounds implements Parcelable {
* @return true, if the bounds is contained within the bounds
*/
public boolean contains(final LatLngBounds other) {
- return contains(other.getNorthEast()) && contains(other.getSouthWest());
+ return contains(other.getNorthEast())
+ && contains(other.getSouthWest());
}
/**
@@ -305,17 +431,17 @@ public class LatLngBounds implements Parcelable {
* Returns a new LatLngBounds that stretches to include another LatLngBounds,
* given by corner points.
*
- * @param lonNorth Northern Longitude
- * @param latEast Eastern Latitude
- * @param lonSouth Southern Longitude
- * @param latWest Western Longitude
+ * @param latNorth Northern Latitude
+ * @param lonEast Eastern Longitude
+ * @param latSouth Southern Latitude
+ * @param lonWest Western Longitude
* @return BoundingBox
*/
- public LatLngBounds union(final double lonNorth, final double latEast, final double lonSouth, final double latWest) {
- return new LatLngBounds((this.latitudeNorth < lonNorth) ? lonNorth : this.latitudeNorth,
- (this.longitudeEast < latEast) ? latEast : this.longitudeEast,
- (this.latitudeSouth > lonSouth) ? lonSouth : this.latitudeSouth,
- (this.longitudeWest > latWest) ? latWest : this.longitudeWest);
+ public LatLngBounds union(final double latNorth, final double lonEast, final double latSouth, final double lonWest) {
+ return new LatLngBounds((this.latitudeNorth < latNorth) ? latNorth : this.latitudeNorth,
+ (this.longitudeEast < lonEast) ? lonEast : this.longitudeEast,
+ (this.latitudeSouth > latSouth) ? latSouth : this.latitudeSouth,
+ (this.longitudeWest > lonWest) ? lonWest : this.longitudeWest);
}
/**
@@ -326,13 +452,13 @@ public class LatLngBounds implements Parcelable {
*/
@Nullable
public LatLngBounds intersect(LatLngBounds box) {
- double minLatWest = Math.max(getLonWest(), box.getLonWest());
- double maxLatEast = Math.min(getLonEast(), box.getLonEast());
- if (maxLatEast > minLatWest) {
- double minLonSouth = Math.max(getLatSouth(), box.getLatSouth());
- double maxLonNorth = Math.min(getLatNorth(), box.getLatNorth());
- if (maxLonNorth > minLonSouth) {
- return new LatLngBounds(maxLonNorth, maxLatEast, minLonSouth, minLatWest);
+ double minLonWest = Math.max(getLonWest(), box.getLonWest());
+ double maxLonEast = Math.min(getLonEast(), box.getLonEast());
+ if (maxLonEast > minLonWest) {
+ double minLatSouth = Math.max(getLatSouth(), box.getLatSouth());
+ double maxLatNorth = Math.min(getLatNorth(), box.getLatNorth());
+ if (maxLatNorth > minLatSouth) {
+ return new LatLngBounds(maxLatNorth, maxLonEast, minLatSouth, minLonWest);
}
}
return null;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationSource.java
deleted file mode 100644
index 1313587158..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationSource.java
+++ /dev/null
@@ -1,199 +0,0 @@
-package com.mapbox.mapboxsdk.location;
-
-import android.content.Context;
-import android.location.Location;
-import android.support.annotation.Nullable;
-
-import com.mapbox.mapboxsdk.Mapbox;
-import com.mapbox.services.android.telemetry.location.LocationEngine;
-import com.mapbox.services.android.telemetry.location.LocationEngineListener;
-import com.mapbox.services.android.telemetry.location.LocationEnginePriority;
-import com.mapzen.android.lost.api.LocationListener;
-import com.mapzen.android.lost.api.LocationRequest;
-import com.mapzen.android.lost.api.LocationServices;
-import com.mapzen.android.lost.api.LostApiClient;
-
-/**
- * LocationEngine using the Open Source Lost library
- * Manages locational updates. Contains methods to register and unregister location listeners.
- * <ul>
- * <li>You can register a LocationEngineListener with LocationSource#addLocationEngineListener(LocationEngineListener)
- * to receive location updates.</li>
- * <li> You can unregister a LocationEngineListener with
- * LocationEngine#removeLocationEngineListener(LocationEngineListener)} to stop receiving location updates.</li>
- * </ul>
- * <p>
- * Note: If registering a listener in your Activity.onStart() implementation, you should unregister it in
- * Activity.onStop(). (You won't receive location updates when paused, and this will cut down on unnecessary system
- * overhead). Do not unregister in Activity.onSaveInstanceState(), because this won't be called if the user moves back
- * in the history stack.
- * </p>
- *
- * @deprecated Use a {@link Mapbox#getLocationEngine()} instead.
- */
-@Deprecated
-public class LocationSource extends LocationEngine implements LostApiClient.ConnectionCallbacks, LocationListener {
-
- private Context context;
- private LostApiClient lostApiClient;
-
- /**
- * Constructs a location source instance.
- *
- * @param context the context from which the Application context will be derived.
- */
- public LocationSource(Context context) {
- super();
- this.context = context.getApplicationContext();
- lostApiClient = new LostApiClient.Builder(this.context)
- .addConnectionCallbacks(this)
- .build();
- }
-
- /**
- * Constructs a location source instance.
- * Needed to create empty location source implementations.
- */
- public LocationSource() {
- }
-
- /**
- * Activate the location engine which will connect whichever location provider you are using. You'll need to call
- * this before requesting user location updates using {@link LocationEngine#requestLocationUpdates()}.
- */
- @Override
- public void activate() {
- connect();
- }
-
- /**
- * Disconnect the location engine which is useful when you no longer need location updates or requesting the users
- * {@link LocationEngine#getLastLocation()}. Before deactivating, you'll need to stop request user location updates
- * using {@link LocationEngine#removeLocationUpdates()}.
- */
- @Override
- public void deactivate() {
- if (lostApiClient != null && lostApiClient.isConnected()) {
- lostApiClient.disconnect();
- }
- }
-
- /**
- * Check if your location provider has been activated/connected. This is mainly used internally but is also useful in
- * the rare case when you'd like to know if your location engine is connected or not.
- *
- * @return boolean true if the location engine has been activated/connected, else false.
- */
- @Override
- public boolean isConnected() {
- return lostApiClient.isConnected();
- }
-
- /**
- * Invoked when the location provider has connected.
- */
- @Override
- public void onConnected() {
- for (LocationEngineListener listener : locationListeners) {
- listener.onConnected();
- }
- }
-
- /**
- * Invoked when the location provider connection has been suspended.
- */
- @Override
- public void onConnectionSuspended() {
- // Empty
- }
-
- /**
- * Returns the Last known location is the location provider is connected and location permissions are granted.
- *
- * @return the last known location
- */
- @Override
- @Nullable
- public Location getLastLocation() {
- if (lostApiClient.isConnected()) {
- //noinspection MissingPermission
- return LocationServices.FusedLocationApi.getLastLocation(lostApiClient);
- }
- return null;
- }
-
- /**
- * Request location updates to the location provider.
- */
- @Override
- public void requestLocationUpdates() {
- LocationRequest request = LocationRequest.create();
-
- if (interval != null) {
- request.setInterval(interval);
- }
- if (fastestInterval != null) {
- request.setFastestInterval(fastestInterval);
- }
- if (smallestDisplacement != null) {
- request.setSmallestDisplacement(smallestDisplacement);
- }
-
- if (priority == LocationEnginePriority.NO_POWER) {
- request.setPriority(LocationRequest.PRIORITY_NO_POWER);
- } else if (priority == LocationEnginePriority.LOW_POWER) {
- request.setPriority(LocationRequest.PRIORITY_LOW_POWER);
- } else if (priority == LocationEnginePriority.BALANCED_POWER_ACCURACY) {
- request.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
- } else if (priority == LocationEnginePriority.HIGH_ACCURACY) {
- request.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
- }
-
- if (lostApiClient.isConnected()) {
- //noinspection MissingPermission
- LocationServices.FusedLocationApi.requestLocationUpdates(lostApiClient, request, this);
- }
- }
-
- /**
- * Dismiss ongoing location update to the location provider.
- */
- @Override
- public void removeLocationUpdates() {
- if (lostApiClient.isConnected()) {
- LocationServices.FusedLocationApi.removeLocationUpdates(lostApiClient, this);
- }
- }
-
- /**
- * Returns the location engine type.
- *
- * @return Lost type
- */
- @Override
- public Type obtainType() {
- return Type.LOST;
- }
-
- /**
- * Invoked when the Location has changed.
- *
- * @param location the new location
- */
- @Override
- public void onLocationChanged(Location location) {
- for (LocationEngineListener listener : locationListeners) {
- listener.onLocationChanged(location);
- }
- }
-
- private void connect() {
- if (lostApiClient != null) {
- if (lostApiClient.isConnected()) {
- onConnected();
- } else {
- lostApiClient.connect();
- }
- }
- }
-} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/package-info.java
deleted file mode 100644
index b27559e95e..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/package-info.java
+++ /dev/null
@@ -1,4 +0,0 @@
-/**
- * Contains the Mapbox Maps Android Location API classes.
- */
-package com.mapbox.mapboxsdk.location; \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java
index 2956d864e6..2bcbd5ce40 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java
@@ -10,12 +10,12 @@ import android.support.annotation.NonNull;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Toast;
+
import com.mapbox.mapboxsdk.R;
import com.mapbox.mapboxsdk.attribution.Attribution;
import com.mapbox.mapboxsdk.attribution.AttributionParser;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.style.sources.Source;
-import com.mapbox.services.android.telemetry.MapboxTelemetry;
import java.util.ArrayList;
import java.util.List;
@@ -30,17 +30,16 @@ import java.util.Set;
* Additionally an telemetry option item is shown to configure telemetry settings.
* </p>
*/
-class AttributionDialogManager implements View.OnClickListener, DialogInterface.OnClickListener {
+public class AttributionDialogManager implements View.OnClickListener, DialogInterface.OnClickListener {
private static final String MAP_FEEDBACK_URL = "https://www.mapbox.com/map-feedback";
private static final String MAP_FEEDBACK_LOCATION_FORMAT = MAP_FEEDBACK_URL + "/#/%f/%f/%d";
private final Context context;
private final MapboxMap mapboxMap;
- private String[] attributionTitles;
private Set<Attribution> attributionSet;
- AttributionDialogManager(@NonNull Context context, @NonNull MapboxMap mapboxMap) {
+ public AttributionDialogManager(@NonNull Context context, @NonNull MapboxMap mapboxMap) {
this.context = context;
this.mapboxMap = mapboxMap;
}
@@ -49,11 +48,10 @@ class AttributionDialogManager implements View.OnClickListener, DialogInterface.
@Override
public void onClick(View view) {
attributionSet = new AttributionBuilder(mapboxMap).build();
- showAttributionDialog();
+ showAttributionDialog(getAttributionTitles());
}
- private void showAttributionDialog() {
- attributionTitles = getAttributionTitles();
+ protected void showAttributionDialog(String[] attributionTitles) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.mapbox_attributionsDialogTitle);
builder.setAdapter(new ArrayAdapter<>(context, R.layout.mapbox_attribution_list_item, attributionTitles), this);
@@ -79,7 +77,7 @@ class AttributionDialogManager implements View.OnClickListener, DialogInterface.
}
private boolean isLatestEntry(int attributionKeyIndex) {
- return attributionKeyIndex == attributionTitles.length - 1;
+ return attributionKeyIndex == getAttributionTitles().length - 1;
}
private void showTelemetryDialog() {
@@ -89,7 +87,7 @@ class AttributionDialogManager implements View.OnClickListener, DialogInterface.
builder.setPositiveButton(R.string.mapbox_attributionTelemetryPositive, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- MapboxTelemetry.getInstance().setTelemetryEnabled(true);
+ Events.obtainTelemetry().enable();
dialog.cancel();
}
});
@@ -103,7 +101,7 @@ class AttributionDialogManager implements View.OnClickListener, DialogInterface.
builder.setNegativeButton(R.string.mapbox_attributionTelemetryNegative, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- MapboxTelemetry.getInstance().setTelemetryEnabled(false);
+ Events.obtainTelemetry().disable();
dialog.cancel();
}
});
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Events.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Events.java
new file mode 100644
index 0000000000..a68d4763ac
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Events.java
@@ -0,0 +1,36 @@
+package com.mapbox.mapboxsdk.maps;
+
+
+import com.mapbox.android.telemetry.MapboxTelemetry;
+import com.mapbox.android.telemetry.TelemetryEnabler;
+import com.mapbox.mapboxsdk.BuildConfig;
+import com.mapbox.mapboxsdk.Mapbox;
+
+class Events {
+ static final String TWO_FINGER_TAP = "TwoFingerTap";
+ static final String DOUBLE_TAP = "DoubleTap";
+ static final String SINGLE_TAP = "SingleTap";
+ static final String PAN = "Pan";
+ static final String PINCH = "Pinch";
+ static final String ROTATION = "Rotation";
+ static final String PITCH = "Pitch";
+ private MapboxTelemetry telemetry;
+
+ private Events() {
+ telemetry = new MapboxTelemetry(Mapbox.getApplicationContext(), Mapbox.getAccessToken(),
+ BuildConfig.MAPBOX_EVENTS_USER_AGENT);
+ TelemetryEnabler.State telemetryState = TelemetryEnabler.retrieveTelemetryStateFromPreferences();
+ if (TelemetryEnabler.State.NOT_INITIALIZED.equals(telemetryState)
+ || TelemetryEnabler.State.ENABLED.equals(telemetryState)) {
+ telemetry.enable();
+ }
+ }
+
+ private static class EventsHolder {
+ private static final Events INSTANCE = new Events();
+ }
+
+ static MapboxTelemetry obtainTelemetry() {
+ return EventsHolder.INSTANCE.telemetry;
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java
index 1788cb4428..8047e19809 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java
@@ -5,7 +5,6 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.PointF;
-import android.location.Location;
import android.support.annotation.Nullable;
import android.support.v4.view.GestureDetectorCompat;
import android.support.v4.view.ScaleGestureDetectorCompat;
@@ -19,12 +18,12 @@ import android.view.ViewConfiguration;
import com.almeros.android.multitouch.gesturedetectors.RotateGestureDetector;
import com.almeros.android.multitouch.gesturedetectors.ShoveGestureDetector;
import com.almeros.android.multitouch.gesturedetectors.TwoFingerGestureDetector;
+import com.mapbox.android.telemetry.Event;
+import com.mapbox.android.telemetry.MapEventFactory;
+import com.mapbox.android.telemetry.MapState;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.services.android.telemetry.MapboxEvent;
-import com.mapbox.services.android.telemetry.MapboxTelemetry;
-import com.mapbox.services.android.telemetry.utils.MathUtils;
-import com.mapbox.services.android.telemetry.utils.TelemetryUtils;
+import com.mapbox.mapboxsdk.utils.MathUtils;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -145,19 +144,6 @@ final class MapGestureDetector {
}
/**
- * Given coordinates from a gesture, use the current projection to translate it into
- * a Location object.
- *
- * @param x coordinate
- * @param y coordinate
- * @return location
- */
- private Location getLocationFromGesture(float x, float y) {
- LatLng latLng = projection.fromScreenLocation(new PointF(x, y));
- return TelemetryUtils.buildLocation(latLng.getLongitude(), latLng.getLatitude());
- }
-
- /**
* Called when user touches the screen, all positions are absolute.
* <p>
* Forwards event to the related gesture detectors.
@@ -202,9 +188,13 @@ final class MapGestureDetector {
&& uiSettings.isZoomGesturesEnabled();
if (twoTap) {
// Confirmed 2nd Finger Down
- MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent(
- getLocationFromGesture(event.getX(), event.getY()),
- MapboxEvent.GESTURE_TWO_FINGER_SINGLETAP, transform));
+ if (isZoomValid(transform)) {
+ MapEventFactory mapEventFactory = new MapEventFactory();
+ LatLng latLng = projection.fromScreenLocation(new PointF(event.getX(), event.getY()));
+ MapState twoFingerTap = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom());
+ twoFingerTap.setGesture(Events.TWO_FINGER_TAP);
+ Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, twoFingerTap));
+ }
}
break;
@@ -233,8 +223,12 @@ final class MapGestureDetector {
// Scroll / Pan Has Stopped
if (scrollGestureOccurred) {
- MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapDragEndEvent(
- getLocationFromGesture(event.getX(), event.getY()), transform));
+ if (isZoomValid(transform)) {
+ MapEventFactory mapEventFactory = new MapEventFactory();
+ LatLng latLng = projection.fromScreenLocation(new PointF(event.getX(), event.getY()));
+ MapState dragend = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom());
+ Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_DRAGEND, dragend));
+ }
scrollGestureOccurred = false;
if (!scaleAnimating && !rotateAnimating) {
@@ -350,9 +344,13 @@ final class MapGestureDetector {
// Zoom in on gesture
transform.zoom(true, new PointF(e.getX(), e.getY()));
}
- MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent(
- getLocationFromGesture(e.getX(), e.getY()),
- MapboxEvent.GESTURE_DOUBLETAP, transform));
+ if (isZoomValid(transform)) {
+ MapEventFactory mapEventFactory = new MapEventFactory();
+ LatLng latLng = projection.fromScreenLocation(new PointF(e.getX(), e.getY()));
+ MapState doubleTap = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom());
+ doubleTap.setGesture(Events.DOUBLE_TAP);
+ Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, doubleTap));
+ }
break;
}
@@ -380,9 +378,13 @@ final class MapGestureDetector {
notifyOnMapClickListeners(tapPoint);
}
- MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent(
- getLocationFromGesture(motionEvent.getX(), motionEvent.getY()),
- MapboxEvent.GESTURE_SINGLETAP, transform));
+ if (isZoomValid(transform)) {
+ MapEventFactory mapEventFactory = new MapEventFactory();
+ LatLng latLng = projection.fromScreenLocation(new PointF(motionEvent.getX(), motionEvent.getY()));
+ MapState singleTap = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom());
+ singleTap.setGesture(Events.SINGLE_TAP);
+ Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, singleTap));
+ }
return true;
}
@@ -456,9 +458,13 @@ final class MapGestureDetector {
cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
}
- MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent(
- getLocationFromGesture(e1.getX(), e1.getY()),
- MapboxEvent.GESTURE_PAN_START, transform));
+ if (isZoomValid(transform)) {
+ MapEventFactory mapEventFactory = new MapEventFactory();
+ LatLng latLng = projection.fromScreenLocation(new PointF(e1.getX(), e1.getY()));
+ MapState pan = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom());
+ pan.setGesture(Events.PAN);
+ Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, pan));
+ }
}
// reset tracking if needed
@@ -541,9 +547,13 @@ final class MapGestureDetector {
recentScaleGestureOccurred = true;
scalePointBegin = new PointF(detector.getFocusX(), detector.getFocusY());
scaleBeginTime = detector.getEventTime();
- MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent(
- getLocationFromGesture(detector.getFocusX(), detector.getFocusY()),
- MapboxEvent.GESTURE_PINCH_START, transform));
+ if (isZoomValid(transform)) {
+ MapEventFactory mapEventFactory = new MapEventFactory();
+ LatLng latLng = projection.fromScreenLocation(new PointF(detector.getFocusX(), detector.getFocusY()));
+ MapState pinch = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom());
+ pinch.setGesture(Events.PINCH);
+ Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, pinch));
+ }
return true;
}
@@ -724,9 +734,13 @@ final class MapGestureDetector {
// Also is zoom already started, don't rotate
float angle = detector.getRotationDegreesDelta();
if (Math.abs(angle) >= ROTATE_INVOKE_ANGLE) {
- MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent(
- getLocationFromGesture(detector.getFocusX(), detector.getFocusY()),
- MapboxEvent.GESTURE_ROTATION_START, transform));
+ if (isZoomValid(transform)) {
+ MapEventFactory mapEventFactory = new MapEventFactory();
+ LatLng latLng = projection.fromScreenLocation(new PointF(detector.getFocusX(), detector.getFocusY()));
+ MapState rotation = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom());
+ rotation.setGesture(Events.ROTATION);
+ Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, rotation));
+ }
started = true;
}
@@ -895,9 +909,13 @@ final class MapGestureDetector {
if (!tiltGestureOccurred && ((totalDelta > 10.0f) || (totalDelta < -10.0f))) {
tiltGestureOccurred = true;
beginTime = detector.getEventTime();
- MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent(
- getLocationFromGesture(detector.getFocusX(), detector.getFocusY()),
- MapboxEvent.GESTURE_PITCH_START, transform));
+ if (isZoomValid(transform)) {
+ MapEventFactory mapEventFactory = new MapEventFactory();
+ LatLng latLng = projection.fromScreenLocation(new PointF(detector.getFocusX(), detector.getFocusY()));
+ MapState pitch = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom());
+ pitch.setGesture(Events.PITCH);
+ Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, pitch));
+ }
}
if (!tiltGestureOccurred) {
@@ -962,4 +980,15 @@ final class MapGestureDetector {
void removeOnScrollListener(MapboxMap.OnScrollListener onScrollListener) {
onScrollListenerList.remove(onScrollListener);
}
+
+ private boolean isZoomValid(Transform transform) {
+ if (transform == null) {
+ return false;
+ }
+ double mapZoom = transform.getZoom();
+ if (mapZoom < MapboxConstants.MINIMUM_ZOOM || mapZoom > MapboxConstants.MAXIMUM_ZOOM) {
+ return false;
+ }
+ return true;
+ }
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java
index a8e065c45e..990c56cb51 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java
@@ -22,6 +22,12 @@ import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ZoomButtonsController;
+
+import com.mapbox.android.telemetry.AppUserTurnstile;
+import com.mapbox.android.telemetry.Event;
+import com.mapbox.android.telemetry.MapEventFactory;
+import com.mapbox.android.telemetry.MapboxTelemetry;
+import com.mapbox.mapboxsdk.BuildConfig;
import com.mapbox.mapboxsdk.R;
import com.mapbox.mapboxsdk.annotations.Annotation;
import com.mapbox.mapboxsdk.annotations.MarkerViewManager;
@@ -35,8 +41,6 @@ import com.mapbox.mapboxsdk.maps.widgets.MyLocationView;
import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings;
import com.mapbox.mapboxsdk.net.ConnectivityReceiver;
import com.mapbox.mapboxsdk.storage.FileSource;
-import com.mapbox.services.android.telemetry.MapboxTelemetry;
-import timber.log.Timber;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
@@ -48,6 +52,8 @@ import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
+import timber.log.Timber;
+
import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_MAP_NORTH_ANIMATION;
import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_WAIT_IDLE;
@@ -192,7 +198,7 @@ public class MapView extends FrameLayout {
compassView.setOnClickListener(createCompassClickListener(cameraChangeDispatcher));
// inject widgets with MapboxMap
myLocationView.setMapboxMap(mapboxMap);
- attrView.setOnClickListener(new AttributionDialogManager(context, mapboxMap));
+ attrView.setOnClickListener(new AttributionClickListener(context, mapboxMap));
// Ensure this view is interactable
setClickable(true);
@@ -262,7 +268,7 @@ public class MapView extends FrameLayout {
/**
* <p>
* You must call this method from the parent's Activity#onCreate(Bundle)} or
- * Fragment#onCreate(Bundle).
+ * Fragment#onViewCreated(View, Bundle).
* </p>
* You must set a valid access token with {@link com.mapbox.mapboxsdk.Mapbox#getInstance(Context, String)}
* before you call this method or an exception will be thrown.
@@ -273,7 +279,12 @@ public class MapView extends FrameLayout {
@UiThread
public void onCreate(@Nullable Bundle savedInstanceState) {
if (savedInstanceState == null) {
- MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapLoadEvent());
+ MapboxTelemetry telemetry = Events.obtainTelemetry();
+ AppUserTurnstile turnstileEvent = new AppUserTurnstile(BuildConfig.MAPBOX_SDK_IDENTIFIER,
+ BuildConfig.MAPBOX_SDK_VERSION);
+ telemetry.push(turnstileEvent);
+ MapEventFactory mapEventFactory = new MapEventFactory();
+ telemetry.push(mapEventFactory.createMapLoadEvent(Event.Type.MAP_LOAD));
} else if (savedInstanceState.getBoolean(MapboxConstants.STATE_HAS_SAVED_STATE)) {
this.savedInstanceState = savedInstanceState;
}
@@ -285,7 +296,7 @@ public class MapView extends FrameLayout {
mapRenderer = new TextureViewMapRenderer(getContext(), textureView, options.getLocalIdeographFontFamily()) {
@Override
protected void onSurfaceCreated(GL10 gl, EGLConfig config) {
- initRenderSurface();
+ MapView.this.onSurfaceCreated();
super.onSurfaceCreated(gl, config);
}
};
@@ -297,7 +308,7 @@ public class MapView extends FrameLayout {
mapRenderer = new GLSurfaceViewMapRenderer(getContext(), glSurfaceView, options.getLocalIdeographFontFamily()) {
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
- initRenderSurface();
+ MapView.this.onSurfaceCreated();
super.onSurfaceCreated(gl, config);
}
};
@@ -309,16 +320,13 @@ public class MapView extends FrameLayout {
nativeMapView.resizeView(getMeasuredWidth(), getMeasuredHeight());
}
- private void initRenderSurface() {
+ private void onSurfaceCreated() {
hasSurface = true;
- post(new Runnable() {
- @Override
- public void run() {
- // Initialise only when not destroyed and only once
- if (!destroyed && mapboxMap == null) {
- initialiseMap();
- mapboxMap.onStart();
- }
+ post(() -> {
+ // Initialise only when not destroyed and only once
+ if (!destroyed && mapboxMap == null) {
+ initialiseMap();
+ mapboxMap.onStart();
}
});
}
@@ -1089,4 +1097,28 @@ public class MapView extends FrameLayout {
onMapReadyCallbackList.clear();
}
}
+
+ /**
+ * Click event hook for providing a custom attribution dialog manager.
+ */
+ private static class AttributionClickListener implements OnClickListener {
+
+ private final AttributionDialogManager defaultDialogManager;
+ private UiSettings uiSettings;
+
+ private AttributionClickListener(Context context, MapboxMap mapboxMap) {
+ this.defaultDialogManager = new AttributionDialogManager(context, mapboxMap);
+ this.uiSettings = mapboxMap.getUiSettings();
+ }
+
+ @Override
+ public void onClick(View v) {
+ AttributionDialogManager customDialogManager = uiSettings.getAttributionDialogManager();
+ if (customDialogManager != null) {
+ uiSettings.getAttributionDialogManager().onClick(v);
+ } else {
+ defaultDialogManager.onClick(v);
+ }
+ }
+ }
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxEventWrapper.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxEventWrapper.java
deleted file mode 100644
index 6730278d79..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxEventWrapper.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package com.mapbox.mapboxsdk.maps;
-
-import android.location.Location;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
-import com.mapbox.mapboxsdk.constants.MapboxConstants;
-import com.mapbox.services.android.telemetry.MapboxEvent;
-
-import java.util.Hashtable;
-
-/**
- * Wrapper class for MapboxEvent
- * <p>
- * Provides facility methods to use Transform and handle the case that the zoom, required for a telemetry event,
- * isn't available yet.
- * </p>
- */
-class MapboxEventWrapper {
-
- @Nullable
- static Hashtable<String, Object> buildMapClickEvent(
- @NonNull Location location, @NonNull String gestureId, Transform transform) {
- try {
- double mapZoom = transform.getZoom();
- if (mapZoom >= MapboxConstants.MINIMUM_ZOOM && mapZoom <= MapboxConstants.MAXIMUM_ZOOM) {
- // validate zoom #8057
- return MapboxEvent.buildMapClickEvent(location, gestureId, transform.getZoom());
- }
- } catch (NullPointerException exception) {
- // Map/Transform is not ready yet #8650
- // returning null is valid, event is ignored.
- }
- return null;
- }
-
- @Nullable
- static Hashtable<String, Object> buildMapDragEndEvent(
- @NonNull Location location, Transform transform) {
- try {
- double mapZoom = transform.getZoom();
- if (mapZoom >= MapboxConstants.MINIMUM_ZOOM && mapZoom <= MapboxConstants.MAXIMUM_ZOOM) {
- // validate zoom #8057
- return MapboxEvent.buildMapDragEndEvent(location, transform.getZoom());
- }
- } catch (NullPointerException exception) {
- // Map/Transform is not ready yet #8650
- // returning null is valid, event is ignored.
- }
- return null;
- }
-
- @Nullable
- static Hashtable<String, Object> buildMapLoadEvent() {
- return MapboxEvent.buildMapLoadEvent();
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java
index ba116e1278..2fd9a9010c 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java
@@ -16,6 +16,8 @@ import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
+import com.mapbox.geojson.Feature;
+import com.mapbox.geojson.Geometry;
import com.mapbox.mapboxsdk.annotations.Annotation;
import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions;
import com.mapbox.mapboxsdk.annotations.BaseMarkerViewOptions;
@@ -41,9 +43,7 @@ import com.mapbox.mapboxsdk.style.layers.Filter;
import com.mapbox.mapboxsdk.style.layers.Layer;
import com.mapbox.mapboxsdk.style.light.Light;
import com.mapbox.mapboxsdk.style.sources.Source;
-import com.mapbox.services.android.telemetry.location.LocationEngine;
-import com.mapbox.services.commons.geojson.Feature;
-import com.mapbox.services.commons.geojson.Geometry;
+import com.mapbox.android.core.location.LocationEngine;
import java.lang.reflect.ParameterizedType;
import java.util.HashMap;
@@ -563,7 +563,7 @@ public final class MapboxMap {
*
* @return the TrackingSettings asssociated with this map
* @deprecated use location layer plugin from
- * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead.
+ * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugin-locationlayer instead.
*/
@Deprecated
public TrackingSettings getTrackingSettings() {
@@ -579,7 +579,7 @@ public final class MapboxMap {
*
* @return the MyLocationViewSettings associated with this map
* @deprecated use location layer plugin from
- * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead.
+ * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugin-locationlayer instead.
*/
@Deprecated
public MyLocationViewSettings getMyLocationViewSettings() {
@@ -2077,7 +2077,7 @@ public final class MapboxMap {
*
* @return True if the my-location layer is enabled, false otherwise.
* @deprecated use location layer plugin from
- * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead.
+ * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugin-locationlayer instead.
*/
@Deprecated
public boolean isMyLocationEnabled() {
@@ -2095,7 +2095,7 @@ public final class MapboxMap {
*
* @param enabled True to enable; false to disable.
* @deprecated use location layer plugin from
- * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead.
+ * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugin-locationlayer instead.
*/
@Deprecated
public void setMyLocationEnabled(boolean enabled) {
@@ -2107,7 +2107,7 @@ public final class MapboxMap {
*
* @return The currently displayed user location.
* @deprecated use location layer plugin from
- * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead.
+ * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugin-locationlayer instead.
*/
@Nullable
@Deprecated
@@ -2122,7 +2122,7 @@ public final class MapboxMap {
* @param listener The callback that's invoked when the user clicks on a marker.
* To unset the callback, use null.
* @deprecated use location layer plugin from
- * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead.
+ * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugin-locationlayer instead.
*/
@Deprecated
public void setOnMyLocationChangeListener(@Nullable MapboxMap.OnMyLocationChangeListener
@@ -2135,7 +2135,7 @@ public final class MapboxMap {
*
* @param locationSource A {@link LocationEngine} location source to use in the my-location layer.
* @deprecated use location layer plugin from
- * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead.
+ * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugin-locationlayer instead.
*/
@Deprecated
public void setLocationSource(@Nullable LocationEngine locationSource) {
@@ -2148,7 +2148,7 @@ public final class MapboxMap {
* @param listener The callback that's invoked when the location tracking mode changes.
* To unset the callback, use null.
* @deprecated use location layer plugin from
- * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead.
+ * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugin-locationlayer instead.
*/
@Deprecated
public void setOnMyLocationTrackingModeChangeListener(
@@ -2162,7 +2162,7 @@ public final class MapboxMap {
* @param listener The callback that's invoked when the bearing tracking mode changes.
* To unset the callback, use null.
* @deprecated use location layer plugin from
- * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead.
+ * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugin-locationlayer instead.
*/
@Deprecated
public void setOnMyBearingTrackingModeChangeListener(@Nullable OnMyBearingTrackingModeChangeListener listener) {
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 e8eb7e8718..785b045779 100755
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java
@@ -11,6 +11,8 @@ import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.DisplayMetrics;
+import com.mapbox.geojson.Feature;
+import com.mapbox.geojson.Geometry;
import com.mapbox.mapboxsdk.LibraryLoader;
import com.mapbox.mapboxsdk.annotations.Icon;
import com.mapbox.mapboxsdk.annotations.Marker;
@@ -29,8 +31,6 @@ import com.mapbox.mapboxsdk.style.light.Light;
import com.mapbox.mapboxsdk.style.sources.CannotAddSourceException;
import com.mapbox.mapboxsdk.style.sources.Source;
import com.mapbox.mapboxsdk.utils.BitmapUtils;
-import com.mapbox.services.commons.geojson.Feature;
-import com.mapbox.services.commons.geojson.Geometry;
import java.nio.ByteBuffer;
import java.util.ArrayList;
@@ -733,7 +733,7 @@ final class NativeMapView {
if (isDestroyedOn("addSource")) {
return;
}
- nativeAddSource(source.getNativePtr());
+ nativeAddSource(source, source.getNativePtr());
}
@Nullable
@@ -741,14 +741,15 @@ final class NativeMapView {
if (isDestroyedOn("removeSource")) {
return null;
}
- return nativeRemoveSourceById(sourceId);
+ Source source = getSource(sourceId);
+ return removeSource(source);
}
public Source removeSource(@NonNull Source source) {
if (isDestroyedOn("removeSource")) {
return null;
}
- nativeRemoveSource(source.getNativePtr());
+ nativeRemoveSource(source, source.getNativePtr());
return source;
}
@@ -1027,11 +1028,9 @@ final class NativeMapView {
private native Source nativeGetSource(String sourceId);
- private native void nativeAddSource(long nativeSourcePtr) throws CannotAddSourceException;
+ private native void nativeAddSource(Source source, long sourcePtr) throws CannotAddSourceException;
- private native Source nativeRemoveSourceById(String sourceId);
-
- private native void nativeRemoveSource(long sourcePtr);
+ private native void nativeRemoveSource(Source source, long sourcePtr);
private native void nativeAddImage(String name, int width, int height, float pixelRatio,
byte[] array);
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java
index 16c73b1ca5..ae559189ad 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java
@@ -104,11 +104,12 @@ public class Projection {
LatLng bottomLeft = fromScreenLocation(new PointF(left, bottom));
return new VisibleRegion(topLeft, topRight, bottomLeft, bottomRight,
- LatLngBounds.from(
- topRight.getLatitude(),
- topRight.getLongitude(),
- bottomLeft.getLatitude(),
- bottomLeft.getLongitude())
+ new LatLngBounds.Builder()
+ .include(topRight)
+ .include(bottomLeft)
+ .include(bottomRight)
+ .include(topLeft)
+ .build()
);
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java
index 81fd091c90..3743096824 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java
@@ -12,9 +12,9 @@ import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.constants.MyBearingTracking;
import com.mapbox.mapboxsdk.constants.MyLocationTracking;
import com.mapbox.mapboxsdk.maps.widgets.MyLocationView;
-import com.mapbox.services.android.telemetry.location.LocationEngine;
-import com.mapbox.services.android.telemetry.location.LocationEngineListener;
-import com.mapbox.services.android.telemetry.permissions.PermissionsManager;
+import com.mapbox.android.core.location.LocationEngine;
+import com.mapbox.android.core.location.LocationEngineListener;
+import com.mapbox.android.core.permissions.PermissionsManager;
import timber.log.Timber;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java
index 3fd3e1220a..12d8dfe85b 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java
@@ -37,6 +37,7 @@ public final class UiSettings {
private final ImageView attributionsView;
private final int[] attributionsMargins = new int[4];
+ private AttributionDialogManager attributionDialogManager;
private final View logoView;
private final int[] logoMargins = new int[4];
@@ -535,6 +536,28 @@ public final class UiSettings {
return attributionsView.getVisibility() == View.VISIBLE;
}
+
+ /**
+ * Set a custom attribution dialog manager.
+ * <p>
+ * Set to null to reset to default behaviour.
+ * </p>
+ *
+ * @param attributionDialogManager the manager class used for showing attribution
+ */
+ public void setAttributionDialogManager(AttributionDialogManager attributionDialogManager) {
+ this.attributionDialogManager = attributionDialogManager;
+ }
+
+ /**
+ * Get the custom attribution dialog manager.
+ *
+ * @return the active manager class used for showing attribution
+ */
+ public AttributionDialogManager getAttributionDialogManager() {
+ return attributionDialogManager;
+ }
+
/**
* <p>
* Sets the gravity of the attribution.
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java
index 1e604c9bef..45f72af1c5 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java
@@ -6,10 +6,10 @@ import android.support.annotation.NonNull;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.ViewPropertyAnimatorCompat;
import android.support.v4.view.ViewPropertyAnimatorListenerAdapter;
+import android.support.v7.widget.AppCompatImageView;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.ImageView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
@@ -22,7 +22,7 @@ import com.mapbox.mapboxsdk.maps.MapboxMap;
* use {@link com.mapbox.mapboxsdk.maps.UiSettings}.
* </p>
*/
-public final class CompassView extends ImageView implements Runnable {
+public final class CompassView extends AppCompatImageView implements Runnable {
public static final long TIME_WAIT_IDLE = 500;
public static final long TIME_MAP_NORTH_ANIMATION = 150;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java
index 1cdd91028d..3f37da99d5 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java
@@ -35,9 +35,9 @@ import com.mapbox.mapboxsdk.constants.MyLocationTracking;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.Projection;
-import com.mapbox.services.android.telemetry.location.LocationEngine;
-import com.mapbox.services.android.telemetry.location.LocationEngineListener;
-import com.mapbox.services.android.telemetry.location.LocationEnginePriority;
+import com.mapbox.android.core.location.LocationEngine;
+import com.mapbox.android.core.location.LocationEngineListener;
+import com.mapbox.android.core.location.LocationEnginePriority;
import java.lang.ref.WeakReference;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java
index 987756876b..090e9576ee 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java
@@ -250,12 +250,9 @@ public class OfflineRegion {
@Override
public void onStatusChanged(final OfflineRegionStatus status) {
if (deliverMessages()) {
- handler.post(new Runnable() {
- @Override
- public void run() {
- if (observer != null) {
- observer.onStatusChanged(status);
- }
+ handler.post(() -> {
+ if (observer != null) {
+ observer.onStatusChanged(status);
}
});
}
@@ -264,12 +261,9 @@ public class OfflineRegion {
@Override
public void onError(final OfflineRegionError error) {
if (deliverMessages()) {
- handler.post(new Runnable() {
- @Override
- public void run() {
- if (observer != null) {
- observer.onError(error);
- }
+ handler.post(() -> {
+ if (observer != null) {
+ observer.onError(error);
}
});
}
@@ -278,12 +272,9 @@ public class OfflineRegion {
@Override
public void mapboxTileCountLimitExceeded(final long limit) {
if (deliverMessages()) {
- handler.post(new Runnable() {
- @Override
- public void run() {
- if (observer != null) {
- observer.mapboxTileCountLimitExceeded(limit);
- }
+ handler.post(() -> {
+ if (observer != null) {
+ observer.mapboxTileCountLimitExceeded(limit);
}
});
}
@@ -322,23 +313,17 @@ public class OfflineRegion {
getOfflineRegionStatus(new OfflineRegionStatusCallback() {
@Override
public void onStatus(final OfflineRegionStatus status) {
- handler.post(new Runnable() {
- @Override
- public void run() {
- callback.onStatus(status);
- FileSource.getInstance(Mapbox.getApplicationContext()).deactivate();
- }
+ handler.post(() -> {
+ callback.onStatus(status);
+ FileSource.getInstance(Mapbox.getApplicationContext()).deactivate();
});
}
@Override
public void onError(final String error) {
- handler.post(new Runnable() {
- @Override
- public void run() {
- callback.onError(error);
- FileSource.getInstance(Mapbox.getApplicationContext()).deactivate();
- }
+ handler.post(() -> {
+ callback.onError(error);
+ FileSource.getInstance(Mapbox.getApplicationContext()).deactivate();
});
}
});
@@ -368,25 +353,19 @@ public class OfflineRegion {
deleteOfflineRegion(new OfflineRegionDeleteCallback() {
@Override
public void onDelete() {
- handler.post(new Runnable() {
- @Override
- public void run() {
- callback.onDelete();
- FileSource.getInstance(Mapbox.getApplicationContext()).deactivate();
- OfflineRegion.this.finalize();
- }
+ handler.post((Runnable) () -> {
+ callback.onDelete();
+ FileSource.getInstance(Mapbox.getApplicationContext()).deactivate();
+ OfflineRegion.this.finalize();
});
}
@Override
public void onError(final String error) {
- handler.post(new Runnable() {
- @Override
- public void run() {
- isDeleted = false;
- FileSource.getInstance(Mapbox.getApplicationContext()).deactivate();
- callback.onError(error);
- }
+ handler.post(() -> {
+ isDeleted = false;
+ FileSource.getInstance(Mapbox.getApplicationContext()).deactivate();
+ callback.onError(error);
});
}
});
@@ -407,23 +386,15 @@ public class OfflineRegion {
updateOfflineRegionMetadata(bytes, new OfflineRegionUpdateMetadataCallback() {
@Override
public void onUpdate(final byte[] metadata) {
- handler.post(new Runnable() {
- @Override
- public void run() {
- OfflineRegion.this.metadata = metadata;
- callback.onUpdate(metadata);
- }
+ handler.post(() -> {
+ OfflineRegion.this.metadata = metadata;
+ callback.onUpdate(metadata);
});
}
@Override
public void onError(final String error) {
- handler.post(new Runnable() {
- @Override
- public void run() {
- callback.onError(error);
- }
- });
+ handler.post(() -> callback.onError(error));
}
});
}
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
new file mode 100644
index 0000000000..7b841a2580
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java
@@ -0,0 +1,1797 @@
+package com.mapbox.mapboxsdk.style.expressions;
+
+import android.support.annotation.ColorInt;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.Size;
+
+import com.mapbox.mapboxsdk.style.layers.PropertyFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The value for any layout property, paint property, or filter may be specified as an expression.
+ * An expression defines a formula for computing the value of the property using the operators described below.
+ * The set of expression operators provided by Mapbox GL includes:
+ * <p>
+ * <ul>
+ * <li>Element</li>
+ * <li>Mathematical operators for performing arithmetic and other operations on numeric values</li>
+ * <li>Logical operators for manipulating boolean values and making conditional decisions</li>
+ * <li>String operators for manipulating strings</li>
+ * <li>Data operators, providing access to the properties of source features</li>
+ * <li>Camera operators, providing access to the parameters defining the current map view</li>
+ * </ul>
+ * </p>
+ * <p>
+ * Expressions are represented as JSON arrays.
+ * The first element of an expression array is a string naming the expression operator,
+ * e.g. "*"or "case". Subsequent elements (if any) are the arguments to the expression.
+ * Each argument is either a literal value (a string, number, boolean, or null), or another expression array.
+ * </p>
+ * <p>
+ * Data expression: a data expression is any expression that access feature data -- that is,
+ * any expression that uses one of the data operators:get,has,id,geometry-type, or properties.
+ * Data expressions allow a feature's properties to determine its appearance.
+ * They can be used to differentiate features within the same layer and to create data visualizations.
+ * </p>
+ * <p>
+ * Camera expression: a camera expression is any expression that uses the zoom operator.
+ * Such expressions allow the the appearance of a layer to change with the map's zoom level.
+ * Camera expressions can be used to create the appearance of depth and to control data density.
+ * </p>
+ * <p>
+ * Composition: a single expression may use a mix of data operators, camera operators, and other operators.
+ * Such composite expressions allows a layer's appearance to be determined by
+ * a combination of the zoom level and individual feature properties.
+ * </p>
+ *
+ * @param <T> the type of the expression
+ */
+public class Expression<T> {
+
+ private final String operator;
+ private final Expression[] arguments;
+
+ /**
+ * Creates an empty expression for expression literals
+ */
+ Expression() {
+ operator = null;
+ arguments = null;
+ }
+
+ /**
+ * Creates an expression from its operator and varargs expressions.
+ *
+ * @param operator the expression operator
+ * @param arguments expressions input
+ */
+ @SafeVarargs
+ public Expression(@NonNull String operator, @Nullable Expression... arguments) {
+ this.operator = operator;
+ this.arguments = arguments;
+ }
+
+ /**
+ * Converts the expression to Object array representation.
+ * <p>
+ * The output will later be converted to a JSON Object array.
+ * </p>
+ *
+ * @return the converted object array expression
+ */
+ @NonNull
+ public Object[] toArray() {
+ List<Object> array = new ArrayList<>();
+ array.add(operator);
+ if (arguments != null) {
+ for (Expression argument : arguments) {
+ if (argument instanceof Expression.ExpressionLiteral) {
+ array.add(toValue((ExpressionLiteral) argument));
+ } else {
+ array.add(argument.toArray());
+ }
+ }
+ }
+ return array.toArray();
+ }
+
+ /**
+ * Converts the expression value to an Object.
+ *
+ * @param expressionValue the expression value to convert
+ * @return the converted object 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) {
+ return toValue((ExpressionLiteral) value);
+ } else if (value instanceof Expression) {
+ return ((Expression) value).toArray();
+ }
+ return value;
+ }
+
+ /**
+ * ExpressionLiteral wraps an object to be used as a literal in an expression.
+ * <p>
+ * ExpressionLiteral is created with {@link #literal(Number)}, {@link #literal(boolean)},
+ * {@link #literal(String)} and {@link #literal(Object)}.
+ * </p>
+ *
+ * @param <T>
+ */
+ private static class ExpressionLiteral<T> extends Expression<T> {
+
+ protected T object;
+
+ /**
+ * Create an ExpressionValue wrapper.
+ *
+ * @param object the object to be wrapped
+ */
+ ExpressionLiteral(@NonNull T object) {
+ this.object = object;
+ }
+
+ /**
+ * Get the wrapped object.
+ *
+ * @return the wrapped object
+ */
+ Object toValue() {
+ return object;
+ }
+ }
+
+ //
+ // Types
+ //
+
+ /**
+ * Expression interpolator type.
+ * <p>
+ * Is used for first parameter of {@link #interpolate(Expression, Expression, Stop...)}.
+ * </p>
+ */
+ public static class Interpolator {
+ }
+
+ /**
+ * 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
+ */
+ public String convertColor() {
+ return PropertyFactory.colorToRgbaString(color);
+ }
+ }
+
+ /**
+ * Expression array type.
+ */
+ public static class Array {
+ }
+
+ /**
+ * Expression stop type.
+ * <p>
+ * Can be used for {@link #stop(Object, Object)} as part of varargs parameter in
+ * {@link #step(Number, Expression, Stop...)} or {@link #interpolate(Expression, Expression, Stop...)}.
+ * </p>
+ */
+ public static class Stop {
+
+ private Object value;
+ private Object output;
+
+ public Stop(Object value, Object output) {
+ this.value = value;
+ this.output = output;
+ }
+ }
+
+ //
+ // Literals
+ //
+
+ /**
+ * Create a literal number expression.
+ *
+ * @param number the number
+ * @return the expression
+ */
+ public static Expression<Number> literal(@NonNull Number number) {
+ return new ExpressionLiteral<>(number);
+ }
+
+ /**
+ * Create a literal string expression.
+ *
+ * @param string the string
+ * @return the expression
+ */
+ public static Expression<String> literal(@NonNull String string) {
+ return new ExpressionLiteral<>(string);
+ }
+
+ /**
+ * Create a literal boolean expression.
+ *
+ * @param bool the boolean
+ * @return the expression
+ */
+ public static Expression<Boolean> literal(boolean bool) {
+ return new ExpressionLiteral<>(bool);
+ }
+
+ /**
+ * Create a literal object expression
+ *
+ * @param object the object
+ * @return the expression
+ */
+ public static Expression<Object> literal(@NonNull Object object) {
+ return new ExpressionLiteral<>(object);
+ }
+
+ //
+ // Color
+ //
+
+ /**
+ * Expression literal utility method to convert a color int to an color expression
+ *
+ * @param color the int color
+ * @return the color expression
+ */
+ public static Expression<Color> color(@ColorInt int color) {
+ return new ExpressionLiteral<>(new Color(color));
+ }
+
+ /**
+ * Creates a color value from red, green, and blue components, which must range between 0 and 255,
+ * and an alpha component of 1.
+ * <p>
+ * If any component is out of range, the expression is an error.
+ * </p>
+ *
+ * @param red red color expression
+ * @param green green color expression
+ * @param blue blue color expression
+ * @return expression
+ */
+ public static Expression<Color> rgb(@NonNull Expression<Number> red, @NonNull Expression<Number> green,
+ @NonNull Expression<Number> blue) {
+ return new Expression<>("rgb", red, green, blue);
+ }
+
+ /**
+ * Creates a color value from red, green, and blue components, which must range between 0 and 255,
+ * and an alpha component of 1.
+ * <p>
+ * If any component is out of range, the expression is an error.
+ * </p>
+ *
+ * @param red red color value
+ * @param green green color value
+ * @param blue blue color value
+ * @return expression
+ */
+ public static Expression<Color> rgb(@NonNull Number red, @NonNull Number green, @NonNull Number blue) {
+ return rgb(literal(red), literal(green), literal(blue));
+ }
+
+ /**
+ * Creates a color value from red, green, blue components, which must range between 0 and 255,
+ * and an alpha component which must range between 0 and 1.
+ * <p>
+ * If any component is out of range, the expression is an error.
+ * </p>
+ *
+ * @param red red color value
+ * @param green green color value
+ * @param blue blue color value
+ * @param alpha alpha color value
+ * @return expression
+ */
+ public static Expression<Color> rgba(@NonNull Expression<Number> red, @NonNull Expression<Number> green,
+ @NonNull Expression<Number> blue, @NonNull Expression<Number> alpha) {
+ return new Expression<>("rgba", red, green, blue, alpha);
+ }
+
+ /**
+ * Creates a color value from red, green, blue components, which must range between 0 and 255,
+ * and an alpha component which must range between 0 and 1.
+ * <p>
+ * If any component is out of range, the expression is an error.
+ * </p>
+ *
+ * @param red red color value
+ * @param green green color value
+ * @param blue blue color value
+ * @param alpha alpha color value
+ * @return expression
+ */
+ public static Expression<Color> rgba(@NonNull Number red, @NonNull Number green, @NonNull Number blue, @NonNull Number alpha) {
+ return rgba(literal(red), literal(green), literal(blue), literal(alpha));
+ }
+
+ /**
+ * Returns a four-element array containing the input color's red, green, blue, and alpha components, in that order.
+ *
+ * @param expression an expression to convert to a color
+ * @return expression
+ */
+ public static Expression<Array> toRgba(@NonNull Expression<Color> expression) {
+ return new Expression<>("to-rgba", expression);
+ }
+
+ //
+ // Decision
+ //
+
+ /**
+ * Returns true if the input values are equal, false otherwise.
+ * The inputs must be numbers, strings, or booleans, and both of the same type.
+ *
+ * @param compareOne the first expression
+ * @param compareTwo the second expression
+ * @return expression
+ */
+ public static Expression<Boolean> eq(@NonNull Expression compareOne, @NonNull Expression compareTwo) {
+ return new Expression<>("==", compareOne, compareTwo);
+ }
+
+ /**
+ * Returns true if the input values are equal, false otherwise.
+ *
+ * @param compareOne the first boolean
+ * @param compareTwo the second boolean
+ * @return expression
+ */
+ public static Expression<Boolean> eq(boolean compareOne, boolean compareTwo) {
+ return eq(literal(compareOne), literal(compareTwo));
+ }
+
+ /**
+ * Returns true if the input values are equal, false otherwise.
+ *
+ * @param compareOne the first number
+ * @param compareTwo the second number
+ * @return expression
+ */
+ public static Expression<Boolean> eq(@NonNull String compareOne, @NonNull String compareTwo) {
+ return eq(literal(compareOne), literal(compareTwo));
+ }
+
+ /**
+ * Returns true if the input values are equal, false otherwise.
+ *
+ * @param compareOne the first number
+ * @param compareTwo the second number
+ * @return expression
+ */
+ public static Expression<Boolean> eq(@NonNull Number compareOne, @NonNull Number compareTwo) {
+ return eq(literal(compareOne), literal(compareTwo));
+ }
+
+ /**
+ * Returns true if the input values are not equal, false otherwise.
+ * The inputs must be numbers, strings, or booleans, and both of the same type.
+ *
+ * @param compareOne the first expression
+ * @param compareTwo the second expression
+ * @return expression
+ */
+ public static Expression<Boolean> neq(@NonNull Expression compareOne, @NonNull Expression compareTwo) {
+ return new Expression<>("!=", compareOne, compareTwo);
+ }
+
+ /**
+ * Returns true if the input values are equal, false otherwise.
+ *
+ * @param compareOne the first boolean
+ * @param compareTwo the second boolean
+ * @return expression
+ */
+ public static Expression<Boolean> neq(boolean compareOne, boolean compareTwo) {
+ return new Expression<>("!=", literal(compareOne), literal(compareTwo));
+ }
+
+ /**
+ * Returns `true` if the input values are not equal, `false` otherwise.
+ *
+ * @param compareOne the first string
+ * @param compareTwo the second string
+ * @return expression
+ */
+ public static Expression<Boolean> neq(@NonNull String compareOne, @NonNull String compareTwo) {
+ return new Expression<>("!=", literal(compareOne), literal(compareTwo));
+ }
+
+ /**
+ * Returns `true` if the input values are not equal, `false` otherwise.
+ *
+ * @param compareOne the first number
+ * @param compareTwo the second number
+ * @return expression
+ */
+ public static Expression<Boolean> neq(@NonNull Number compareOne, @NonNull Number compareTwo) {
+ return new Expression<>("!=", literal(compareOne), literal(compareTwo));
+ }
+
+ /**
+ * Returns true if the first input is strictly greater than the second, false otherwise.
+ * The inputs must be numbers or strings, and both of the same type.
+ *
+ * @param compareOne the first expression
+ * @param compareTwo the second expression
+ * @return expression
+ */
+ public static Expression<Boolean> gt(@NonNull Expression compareOne, @NonNull Expression compareTwo) {
+ return new Expression<>(">", compareOne, compareTwo);
+ }
+
+ /**
+ * Returns true if the first input is strictly greater than the second, false otherwise.
+ *
+ * @param compareOne the first number
+ * @param compareTwo the second number
+ * @return expression
+ */
+ public static Expression<Boolean> gt(@NonNull Number compareOne, @NonNull Number compareTwo) {
+ return new Expression<>(">", literal(compareOne), literal(compareTwo));
+ }
+
+ /**
+ * Returns true if the first input is strictly greater than the second, false otherwise.
+ *
+ * @param compareOne the first string
+ * @param compareTwo the second string
+ * @return expression
+ */
+ public static Expression<Boolean> gt(@NonNull String compareOne, @NonNull String compareTwo) {
+ return new Expression<>(">", literal(compareOne), literal(compareTwo));
+ }
+
+ /**
+ * Returns true if the first input is strictly less than the second, false otherwise.
+ * The inputs must be numbers or strings, and both of the same type.
+ *
+ * @param compareOne the first number
+ * @param compareTwo the second number
+ * @return expression
+ */
+ public static Expression<Boolean> lt(@NonNull Expression compareOne, @NonNull Expression compareTwo) {
+ return new Expression<>("<", compareOne, compareTwo);
+ }
+
+ /**
+ * Returns true if the first input is strictly less than the second, false otherwise.
+ *
+ * @param compareOne the first number
+ * @param compareTwo the second number
+ * @return expression
+ */
+ public static Expression<Boolean> lt(@NonNull Number compareOne, @NonNull Number compareTwo) {
+ return new Expression<>("<", literal(compareOne), literal(compareTwo));
+ }
+
+ /**
+ * Returns true if the first input is strictly less than the second, false otherwise.
+ *
+ * @param compareOne the first string
+ * @param compareTwo the second string
+ * @return expression
+ */
+ public static Expression<Boolean> lt(@NonNull String compareOne, @NonNull String compareTwo) {
+ return new Expression<>("<", literal(compareOne), literal(compareTwo));
+ }
+
+ /**
+ * Returns true if the first input is greater than or equal to the second, false otherwise.
+ * The inputs must be numbers or strings, and both of the same type.
+ *
+ * @param compareOne the first expression
+ * @param compareTwo the second expression
+ * @return expression
+ */
+ public static Expression<Boolean> gte(@NonNull Expression compareOne, @NonNull Expression compareTwo) {
+ return new Expression<>(">=", compareOne, compareTwo);
+ }
+
+ /**
+ * Returns true if the first input is greater than or equal to the second, false otherwise.
+ *
+ * @param compareOne the first number
+ * @param compareTwo the second number
+ * @return expression
+ */
+ public static Expression<Boolean> gte(@NonNull Number compareOne, @NonNull Number compareTwo) {
+ return new Expression<>(">=", literal(compareOne), literal(compareTwo));
+ }
+
+ /**
+ * Returns true if the first input is greater than or equal to the second, false otherwise.
+ *
+ * @param compareOne the first string
+ * @param compareTwo the second string
+ * @return expression
+ */
+ public static Expression<Boolean> gte(@NonNull String compareOne, @NonNull String compareTwo) {
+ return new Expression<>(">=", literal(compareOne), literal(compareTwo));
+ }
+
+ /**
+ * Returns true if the first input is less than or equal to the second, false otherwise.
+ * The inputs must be numbers or strings, and both of the same type.
+ *
+ * @param compareOne the first expression
+ * @param compareTwo the second expression
+ * @return expression
+ */
+ public static Expression<Boolean> lte(@NonNull Expression compareOne, @NonNull Expression compareTwo) {
+ return new Expression<>("<=", compareOne, compareTwo);
+ }
+
+ /**
+ * Returns true if the first input is less than or equal to the second, false otherwise.
+ *
+ * @param compareOne the first number
+ * @param compareTwo the second number
+ * @return expression
+ */
+ public static Expression<Boolean> lte(@NonNull Number compareOne, @NonNull Number compareTwo) {
+ return new Expression<>("<=", literal(compareOne), literal(compareTwo));
+ }
+
+ /**
+ * Returns true if the first input is less than or equal to the second, false otherwise.
+ *
+ * @param compareOne the first string
+ * @param compareTwo the second string
+ * @return expression
+ */
+ public static Expression<Boolean> lte(@NonNull String compareOne, @NonNull String compareTwo) {
+ return new Expression<>("<=", literal(compareOne), literal(compareTwo));
+ }
+
+ /**
+ * Returns `true` if all the inputs are `true`, `false` otherwise.
+ * <p>
+ * The inputs are evaluated in order, and evaluation is short-circuiting:
+ * once an input expression evaluates to `false`,
+ * the result is `false` and no further input expressions are evaluated.
+ * </p>
+ *
+ * @param input expression input
+ * @return expression
+ */
+ @SafeVarargs
+ public static Expression<Boolean> all(@NonNull Expression<Boolean>... input) {
+ return new Expression<>("all", input);
+ }
+
+ /**
+ * Returns `true` if any of the inputs are `true`, `false` otherwise.
+ * <p>
+ * The inputs are evaluated in order, and evaluation is short-circuiting:
+ * once an input expression evaluates to `true`,
+ * the result is `true` and no further input expressions are evaluated.
+ * </p>
+ *
+ * @param input expression input
+ * @return expression
+ */
+ @SafeVarargs
+ public static Expression<Boolean> any(@NonNull Expression<Boolean>... input) {
+ return new Expression<>("any", input);
+ }
+
+ /**
+ * Logical negation. Returns `true` if the input is `false`, and `false` if the input is `true`.
+ *
+ * @param input expression input
+ * @return expression
+ */
+ public static Expression<Boolean> not(@NonNull Expression<Boolean> input) {
+ return new Expression<>("!", input);
+ }
+
+ /**
+ * Logical negation. Returns `true` if the input is `false`, and `false` if the input is `true`.
+ *
+ * @param input boolean input
+ * @return expression
+ */
+ public static Expression<Boolean> not(boolean input) {
+ return not(literal(input));
+ }
+
+ /**
+ * Selects the first output whose corresponding test condition evaluates to true.
+ *
+ * @param input expression input
+ * @return expression
+ */
+ @SafeVarargs
+ public static Expression switchCase(@NonNull @Size(min = 1) Expression... input) {
+ return new Expression("case", input);
+ }
+
+ /**
+ * Selects the output whose label value matches the input value, or the fallback value if no match is found.
+ * The `input` can be any string or number expression.
+ * Each label can either be a single literal value or an array of values.
+ *
+ * @param input expression input
+ * @return expression
+ */
+ public static Expression match(@NonNull @Size(min = 2) Expression... input) {
+ return new Expression("match", input);
+ }
+
+ /**
+ * Selects the output whose label value matches the input value, or the fallback value if no match is found.
+ * The `input` can be any string or number expression.
+ * Each label can either be a single literal value or an array of values.
+ *
+ * @param input expression input
+ * @return expression
+ */
+ public static Expression match(@NonNull Expression input, @NonNull Stop... stops) {
+ Expression[] expressions = new Expression[stops.length * 2];
+ for (int i = 0; i < stops.length; i++) {
+ expressions[i * 2] = literal(stops[i].value);
+ expressions[i * 2 + 1] = literal(stops[i].output);
+ }
+ return match(join(new Expression[] {input}, expressions));
+ }
+
+ /**
+ * Evaluates each expression in turn until the first non-null value is obtained, and returns that value.
+ *
+ * @param input expression input
+ * @return expression
+ */
+ public static Expression coalesce(@NonNull Expression... input) {
+ return new Expression("coalesce", input);
+ }
+
+ //
+ // FeatureData
+ //
+
+ /**
+ * Gets the feature properties object.
+ * <p>
+ * Note that in some cases, it may be more efficient to use {@link #get(Expression)}} instead.
+ * </p>
+ *
+ * @return expression
+ */
+ public static Expression<Object> properties() {
+ return new Expression<>("properties");
+ }
+
+ /**
+ * Gets the feature's geometry type: Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon.
+ *
+ * @return expression
+ */
+ public static Expression<String> geometryType() {
+ return new Expression<>("geometry-type");
+ }
+
+ /**
+ * Gets the feature's id, if it has one.
+ *
+ * @return expression
+ */
+ public static Expression<Number> id() {
+ return new Expression<>("id");
+ }
+
+ //
+ // Heatmap
+ //
+
+ /**
+ * Gets the kernel density estimation of a pixel in a heatmap layer,
+ * which is a relative measure of how many data points are crowded around a particular pixel.
+ * Can only be used in the `heatmap-color` property.
+ *
+ * @return expression
+ */
+ public static Expression<Number> heatmapDensity() {
+ return new Expression<>("heatmap-density");
+ }
+
+ //
+ // Lookup
+ //
+
+ /**
+ * Retrieves an item from an array.
+ *
+ * @param number the index expression
+ * @param expression the array expression
+ * @return expression
+ */
+ public static Expression<Object> at(@NonNull Expression<Number> number, @NonNull Expression expression) {
+ return new Expression<>("at", number, expression);
+ }
+
+ /**
+ * Retrieves an item from an array.
+ *
+ * @param number the index expression
+ * @param expression the array expression
+ * @return expression
+ */
+ public static Expression<Object> at(@NonNull Number number, @NonNull Expression expression) {
+ return at(literal(number), expression);
+ }
+
+ /**
+ * Retrieves a property value from the current feature's properties,
+ * or from another object if a second argument is provided.
+ * Returns null if the requested property is missing.
+ *
+ * @param input expression input
+ * @return expression
+ */
+ public static Expression get(@NonNull Expression<String> input) {
+ return new Expression<>("get", input);
+ }
+
+ /**
+ * Retrieves a property value from the current feature's properties,
+ * or from another object if a second argument is provided.
+ * Returns null if the requested property is missing.
+ *
+ * @param input string input
+ * @return expression
+ */
+ public static Expression get(@NonNull String input) {
+ return get(literal(input));
+ }
+
+ /**
+ * Retrieves a property value from another object.
+ * Returns null if the requested property is missing.
+ *
+ * @param key a property value key
+ * @param object an expression object
+ * @return expression
+ */
+ public static Expression<Object> get(@NonNull Expression<String> key, @NonNull Expression<Object> object) {
+ return new Expression<>("get", key, object);
+ }
+
+ /**
+ * Retrieves a property value from another object.
+ * Returns null if the requested property is missing.
+ *
+ * @param key a property value key
+ * @param object an expression object
+ * @return expression
+ */
+ public static Expression<Object> get(@NonNull String key, @NonNull Expression<Object> object) {
+ return get(literal(key), object);
+ }
+
+ /**
+ * Tests for the presence of an property value in the current feature's properties.
+ *
+ * @param key the expression property value key
+ * @return expression
+ */
+ public static Expression<Boolean> has(@NonNull Expression<String> key) {
+ return new Expression<>("has", key);
+ }
+
+ /**
+ * Tests for the presence of an property value in the current feature's properties.
+ *
+ * @param key the property value key
+ * @return expression
+ */
+ public static Expression<Boolean> has(@NonNull String key) {
+ return has(literal(key));
+ }
+
+ /**
+ * Tests for the presence of an property value from another object.
+ *
+ * @param key the expression property value key
+ * @param object an expression object
+ * @return expression
+ */
+ public static Expression<Boolean> has(@NonNull Expression<String> key, @NonNull Expression<Object> object) {
+ return new Expression<>("has", key, object);
+ }
+
+ /**
+ * Tests for the presence of an property value from another object.
+ *
+ * @param key the property value key
+ * @param object an expression object
+ * @return expression
+ */
+ public static Expression<Boolean> has(@NonNull String key, @NonNull Expression<Object> object) {
+ return has(literal(key), object);
+ }
+
+ /**
+ * Gets the length of an array or string.
+ *
+ * @param expression an expression object or expression string
+ * @return expression
+ */
+ public static Expression<Number> length(@NonNull Expression<?> expression) {
+ return new Expression<>("length", expression);
+ }
+
+ /**
+ * Gets the length of an array or string.
+ *
+ * @param input a string
+ * @return expression
+ */
+ public static Expression<Number> length(@NonNull String input) {
+ return length(literal(input));
+ }
+
+ //
+ // Math
+ //
+
+ /**
+ * Returns mathematical constant ln(2).
+ *
+ * @return expression
+ */
+ public static Expression<Number> ln2() {
+ return new Expression<>("ln2");
+ }
+
+ /**
+ * Returns the mathematical constant pi.
+ *
+ * @return expression
+ */
+ public static Expression<Number> pi() {
+ return new Expression<>("pi");
+ }
+
+ /**
+ * Returns the mathematical constant e.
+ *
+ * @return expression
+ */
+ public static Expression<Number> e() {
+ return new Expression<>("e");
+ }
+
+ /**
+ * Returns the sum of the inputs.
+ *
+ * @param numbers the numbers to calculate the sum for
+ * @return expression
+ */
+ @SafeVarargs
+ public static Expression<Number> sum(@Size(min = 2) Expression<Number>... numbers) {
+ return new Expression<>("+", numbers);
+ }
+
+ /**
+ * Returns the sum of the inputs.
+ *
+ * @param numbers the numbers to calculate the sum for
+ * @return expression
+ */
+ @SuppressWarnings("unchecked")
+ public static Expression<Number> sum(@Size(min = 2) Number... numbers) {
+ Expression<Number>[] numberExpression = (Expression<Number>[]) new Expression<?>[numbers.length];
+ for (int i = 0; i < numbers.length; i++) {
+ numberExpression[i] = literal(numbers[i]);
+ }
+ return sum(numberExpression);
+ }
+
+ /**
+ * Returns the product of the inputs.
+ *
+ * @param numbers the numbers to calculate the product for
+ * @return expression
+ */
+ @SafeVarargs
+ public static Expression<Number> product(@Size(min = 2) Expression<Number>... numbers) {
+ return new Expression<>("*", numbers);
+ }
+
+ /**
+ * Returns the product of the inputs.
+ *
+ * @param numbers the numbers to calculate the product for
+ * @return expression
+ */
+ @SuppressWarnings("unchecked")
+ public static Expression<Number> product(@Size(min = 2) Number... numbers) {
+ Expression<Number>[] numberExpression = (Expression<Number>[]) new Expression<?>[numbers.length];
+ for (int i = 0; i < numbers.length; i++) {
+ numberExpression[i] = literal(numbers[i]);
+ }
+ return product(numberExpression);
+ }
+
+ /**
+ * Returns the result of subtracting a number from 0.
+ *
+ * @param number the number subtract from 0
+ * @return expression
+ */
+ public static Expression<Number> subtract(@NonNull Expression<Number> number) {
+ return new Expression<>("-", number);
+ }
+
+ /**
+ * Returns the result of subtracting a number from 0.
+ *
+ * @param number the number subtract from 0
+ * @return expression
+ */
+ public static Expression<Number> subtract(@NonNull Number number) {
+ return subtract(literal(number));
+ }
+
+ /**
+ * Returns the result of subtracting the second input from the first.
+ *
+ * @param first the first number
+ * @param second the second number
+ * @return expression
+ */
+ public static Expression<Number> subtract(@NonNull Expression<Number> first, @NonNull Expression<Number> second) {
+ return new Expression<>("-", first, second);
+ }
+
+ /**
+ * Returns the result of subtracting the second input from the first.
+ *
+ * @param first the first number
+ * @param second the second number
+ * @return expression
+ */
+ public static Expression<Number> subtract(@NonNull Number first, @NonNull Number second) {
+ return subtract(literal(first), literal(second));
+ }
+
+ /**
+ * Returns the result of floating point division of the first input by the second.
+ *
+ * @param first the first number
+ * @param second the second number
+ * @return expression
+ */
+ @SuppressWarnings("unchecked")
+ public static Expression<Number> division(@NonNull Expression<Number> first, @NonNull Expression<Number> second) {
+ return new Expression<>("/", first, second);
+ }
+
+ /**
+ * Returns the result of floating point division of the first input by the second.
+ *
+ * @param first the first number
+ * @param second the second number
+ * @return expression
+ */
+ @SuppressWarnings("unchecked")
+ public static Expression<Number> division(@NonNull Number first, @NonNull Number second) {
+ return division(literal(first), literal(second));
+ }
+
+ /**
+ * Returns the remainder after integer division of the first input by the second.
+ *
+ * @param first the first number
+ * @param second the second number
+ * @return expression
+ */
+ public static Expression<Number> mod(@NonNull Expression<Number> first, @NonNull Expression<Number> second) {
+ return new Expression<>("%", first, second);
+ }
+
+ /**
+ * Returns the remainder after integer division of the first input by the second.
+ *
+ * @param first the first number
+ * @param second the second number
+ * @return expression
+ */
+ public static Expression<Number> mod(@NonNull Number first, @NonNull Number second) {
+ return mod(literal(first), literal(second));
+ }
+
+ /**
+ * Returns the result of raising the first input to the power specified by the second.
+ *
+ * @param first the first number
+ * @param second the second number
+ * @return expression
+ */
+ public static Expression<Number> pow(@NonNull Expression<Number> first, @NonNull Expression<Number> second) {
+ return new Expression<>("^", first, second);
+ }
+
+ /**
+ * Returns the result of raising the first input to the power specified by the second.
+ *
+ * @param first the first number
+ * @param second the second number
+ * @return expression
+ */
+ public static Expression<Number> pow(@NonNull Number first, @NonNull Number second) {
+ return pow(literal(first), literal(second));
+ }
+
+ /**
+ * Returns the square root of the input
+ *
+ * @param number the number to take the square root from
+ * @return expression
+ */
+ public static Expression<Number> sqrt(@NonNull Expression<Number> number) {
+ return new Expression<>("sqrt", number);
+ }
+
+ /**
+ * Returns the square root of the input
+ *
+ * @param number the number to take the square root from
+ * @return expression
+ */
+ public static Expression<Number> sqrt(@NonNull Number number) {
+ return sqrt(literal(number));
+ }
+
+ /**
+ * Returns the base-ten logarithm of the input.
+ *
+ * @param number the number to take base-ten logarithm from
+ * @return expression
+ */
+ public static Expression<Number> log10(@NonNull Expression<Number> number) {
+ return new Expression<>("log10", number);
+ }
+
+ /**
+ * Returns the base-ten logarithm of the input.
+ *
+ * @param number the number to take base-ten logarithm from
+ * @return expression
+ */
+ public static Expression<Number> log10(@NonNull Number number) {
+ return log10(literal(number));
+ }
+
+ /**
+ * Returns the natural logarithm of the input.
+ *
+ * @param number the number to take natural logarithm from
+ * @return expression
+ */
+ public static Expression<Number> ln(Expression<Number> number) {
+ return new Expression<>("ln", number);
+ }
+
+ /**
+ * Returns the natural logarithm of the input.
+ *
+ * @param number the number to take natural logarithm from
+ * @return expression
+ */
+ public static Expression<Number> ln(Number number) {
+ return ln(literal(number));
+ }
+
+ /**
+ * Returns the base-two logarithm of the input.
+ *
+ * @param number the number to take base-two logarithm from
+ * @return expression
+ */
+ public static Expression<Number> log2(@NonNull Expression<Number> number) {
+ return new Expression<>("log2", number);
+ }
+
+ /**
+ * Returns the base-two logarithm of the input.
+ *
+ * @param number the number to take base-two logarithm from
+ * @return expression
+ */
+ public static Expression<Number> log2(@NonNull Number number) {
+ return log2(literal(number));
+ }
+
+ /**
+ * Returns the sine of the input.
+ *
+ * @param number the number to calculate the sine for
+ * @return expression
+ */
+ public static Expression<Number> sin(@NonNull Expression<Number> number) {
+ return new Expression<>("sin", number);
+ }
+
+ /**
+ * Returns the sine of the input.
+ *
+ * @param number the number to calculate the sine for
+ * @return expression
+ */
+ public static Expression<Number> sin(@NonNull Number number) {
+ return sin(literal(number));
+ }
+
+ /**
+ * Returns the cosine of the input.
+ *
+ * @param number the number to calculate the cosine for
+ * @return expression
+ */
+ public static Expression<Number> cos(@NonNull Expression<Number> number) {
+ return new Expression<>("cos", number);
+ }
+
+ /**
+ * Returns the cosine of the input.
+ *
+ * @param number the number to calculate the cosine for
+ * @return expression
+ */
+ public static Expression<Number> cos(@NonNull Number number) {
+ return new Expression<>("cos", literal(number));
+ }
+
+ /**
+ * Returns the tangent of the input.
+ *
+ * @param number the number to calculate the tangent for
+ * @return expression
+ */
+ public static Expression<Number> tan(@NonNull Expression<Number> number) {
+ return new Expression<>("tan", number);
+ }
+
+ /**
+ * Returns the tangent of the input.
+ *
+ * @param number the number to calculate the tangent for
+ * @return expression
+ */
+ public static Expression<Number> tan(@NonNull Number number) {
+ return new Expression<>("tan", literal(number));
+ }
+
+ /**
+ * Returns the arcsine of the input.
+ *
+ * @param number the number to calculate the arcsine for
+ * @return expression
+ */
+ public static Expression<Number> asin(@NonNull Expression<Number> number) {
+ return new Expression<>("asin", number);
+ }
+
+ /**
+ * Returns the arcsine of the input.
+ *
+ * @param number the number to calculate the arcsine for
+ * @return expression
+ */
+ public static Expression<Number> asin(@NonNull Number number) {
+ return asin(literal(number));
+ }
+
+ /**
+ * Returns the arccosine of the input.
+ *
+ * @param number the number to calculate the arccosine for
+ * @return expression
+ */
+ public static Expression<Number> acos(@NonNull Expression<Number> number) {
+ return new Expression<>("acos", number);
+ }
+
+ /**
+ * Returns the arccosine of the input.
+ *
+ * @param number the number to calculate the arccosine for
+ * @return expression
+ */
+ public static Expression<Number> acos(@NonNull Number number) {
+ return acos(literal(number));
+ }
+
+ /**
+ * Returns the arctangent of the input.
+ *
+ * @param number the number to calculate the arctangent for
+ * @return expression
+ */
+ public static Expression<Number> atan(@NonNull Expression<Number> number) {
+ return new Expression("atan", number);
+ }
+
+ /**
+ * Returns the arctangent of the input.
+ *
+ * @param number the number to calculate the arctangent for
+ * @return expression
+ */
+ public static Expression<Number> atan(@NonNull Number number) {
+ return atan(literal(number));
+ }
+
+ /**
+ * Returns the minimum value of the inputs.
+ *
+ * @param numbers varargs of numbers to get the minimum from
+ * @return expression
+ */
+ @SafeVarargs
+ public static Expression<Number> min(@Size(min = 1) Expression<Number>... numbers) {
+ return new Expression<>("min", numbers);
+ }
+
+ /**
+ * Returns the minimum value of the inputs.
+ *
+ * @param numbers varargs of numbers to get the minimum from
+ * @return expression
+ */
+ @SuppressWarnings("unchecked")
+ public static Expression<Number> min(@Size(min = 1) Number... numbers) {
+ Expression<Number>[] numberExpression = (Expression<Number>[]) new Expression<?>[numbers.length];
+ for (int i = 0; i < numbers.length; i++) {
+ numberExpression[i] = literal(numbers[i]);
+ }
+ return min(numberExpression);
+ }
+
+ /**
+ * Returns the maximum value of the inputs.
+ *
+ * @param numbers varargs of numbers to get the maximum from
+ * @return expression
+ */
+ @SafeVarargs
+ public static Expression<Number> max(@Size(min = 1) Expression<Number>... numbers) {
+ return new Expression<>("max", numbers);
+ }
+
+ /**
+ * Returns the maximum value of the inputs.
+ *
+ * @param numbers varargs of numbers to get the maximum from
+ * @return expression
+ */
+ @SuppressWarnings("unchecked")
+ public static Expression<Number> max(@Size(min = 1) Number... numbers) {
+ Expression<Number>[] numberExpression = (Expression<Number>[]) new Expression<?>[numbers.length];
+ for (int i = 0; i < numbers.length; i++) {
+ numberExpression[i] = literal(numbers[i]);
+ }
+ return max(numberExpression);
+ }
+
+ //
+ // String
+ //
+
+ /**
+ * Returns the input string converted to uppercase.
+ * <p>
+ * Follows the Unicode Default Case Conversion algorithm
+ * and the locale-insensitive case mappings in the Unicode Character Database.
+ * </p>
+ *
+ * @param string the string to upcase
+ * @return expression
+ */
+ public static Expression<String> upcase(@NonNull Expression<String> string) {
+ return new Expression<>("upcase", string);
+ }
+
+ /**
+ * Returns the input string converted to uppercase.
+ * <p>
+ * Follows the Unicode Default Case Conversion algorithm
+ * and the locale-insensitive case mappings in the Unicode Character Database.
+ * </p>
+ *
+ * @param string string to upcase
+ * @return expression
+ */
+ public static Expression<String> upcase(@NonNull String string) {
+ return upcase(literal(string));
+ }
+
+ /**
+ * Returns the input string converted to lowercase.
+ * <p>
+ * Follows the Unicode Default Case Conversion algorithm
+ * and the locale-insensitive case mappings in the Unicode Character Database.
+ * </p>
+ *
+ * @param input expression input
+ * @return expression
+ */
+ public static Expression<String> downcase(@NonNull Expression<String> input) {
+ return new Expression<>("downcase", input);
+ }
+
+ /**
+ * Returns the input string converted to lowercase.
+ * <p>
+ * Follows the Unicode Default Case Conversion algorithm
+ * and the locale-insensitive case mappings in the Unicode Character Database.
+ * </p>
+ *
+ * @param input string to downcase
+ * @return expression
+ */
+ public static Expression<String> downcase(@NonNull String input) {
+ return downcase(literal(input));
+ }
+
+ /**
+ * Returns a string consisting of the concatenation of the inputs.
+ *
+ * @param input expression input
+ * @return expression
+ */
+ @SafeVarargs
+ public static Expression<String> concat(@NonNull Expression<String>... input) {
+ return new Expression<>("concat", input);
+ }
+
+ /**
+ * Returns a string consisting of the concatenation of the inputs.
+ *
+ * @param input expression input
+ * @return expression
+ */
+ @SuppressWarnings("unchecked")
+ public static Expression<String> concat(@NonNull String... input) {
+ Expression<String>[] stringExpression = (Expression<String>[]) new Expression<?>[input.length];
+ for (int i = 0; i < input.length; i++) {
+ stringExpression[i] = literal(input[i]);
+ }
+ return concat(stringExpression);
+ }
+
+ //
+ // Types
+ //
+
+ /**
+ * Asserts that the input is an array (optionally with a specific item type and length).
+ * If, when the input expression is evaluated, it is not of the asserted type,
+ * then this assertion will cause the whole expression to be aborted.
+ *
+ * @param input expression input
+ * @return expression
+ */
+ public static Expression<Boolean> array(@NonNull Expression input) {
+ return new Expression<>("array", input);
+ }
+
+ /**
+ * Returns a string describing the type of the given value.
+ *
+ * @param input expression input
+ * @return expression
+ */
+ public static Expression<String> typeOf(@NonNull Expression input) {
+ return new Expression<>("typeof", input);
+ }
+
+ /**
+ * Asserts that the input value is a string.
+ * If multiple values are provided, each one is evaluated in order until a string value is obtained.
+ * If none of the inputs are strings, the expression is an error.
+ *
+ * @param input expression input
+ * @return expression
+ */
+ public static Expression<Boolean> string(@NonNull Expression input) {
+ return new Expression<>("string", input);
+ }
+
+ /**
+ * Asserts that the input value is a number.
+ * If multiple values are provided, each one is evaluated in order until a number value is obtained.
+ * If none of the inputs are numbers, the expression is an error.
+ *
+ * @param input expression input
+ * @return expression
+ */
+ public static Expression<Boolean> number(@NonNull Expression input) {
+ return new Expression<>("number", input);
+ }
+
+ /**
+ * Asserts that the input value is a boolean.
+ * If multiple values are provided, each one is evaluated in order until a boolean value is obtained.
+ * If none of the inputs are booleans, the expression is an error.
+ *
+ * @param input expression input
+ * @return expression
+ */
+ public static Expression<Boolean> bool(@NonNull Expression input) {
+ return new Expression<>("boolean", input);
+ }
+
+ /**
+ * Asserts that the input value is an object. If it is not, the expression is an error
+ *
+ * @param input expression input
+ * @return expression
+ */
+ public static Expression<Boolean> object(@NonNull Expression input) {
+ return new Expression<>("object", input);
+ }
+
+ /**
+ * Converts the input value to a string.
+ * If the input is null, the result is null.
+ * If the input is a boolean, the result is true or false.
+ * If the input is a number, it is converted to a string by NumberToString in the ECMAScript Language Specification.
+ * If the input is a color, it is converted to a string of the form "rgba(r,g,b,a)",
+ * where `r`, `g`, and `b` are numerals ranging from 0 to 255, and `a` ranges from 0 to 1.
+ * Otherwise, the input is converted to a string in the format specified by the JSON.stringify in the ECMAScript
+ * Language Specification.
+ *
+ * @param input expression input
+ * @return expression
+ */
+ public static Expression<String> toString(@NonNull Expression input) {
+ return new Expression<>("to-string", input);
+ }
+
+ /**
+ * Converts the input value to a number, if possible.
+ * If the input is null or false, the result is 0.
+ * If the input is true, the result is 1.
+ * If the input is a string, it is converted to a number as specified by the ECMAScript Language Specification.
+ * If multiple values are provided, each one is evaluated in order until the first successful conversion is obtained.
+ * If none of the inputs can be converted, the expression is an error.
+ *
+ * @param input expression input
+ * @return expression
+ */
+ public static Expression<Number> toNumber(@NonNull Expression input) {
+ return new Expression<>("to-number", input);
+ }
+
+ /**
+ * "Converts the input value to a boolean. The result is `false` when then input is an empty string, 0, false,
+ * null, or NaN; otherwise it is true.
+ *
+ * @param input expression input
+ * @return expression
+ */
+ public static Expression<Boolean> toBool(@NonNull Expression input) {
+ return new Expression<>("to-boolean", input);
+ }
+
+ /**
+ * Converts the input value to a color. If multiple values are provided,
+ * each one is evaluated in order until the first successful conversion is obtained.
+ * If none of the inputs can be converted, the expression is an error.
+ *
+ * @param input expression input
+ * @return expression
+ */
+ public static Expression<Color> toColor(@NonNull Expression input) {
+ return new Expression<>("to-color", input);
+ }
+
+ //
+ // Variable binding
+ //
+
+ /**
+ * Binds input to named variables,
+ * which can then be referenced in the result expression using {@link #var(String)} or {@link #var(Expression)}.
+ *
+ * @param input expression input
+ * @return expression
+ */
+ @SafeVarargs
+ public static Expression let(@Size(min = 1) Expression... input) {
+ return new Expression<>("let", input);
+ }
+
+ /**
+ * References variable bound using let.
+ *
+ * @param expression the variable naming expression that was bound with using let
+ * @return expression
+ */
+ public static Expression<Object> var(@NonNull Expression<String> expression) {
+ return new Expression<>("var", expression);
+ }
+
+ /**
+ * References variable bound using let.
+ *
+ * @param variableName the variable naming that was bound with using let
+ * @return expression
+ */
+ public static Expression var(@NonNull String variableName) {
+ return var(literal(variableName));
+ }
+
+ //
+ // Zoom
+ //
+
+ /**
+ * Gets the current zoom level.
+ * <p>
+ * Note that in style layout and paint properties,
+ * zoom may only appear as the input to a top-level step or interpolate expression.
+ * </p>
+ *
+ * @return expression
+ */
+ public static Expression<Number> zoom() {
+ return new Expression<>("zoom");
+ }
+
+ //
+ // Ramps, scales, curves
+ //
+
+ public static Stop stop(@NonNull Object stop, @NonNull Object value) {
+ return new Stop(stop, value);
+ }
+
+ /**
+ * Produces discrete, stepped results by evaluating a piecewise-constant function defined by pairs of
+ * input and output values (\"stops\"). The `input` may be any numeric expression (e.g., `[\"get\", \"population\"]`).
+ * Stop inputs must be numeric literals in strictly ascending order.
+ * 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
+ * @return expression
+ */
+ @SafeVarargs
+ public static Expression step(@NonNull Number input, @NonNull Expression expression, Expression... stops) {
+ return step(literal(input), expression, stops);
+ }
+
+ /**
+ * Produces discrete, stepped results by evaluating a piecewise-constant function defined by pairs of
+ * input and output values (\"stops\"). The `input` may be any numeric expression (e.g., `[\"get\", \"population\"]`).
+ * Stop inputs must be numeric literals in strictly ascending order.
+ * 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
+ * @return expression
+ */
+ @SafeVarargs
+ public static Expression step(@NonNull Expression<Number> input, @NonNull Expression expression, Expression... stops) {
+ return new Expression("step", join(new Expression[] {input, expression}, stops));
+ }
+
+ /**
+ * Produces discrete, stepped results by evaluating a piecewise-constant function defined by pairs of
+ * input and output values (\"stops\"). The `input` may be any numeric expression (e.g., `[\"get\", \"population\"]`).
+ * Stop inputs must be numeric literals in strictly ascending order.
+ * 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
+ * @return expression
+ */
+ @SafeVarargs
+ public static Expression step(@NonNull Number input, @NonNull Expression expression, Stop... stops) {
+ Expression[] expressions = new Expression[stops.length * 2];
+ for (int i = 0; i < stops.length; i++) {
+ expressions[i * 2] = literal(stops[i].value);
+ expressions[i * 2 + 1] = literal(stops[i].output);
+ }
+ return step(literal(input), expression, expressions);
+ }
+
+ /**
+ * Produces discrete, stepped results by evaluating a piecewise-constant function defined by pairs of
+ * input and output values (\"stops\"). The `input` may be any numeric expression (e.g., `[\"get\", \"population\"]`).
+ * Stop inputs must be numeric literals in strictly ascending order.
+ * 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
+ * @return expression
+ */
+ @SafeVarargs
+ public static Expression step(@NonNull Expression<Number> input, @NonNull Expression expression, Stop... stops) {
+ Expression[] expressions = new Expression[stops.length * 2];
+ for (int i = 0; i < stops.length; i++) {
+ expressions[i * 2] = literal(stops[i].value);
+ expressions[i * 2 + 1] = literal(stops[i].output);
+ }
+ return step(input, expression, expressions);
+ }
+
+ /**
+ * Produces continuous, smooth results by interpolating between pairs of input and output values (\"stops\").
+ * The `input` may be any numeric expression (e.g., `[\"get\", \"population\"]`).
+ * Stop inputs must be numeric literals in strictly ascending order.
+ * The output type must be `number`, `array&lt;number&gt;`, or `color`.
+ *
+ * @param interpolation type of interpolation
+ * @param number the input expression
+ * @param stops pair of input and output values
+ * @return expression
+ */
+ @SafeVarargs
+ public static Expression interpolate(@NonNull Expression<Interpolator> interpolation,
+ @NonNull Expression<Number> number, Expression... stops) {
+ return new Expression("interpolate", join(new Expression[] {interpolation, number}, stops));
+ }
+
+ /**
+ * Produces continuous, smooth results by interpolating between pairs of input and output values (\"stops\").
+ * The `input` may be any numeric expression (e.g., `[\"get\", \"population\"]`).
+ * Stop inputs must be numeric literals in strictly ascending order.
+ * The output type must be `number`, `array&lt;number&gt;`, or `color`.
+ *
+ * @param interpolation type of interpolation
+ * @param number the input expression
+ * @param stops pair of input and output values
+ * @return expression
+ */
+ @SafeVarargs
+ public static Expression interpolate(@NonNull Expression<Interpolator> interpolation,
+ @NonNull Expression<Number> number, Stop... stops) {
+ Expression[] expressions = new Expression[stops.length * 2];
+ for (int i = 0; i < stops.length; i++) {
+ expressions[i * 2] = literal(stops[i].value);
+ expressions[i * 2 + 1] = literal(stops[i].output);
+ }
+ return interpolate(interpolation, number, expressions);
+ }
+
+ /**
+ * interpolates linearly between the pair of stops just less than and just greater than the input.
+ *
+ * @return expression
+ */
+ public static Expression<Interpolator> linear() {
+ return new Expression<>("linear");
+ }
+
+ /**
+ * Interpolates exponentially between the stops just less than and just greater than the input.
+ * `base` controls the rate at which the output increases:
+ * higher values make the output increase more towards the high end of the range.
+ * With values close to 1 the output increases linearly.
+ *
+ * @param base value controlling the route at which the output increases
+ * @return expression
+ */
+ public static Expression<Interpolator> exponential(@NonNull Number base) {
+ return exponential(literal(base));
+ }
+
+ /**
+ * Interpolates exponentially between the stops just less than and just greater than the input.
+ * The parameter controls the rate at which the output increases:
+ * higher values make the output increase more towards the high end of the range.
+ * With values close to 1 the output increases linearly.
+ *
+ * @param expression base number expression
+ * @return expression
+ */
+ public static Expression<Interpolator> exponential(@NonNull Expression<Number> expression) {
+ return new Expression<>("exponential", expression);
+ }
+
+ /**
+ * Interpolates using the cubic bezier curve defined by the given control points.
+ *
+ * @param x1 x value of the first point of a cubic bezier, ranges from 0 to 1
+ * @param y1 y value of the first point of a cubic bezier, ranges from 0 to 1
+ * @param x2 x value of the second point of a cubic bezier, ranges from 0 to 1
+ * @param y2 y value fo the second point of a cubic bezier, ranges from 0 to 1
+ * @return expression
+ */
+ public static Expression<Interpolator> cubicBezier(@NonNull Expression<Number> x1, @NonNull Expression<Number> y1,
+ @NonNull Expression<Number> x2, @NonNull Expression<Number> y2) {
+ return new Expression<>("cubic-bezier", x1, y1, x2, y2);
+ }
+
+ /**
+ * Interpolates using the cubic bezier curve defined by the given control points.
+ *
+ * @param x1 x value of the first point of a cubic bezier, ranges from 0 to 1
+ * @param y1 y value of the first point of a cubic bezier, ranges from 0 to 1
+ * @param x2 x value of the second point of a cubic bezier, ranges from 0 to 1
+ * @param y2 y value fo the second point of a cubic bezier, ranges from 0 to 1
+ * @return expression
+ */
+ public static Expression<Interpolator> cubicBezier(@NonNull Number x1, @NonNull Number y1,
+ @NonNull Number x2, @NonNull Number y2) {
+ return cubicBezier(literal(x1), literal(y1), literal(x2), literal(y2));
+ }
+
+ /**
+ * Joins two expressions arrays.
+ * <p>
+ * This flattens the object array output of an expression from a nested expression hierarchy.
+ * </p>
+ *
+ * @param left the left part of an expression
+ * @param right the right part of an expression
+ * @return the joined expression
+ */
+ private static Expression[] join(Expression[] left, Expression[] right) {
+ Expression[] output = new Expression[left.length + right.length];
+ System.arraycopy(left, 0, output, 0, left.length);
+ System.arraycopy(right, 0, output, left.length, right.length);
+ return output;
+ }
+
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("[\"").append(operator).append("\"");
+ if (arguments != null) {
+ for (Expression argument : arguments) {
+ builder.append(", ");
+ if (argument instanceof ExpressionLiteral) {
+ builder.append(((ExpressionLiteral) argument).toValue());
+ } else {
+ builder.append(argument.toString());
+ }
+ }
+ }
+ builder.append("]");
+ return builder.toString();
+ }
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HeatmapLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HeatmapLayer.java
new file mode 100644
index 0000000000..6b8fd65def
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HeatmapLayer.java
@@ -0,0 +1,221 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`.
+
+package com.mapbox.mapboxsdk.style.layers;
+
+import android.support.annotation.ColorInt;
+import android.support.annotation.NonNull;
+import android.support.annotation.UiThread;
+
+import static com.mapbox.mapboxsdk.utils.ColorUtils.rgbaToColor;
+
+import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
+
+/**
+ * A heatmap.
+ *
+ * @see <a href="https://www.mapbox.com/mapbox-gl-style-spec/#layers-heatmap">The online documentation</a>
+ */
+@UiThread
+public class HeatmapLayer extends Layer {
+
+ /**
+ * Creates a HeatmapLayer.
+ *
+ * @param nativePtr pointer used by core
+ */
+ public HeatmapLayer(long nativePtr) {
+ super(nativePtr);
+ }
+
+ /**
+ * Creates a HeatmapLayer.
+ *
+ * @param layerId the id of the layer
+ * @param sourceId the id of the source
+ */
+ public HeatmapLayer(String layerId, String sourceId) {
+ initialize(layerId, sourceId);
+ }
+
+ protected native void initialize(String layerId, String sourceId);
+
+ /**
+ * Set the source layer.
+ *
+ * @param sourceLayer the source layer to set
+ */
+ public void setSourceLayer(String sourceLayer) {
+ nativeSetSourceLayer(sourceLayer);
+ }
+
+ /**
+ * Set the source Layer.
+ *
+ * @param sourceLayer the source layer to set
+ * @return This
+ */
+ public HeatmapLayer withSourceLayer(String sourceLayer) {
+ setSourceLayer(sourceLayer);
+ return this;
+ }
+
+ /**
+ * Get the source layer.
+ *
+ * @return sourceLayer the source layer to get
+ */
+ public String getSourceLayer() {
+ return nativeGetSourceLayer();
+ }
+
+ /**
+ * Set a single filter.
+ *
+ * @param filter the filter to set
+ */
+ public void setFilter(Filter.Statement filter) {
+ nativeSetFilter(filter.toArray());
+ }
+
+ /**
+ * Set a single filter.
+ *
+ * @param filter the filter to set
+ * @return This
+ */
+ public HeatmapLayer withFilter(Filter.Statement filter) {
+ setFilter(filter);
+ return this;
+ }
+
+ /**
+ * Set a property or properties.
+ *
+ * @param properties the var-args properties
+ * @return This
+ */
+ public HeatmapLayer withProperties(@NonNull PropertyValue<?>... properties) {
+ setProperties(properties);
+ return this;
+ }
+
+ // Property getters
+
+ /**
+ * Get the HeatmapRadius property
+ *
+ * @return property wrapper value around Float
+ */
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getHeatmapRadius() {
+ return (PropertyValue<Float>) new PropertyValue("heatmap-radius", nativeGetHeatmapRadius());
+ }
+
+ /**
+ * Get the HeatmapRadius property transition options
+ *
+ * @return transition options for Float
+ */
+ public TransitionOptions getHeatmapRadiusTransition() {
+ return nativeGetHeatmapRadiusTransition();
+ }
+
+ /**
+ * Set the HeatmapRadius property transition options
+ *
+ * @param options transition options for Float
+ */
+ public void setHeatmapRadiusTransition(TransitionOptions options) {
+ nativeSetHeatmapRadiusTransition(options.getDuration(), options.getDelay());
+ }
+
+ /**
+ * Get the HeatmapWeight property
+ *
+ * @return property wrapper value around Float
+ */
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getHeatmapWeight() {
+ return (PropertyValue<Float>) new PropertyValue("heatmap-weight", nativeGetHeatmapWeight());
+ }
+
+ /**
+ * Get the HeatmapIntensity property
+ *
+ * @return property wrapper value around Float
+ */
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getHeatmapIntensity() {
+ return (PropertyValue<Float>) new PropertyValue("heatmap-intensity", nativeGetHeatmapIntensity());
+ }
+
+ /**
+ * Get the HeatmapIntensity property transition options
+ *
+ * @return transition options for Float
+ */
+ public TransitionOptions getHeatmapIntensityTransition() {
+ return nativeGetHeatmapIntensityTransition();
+ }
+
+ /**
+ * Set the HeatmapIntensity property transition options
+ *
+ * @param options transition options for Float
+ */
+ public void setHeatmapIntensityTransition(TransitionOptions options) {
+ nativeSetHeatmapIntensityTransition(options.getDuration(), options.getDelay());
+ }
+
+ /**
+ * Get the HeatmapOpacity property
+ *
+ * @return property wrapper value around Float
+ */
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getHeatmapOpacity() {
+ return (PropertyValue<Float>) new PropertyValue("heatmap-opacity", nativeGetHeatmapOpacity());
+ }
+
+ /**
+ * Get the HeatmapOpacity property transition options
+ *
+ * @return transition options for Float
+ */
+ public TransitionOptions getHeatmapOpacityTransition() {
+ return nativeGetHeatmapOpacityTransition();
+ }
+
+ /**
+ * Set the HeatmapOpacity property transition options
+ *
+ * @param options transition options for Float
+ */
+ public void setHeatmapOpacityTransition(TransitionOptions options) {
+ nativeSetHeatmapOpacityTransition(options.getDuration(), options.getDelay());
+ }
+
+ private native Object nativeGetHeatmapRadius();
+
+ private native TransitionOptions nativeGetHeatmapRadiusTransition();
+
+ private native void nativeSetHeatmapRadiusTransition(long duration, long delay);
+
+ private native Object nativeGetHeatmapWeight();
+
+ private native Object nativeGetHeatmapIntensity();
+
+ private native TransitionOptions nativeGetHeatmapIntensityTransition();
+
+ private native void nativeSetHeatmapIntensityTransition(long duration, long delay);
+
+ private native Object nativeGetHeatmapOpacity();
+
+ private native TransitionOptions nativeGetHeatmapOpacityTransition();
+
+ private native void nativeSetHeatmapOpacityTransition(long duration, long delay);
+
+ @Override
+ protected native void finalize() throws Throwable;
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HillshadeLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HillshadeLayer.java
new file mode 100644
index 0000000000..7e3d3a7779
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HillshadeLayer.java
@@ -0,0 +1,286 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`.
+
+package com.mapbox.mapboxsdk.style.layers;
+
+import android.support.annotation.ColorInt;
+import android.support.annotation.NonNull;
+import android.support.annotation.UiThread;
+
+import static com.mapbox.mapboxsdk.utils.ColorUtils.rgbaToColor;
+
+import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
+
+/**
+ * Client-side hillshading visualization based on DEM data. Currently, the implementation only supports Mapbox Terrain RGB and Mapzen Terrarium tiles.
+ *
+ * @see <a href="https://www.mapbox.com/mapbox-gl-style-spec/#layers-hillshade">The online documentation</a>
+ */
+@UiThread
+public class HillshadeLayer extends Layer {
+
+ /**
+ * Creates a HillshadeLayer.
+ *
+ * @param nativePtr pointer used by core
+ */
+ public HillshadeLayer(long nativePtr) {
+ super(nativePtr);
+ }
+
+ /**
+ * Creates a HillshadeLayer.
+ *
+ * @param layerId the id of the layer
+ * @param sourceId the id of the source
+ */
+ public HillshadeLayer(String layerId, String sourceId) {
+ initialize(layerId, sourceId);
+ }
+
+ protected native void initialize(String layerId, String sourceId);
+
+ /**
+ * Set the source layer.
+ *
+ * @param sourceLayer the source layer to set
+ */
+ public void setSourceLayer(String sourceLayer) {
+ nativeSetSourceLayer(sourceLayer);
+ }
+
+ /**
+ * Set the source Layer.
+ *
+ * @param sourceLayer the source layer to set
+ * @return This
+ */
+ public HillshadeLayer withSourceLayer(String sourceLayer) {
+ setSourceLayer(sourceLayer);
+ return this;
+ }
+
+ /**
+ * Set a property or properties.
+ *
+ * @param properties the var-args properties
+ * @return This
+ */
+ public HillshadeLayer withProperties(@NonNull PropertyValue<?>... properties) {
+ setProperties(properties);
+ return this;
+ }
+
+ // Property getters
+
+ /**
+ * Get the HillshadeIlluminationDirection property
+ *
+ * @return property wrapper value around Float
+ */
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getHillshadeIlluminationDirection() {
+ return (PropertyValue<Float>) new PropertyValue("hillshade-illumination-direction", nativeGetHillshadeIlluminationDirection());
+ }
+
+ /**
+ * Get the HillshadeIlluminationAnchor property
+ *
+ * @return property wrapper value around String
+ */
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getHillshadeIlluminationAnchor() {
+ return (PropertyValue<String>) new PropertyValue("hillshade-illumination-anchor", nativeGetHillshadeIlluminationAnchor());
+ }
+
+ /**
+ * Get the HillshadeExaggeration property
+ *
+ * @return property wrapper value around Float
+ */
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getHillshadeExaggeration() {
+ return (PropertyValue<Float>) new PropertyValue("hillshade-exaggeration", nativeGetHillshadeExaggeration());
+ }
+
+ /**
+ * Get the HillshadeExaggeration property transition options
+ *
+ * @return transition options for Float
+ */
+ public TransitionOptions getHillshadeExaggerationTransition() {
+ return nativeGetHillshadeExaggerationTransition();
+ }
+
+ /**
+ * Set the HillshadeExaggeration property transition options
+ *
+ * @param options transition options for Float
+ */
+ public void setHillshadeExaggerationTransition(TransitionOptions options) {
+ nativeSetHillshadeExaggerationTransition(options.getDuration(), options.getDelay());
+ }
+
+ /**
+ * Get the HillshadeShadowColor property
+ *
+ * @return property wrapper value around String
+ */
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getHillshadeShadowColor() {
+ return (PropertyValue<String>) new PropertyValue("hillshade-shadow-color", nativeGetHillshadeShadowColor());
+ }
+
+ /**
+ * The shading color of areas that face away from the light source.
+ *
+ * @return int representation of a rgba string color
+ * @throws RuntimeException thrown if property isn't a value
+ */
+ @ColorInt
+ public int getHillshadeShadowColorAsInt() {
+ PropertyValue<String> value = getHillshadeShadowColor();
+ if (value.isValue()) {
+ return rgbaToColor(value.getValue());
+ } else {
+ throw new RuntimeException("hillshade-shadow-color was set as a Function");
+ }
+ }
+
+ /**
+ * Get the HillshadeShadowColor property transition options
+ *
+ * @return transition options for String
+ */
+ public TransitionOptions getHillshadeShadowColorTransition() {
+ return nativeGetHillshadeShadowColorTransition();
+ }
+
+ /**
+ * Set the HillshadeShadowColor property transition options
+ *
+ * @param options transition options for String
+ */
+ public void setHillshadeShadowColorTransition(TransitionOptions options) {
+ nativeSetHillshadeShadowColorTransition(options.getDuration(), options.getDelay());
+ }
+
+ /**
+ * Get the HillshadeHighlightColor property
+ *
+ * @return property wrapper value around String
+ */
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getHillshadeHighlightColor() {
+ return (PropertyValue<String>) new PropertyValue("hillshade-highlight-color", nativeGetHillshadeHighlightColor());
+ }
+
+ /**
+ * The shading color of areas that faces towards the light source.
+ *
+ * @return int representation of a rgba string color
+ * @throws RuntimeException thrown if property isn't a value
+ */
+ @ColorInt
+ public int getHillshadeHighlightColorAsInt() {
+ PropertyValue<String> value = getHillshadeHighlightColor();
+ if (value.isValue()) {
+ return rgbaToColor(value.getValue());
+ } else {
+ throw new RuntimeException("hillshade-highlight-color was set as a Function");
+ }
+ }
+
+ /**
+ * Get the HillshadeHighlightColor property transition options
+ *
+ * @return transition options for String
+ */
+ public TransitionOptions getHillshadeHighlightColorTransition() {
+ return nativeGetHillshadeHighlightColorTransition();
+ }
+
+ /**
+ * Set the HillshadeHighlightColor property transition options
+ *
+ * @param options transition options for String
+ */
+ public void setHillshadeHighlightColorTransition(TransitionOptions options) {
+ nativeSetHillshadeHighlightColorTransition(options.getDuration(), options.getDelay());
+ }
+
+ /**
+ * Get the HillshadeAccentColor property
+ *
+ * @return property wrapper value around String
+ */
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getHillshadeAccentColor() {
+ return (PropertyValue<String>) new PropertyValue("hillshade-accent-color", nativeGetHillshadeAccentColor());
+ }
+
+ /**
+ * The shading color used to accentuate rugged terrain like sharp cliffs and gorges.
+ *
+ * @return int representation of a rgba string color
+ * @throws RuntimeException thrown if property isn't a value
+ */
+ @ColorInt
+ public int getHillshadeAccentColorAsInt() {
+ PropertyValue<String> value = getHillshadeAccentColor();
+ if (value.isValue()) {
+ return rgbaToColor(value.getValue());
+ } else {
+ throw new RuntimeException("hillshade-accent-color was set as a Function");
+ }
+ }
+
+ /**
+ * Get the HillshadeAccentColor property transition options
+ *
+ * @return transition options for String
+ */
+ public TransitionOptions getHillshadeAccentColorTransition() {
+ return nativeGetHillshadeAccentColorTransition();
+ }
+
+ /**
+ * Set the HillshadeAccentColor property transition options
+ *
+ * @param options transition options for String
+ */
+ public void setHillshadeAccentColorTransition(TransitionOptions options) {
+ nativeSetHillshadeAccentColorTransition(options.getDuration(), options.getDelay());
+ }
+
+ private native Object nativeGetHillshadeIlluminationDirection();
+
+ private native Object nativeGetHillshadeIlluminationAnchor();
+
+ private native Object nativeGetHillshadeExaggeration();
+
+ private native TransitionOptions nativeGetHillshadeExaggerationTransition();
+
+ private native void nativeSetHillshadeExaggerationTransition(long duration, long delay);
+
+ private native Object nativeGetHillshadeShadowColor();
+
+ private native TransitionOptions nativeGetHillshadeShadowColorTransition();
+
+ private native void nativeSetHillshadeShadowColorTransition(long duration, long delay);
+
+ private native Object nativeGetHillshadeHighlightColor();
+
+ private native TransitionOptions nativeGetHillshadeHighlightColorTransition();
+
+ private native void nativeSetHillshadeHighlightColorTransition(long duration, long delay);
+
+ private native Object nativeGetHillshadeAccentColor();
+
+ private native TransitionOptions nativeGetHillshadeAccentColorTransition();
+
+ private native void nativeSetHillshadeAccentColorTransition(long duration, long delay);
+
+ @Override
+ protected native void finalize() throws Throwable;
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Layer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Layer.java
index 5015dd009d..5400e04589 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.mapbox.mapboxsdk.style.expressions.Expression;
import com.mapbox.mapboxsdk.style.functions.Function;
/**
@@ -88,6 +89,14 @@ public abstract class Layer {
}
private Object convertValue(Object value) {
- return value != null && value instanceof Function ? ((Function) value).toValueObject() : value;
+ if (value != null) {
+ if (value instanceof Function) {
+ return ((Function) value).toValueObject();
+ } else if (value instanceof Expression) {
+ return ((Expression) value).toArray();
+ }
+ }
+ return value;
}
-}
+
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java
index 8d5858217b..e52474c35b 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java
@@ -402,7 +402,7 @@ public final class Property {
@Retention(RetentionPolicy.SOURCE)
public @interface TEXT_TRANSFORM {}
- // FILL_TRANSLATE_ANCHOR: Controls the translation reference point.
+ // FILL_TRANSLATE_ANCHOR: Controls the frame of reference for `fill-translate`.
/**
* The fill is translated relative to the map.
@@ -414,7 +414,7 @@ public final class Property {
public static final String FILL_TRANSLATE_ANCHOR_VIEWPORT = "viewport";
/**
- * Controls the translation reference point.
+ * Controls the frame of reference for `fill-translate`.
*/
@StringDef({
FILL_TRANSLATE_ANCHOR_MAP,
@@ -423,7 +423,7 @@ public final class Property {
@Retention(RetentionPolicy.SOURCE)
public @interface FILL_TRANSLATE_ANCHOR {}
- // LINE_TRANSLATE_ANCHOR: Controls the translation reference point.
+ // LINE_TRANSLATE_ANCHOR: Controls the frame of reference for `line-translate`.
/**
* The line is translated relative to the map.
@@ -435,7 +435,7 @@ public final class Property {
public static final String LINE_TRANSLATE_ANCHOR_VIEWPORT = "viewport";
/**
- * Controls the translation reference point.
+ * Controls the frame of reference for `line-translate`.
*/
@StringDef({
LINE_TRANSLATE_ANCHOR_MAP,
@@ -444,7 +444,7 @@ public final class Property {
@Retention(RetentionPolicy.SOURCE)
public @interface LINE_TRANSLATE_ANCHOR {}
- // ICON_TRANSLATE_ANCHOR: Controls the translation reference point.
+ // ICON_TRANSLATE_ANCHOR: Controls the frame of reference for `icon-translate`.
/**
* Icons are translated relative to the map.
@@ -456,7 +456,7 @@ public final class Property {
public static final String ICON_TRANSLATE_ANCHOR_VIEWPORT = "viewport";
/**
- * Controls the translation reference point.
+ * Controls the frame of reference for `icon-translate`.
*/
@StringDef({
ICON_TRANSLATE_ANCHOR_MAP,
@@ -465,7 +465,7 @@ public final class Property {
@Retention(RetentionPolicy.SOURCE)
public @interface ICON_TRANSLATE_ANCHOR {}
- // TEXT_TRANSLATE_ANCHOR: Controls the translation reference point.
+ // TEXT_TRANSLATE_ANCHOR: Controls the frame of reference for `text-translate`.
/**
* The text is translated relative to the map.
@@ -477,7 +477,7 @@ public final class Property {
public static final String TEXT_TRANSLATE_ANCHOR_VIEWPORT = "viewport";
/**
- * Controls the translation reference point.
+ * Controls the frame of reference for `text-translate`.
*/
@StringDef({
TEXT_TRANSLATE_ANCHOR_MAP,
@@ -486,7 +486,7 @@ public final class Property {
@Retention(RetentionPolicy.SOURCE)
public @interface TEXT_TRANSLATE_ANCHOR {}
- // CIRCLE_TRANSLATE_ANCHOR: Controls the translation reference point.
+ // CIRCLE_TRANSLATE_ANCHOR: Controls the frame of reference for `circle-translate`.
/**
* The circle is translated relative to the map.
@@ -498,7 +498,7 @@ public final class Property {
public static final String CIRCLE_TRANSLATE_ANCHOR_VIEWPORT = "viewport";
/**
- * Controls the translation reference point.
+ * Controls the frame of reference for `circle-translate`.
*/
@StringDef({
CIRCLE_TRANSLATE_ANCHOR_MAP,
@@ -549,7 +549,7 @@ public final class Property {
@Retention(RetentionPolicy.SOURCE)
public @interface CIRCLE_PITCH_ALIGNMENT {}
- // FILL_EXTRUSION_TRANSLATE_ANCHOR: Controls the translation reference point.
+ // FILL_EXTRUSION_TRANSLATE_ANCHOR: Controls the frame of reference for `fill-extrusion-translate`.
/**
* The fill extrusion is translated relative to the map.
@@ -561,7 +561,7 @@ public final class Property {
public static final String FILL_EXTRUSION_TRANSLATE_ANCHOR_VIEWPORT = "viewport";
/**
- * Controls the translation reference point.
+ * Controls the frame of reference for `fill-extrusion-translate`.
*/
@StringDef({
FILL_EXTRUSION_TRANSLATE_ANCHOR_MAP,
@@ -570,6 +570,27 @@ public final class Property {
@Retention(RetentionPolicy.SOURCE)
public @interface FILL_EXTRUSION_TRANSLATE_ANCHOR {}
+ // HILLSHADE_ILLUMINATION_ANCHOR: Direction of light source when map is rotated.
+
+ /**
+ * The hillshade illumination is relative to the north direction.
+ */
+ public static final String HILLSHADE_ILLUMINATION_ANCHOR_MAP = "map";
+ /**
+ * The hillshade illumination is relative to the top of the viewport.
+ */
+ public static final String HILLSHADE_ILLUMINATION_ANCHOR_VIEWPORT = "viewport";
+
+ /**
+ * Direction of light source when map is rotated.
+ */
+ @StringDef({
+ HILLSHADE_ILLUMINATION_ANCHOR_MAP,
+ HILLSHADE_ILLUMINATION_ANCHOR_VIEWPORT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface HILLSHADE_ILLUMINATION_ANCHOR {}
+
// ANCHOR: Whether extruded geometries are lit relative to the map or viewport.
/**
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java
index c43977aec6..18ee05e63b 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java
@@ -2,11 +2,11 @@
package com.mapbox.mapboxsdk.style.layers;
-import android.annotation.SuppressLint;
import android.support.annotation.ColorInt;
-import com.mapbox.mapboxsdk.style.functions.Function;
import com.mapbox.mapboxsdk.style.functions.CameraFunction;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
+import com.mapbox.mapboxsdk.style.functions.Function;
import java.util.Locale;
@@ -34,6 +34,7 @@ public class PropertyFactory {
* @param function the visibility function
* @return property wrapper around a String function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, String>> visibility(Function<T, String> function) {
return new LayoutPropertyValue<>("visibility", function);
}
@@ -48,6 +49,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("fill-antialias", value);
}
+ /**
+ * Whether or not the fill should be antialiased.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> fillAntialias(Expression expression) {
+ return new PaintPropertyValue<>("fill-antialias", expression);
+ }
+
/**
* Whether or not the fill should be antialiased.
@@ -56,6 +67,7 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for Boolean
* @return property wrapper around a Boolean function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, Boolean>> fillAntialias(CameraFunction<Z, Boolean> function) {
return new PaintPropertyValue<>("fill-antialias", function);
}
@@ -70,6 +82,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("fill-opacity", value);
}
+ /**
+ * The opacity of the entire fill layer. In contrast to the {@link PropertyFactory#fillColor}, this value will also affect the 1px stroke around the fill, if the stroke is used.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> fillOpacity(Expression expression) {
+ return new PaintPropertyValue<>("fill-opacity", expression);
+ }
+
/**
* The opacity of the entire fill layer. In contrast to the {@link PropertyFactory#fillColor}, this value will also affect the 1px stroke around the fill, if the stroke is used.
@@ -78,6 +100,7 @@ public class PropertyFactory {
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, Float>> fillOpacity(Function<T, Float> function) {
return new PaintPropertyValue<>("fill-opacity", function);
}
@@ -102,6 +125,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("fill-color", value);
}
+ /**
+ * The color of the filled part of this layer. This color can be specified as `rgba` with an alpha component and the color's opacity will not affect the opacity of the 1px stroke, if it is used.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> fillColor(Expression expression) {
+ return new PaintPropertyValue<>("fill-color", expression);
+ }
+
/**
* The color of the filled part of this layer. This color can be specified as `rgba` with an alpha component and the color's opacity will not affect the opacity of the 1px stroke, if it is used.
@@ -110,6 +143,7 @@ public class PropertyFactory {
* @param function a wrapper function for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, String>> fillColor(Function<T, String> function) {
return new PaintPropertyValue<>("fill-color", function);
}
@@ -134,6 +168,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("fill-outline-color", value);
}
+ /**
+ * The outline color of the fill. Matches the value of {@link PropertyFactory#fillColor} if unspecified.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> fillOutlineColor(Expression expression) {
+ return new PaintPropertyValue<>("fill-outline-color", expression);
+ }
+
/**
* The outline color of the fill. Matches the value of {@link PropertyFactory#fillColor} if unspecified.
@@ -142,6 +186,7 @@ public class PropertyFactory {
* @param function a wrapper function for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, String>> fillOutlineColor(Function<T, String> function) {
return new PaintPropertyValue<>("fill-outline-color", function);
}
@@ -156,6 +201,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("fill-translate", value);
}
+ /**
+ * The geometry's offset. Values are [x, y] where negatives indicate left and up, respectively.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> fillTranslate(Expression expression) {
+ return new PaintPropertyValue<>("fill-translate", expression);
+ }
+
/**
* The geometry's offset. Values are [x, y] where negatives indicate left and up, respectively.
@@ -164,12 +219,13 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for Float[]
* @return property wrapper around a Float[] function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, Float[]>> fillTranslate(CameraFunction<Z, Float[]> function) {
return new PaintPropertyValue<>("fill-translate", function);
}
/**
- * Controls the translation reference point.
+ * Controls the frame of reference for {@link PropertyFactory#fillTranslate}.
*
* @param value a String value
* @return property wrapper around String
@@ -178,14 +234,25 @@ public class PropertyFactory {
return new PaintPropertyValue<>("fill-translate-anchor", value);
}
+ /**
+ * Controls the frame of reference for {@link PropertyFactory#fillTranslate}.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> fillTranslateAnchor(Expression expression) {
+ return new PaintPropertyValue<>("fill-translate-anchor", expression);
+ }
+
/**
- * Controls the translation reference point.
+ * Controls the frame of reference for {@link PropertyFactory#fillTranslate}.
*
* @param <Z> the zoom parameter type
* @param function a wrapper {@link CameraFunction} for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> fillTranslateAnchor(CameraFunction<Z, String> function) {
return new PaintPropertyValue<>("fill-translate-anchor", function);
}
@@ -200,6 +267,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("fill-pattern", value);
}
+ /**
+ * Name of image in sprite to use for drawing image fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512).
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> fillPattern(Expression expression) {
+ return new PaintPropertyValue<>("fill-pattern", expression);
+ }
+
/**
* Name of image in sprite to use for drawing image fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512).
@@ -208,6 +285,7 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> fillPattern(CameraFunction<Z, String> function) {
return new PaintPropertyValue<>("fill-pattern", function);
}
@@ -222,6 +300,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("line-opacity", value);
}
+ /**
+ * The opacity at which the line will be drawn.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> lineOpacity(Expression expression) {
+ return new PaintPropertyValue<>("line-opacity", expression);
+ }
+
/**
* The opacity at which the line will be drawn.
@@ -230,6 +318,7 @@ public class PropertyFactory {
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, Float>> lineOpacity(Function<T, Float> function) {
return new PaintPropertyValue<>("line-opacity", function);
}
@@ -254,6 +343,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("line-color", value);
}
+ /**
+ * The color with which the line will be drawn.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> lineColor(Expression expression) {
+ return new PaintPropertyValue<>("line-color", expression);
+ }
+
/**
* The color with which the line will be drawn.
@@ -262,6 +361,7 @@ public class PropertyFactory {
* @param function a wrapper function for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, String>> lineColor(Function<T, String> function) {
return new PaintPropertyValue<>("line-color", function);
}
@@ -276,6 +376,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("line-translate", value);
}
+ /**
+ * The geometry's offset. Values are [x, y] where negatives indicate left and up, respectively.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> lineTranslate(Expression expression) {
+ return new PaintPropertyValue<>("line-translate", expression);
+ }
+
/**
* The geometry's offset. Values are [x, y] where negatives indicate left and up, respectively.
@@ -284,12 +394,13 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for Float[]
* @return property wrapper around a Float[] function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, Float[]>> lineTranslate(CameraFunction<Z, Float[]> function) {
return new PaintPropertyValue<>("line-translate", function);
}
/**
- * Controls the translation reference point.
+ * Controls the frame of reference for {@link PropertyFactory#lineTranslate}.
*
* @param value a String value
* @return property wrapper around String
@@ -298,14 +409,25 @@ public class PropertyFactory {
return new PaintPropertyValue<>("line-translate-anchor", value);
}
+ /**
+ * Controls the frame of reference for {@link PropertyFactory#lineTranslate}.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> lineTranslateAnchor(Expression expression) {
+ return new PaintPropertyValue<>("line-translate-anchor", expression);
+ }
+
/**
- * Controls the translation reference point.
+ * Controls the frame of reference for {@link PropertyFactory#lineTranslate}.
*
* @param <Z> the zoom parameter type
* @param function a wrapper {@link CameraFunction} for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> lineTranslateAnchor(CameraFunction<Z, String> function) {
return new PaintPropertyValue<>("line-translate-anchor", function);
}
@@ -320,6 +442,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("line-width", value);
}
+ /**
+ * Stroke thickness.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> lineWidth(Expression expression) {
+ return new PaintPropertyValue<>("line-width", expression);
+ }
+
/**
* Stroke thickness.
@@ -328,6 +460,7 @@ public class PropertyFactory {
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, Float>> lineWidth(Function<T, Float> function) {
return new PaintPropertyValue<>("line-width", function);
}
@@ -342,6 +475,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("line-gap-width", value);
}
+ /**
+ * Draws a line casing outside of a line's actual path. Value indicates the width of the inner gap.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> lineGapWidth(Expression expression) {
+ return new PaintPropertyValue<>("line-gap-width", expression);
+ }
+
/**
* Draws a line casing outside of a line's actual path. Value indicates the width of the inner gap.
@@ -350,6 +493,7 @@ public class PropertyFactory {
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, Float>> lineGapWidth(Function<T, Float> function) {
return new PaintPropertyValue<>("line-gap-width", function);
}
@@ -364,6 +508,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("line-offset", value);
}
+ /**
+ * The line's offset. For linear features, a positive value offsets the line to the right, relative to the direction of the line, and a negative value to the left. For polygon features, a positive value results in an inset, and a negative value results in an outset.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> lineOffset(Expression expression) {
+ return new PaintPropertyValue<>("line-offset", expression);
+ }
+
/**
* The line's offset. For linear features, a positive value offsets the line to the right, relative to the direction of the line, and a negative value to the left. For polygon features, a positive value results in an inset, and a negative value results in an outset.
@@ -372,6 +526,7 @@ public class PropertyFactory {
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, Float>> lineOffset(Function<T, Float> function) {
return new PaintPropertyValue<>("line-offset", function);
}
@@ -386,6 +541,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("line-blur", value);
}
+ /**
+ * Blur applied to the line, in density-independent pixels.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> lineBlur(Expression expression) {
+ return new PaintPropertyValue<>("line-blur", expression);
+ }
+
/**
* Blur applied to the line, in density-independent pixels.
@@ -394,6 +559,7 @@ public class PropertyFactory {
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, Float>> lineBlur(Function<T, Float> function) {
return new PaintPropertyValue<>("line-blur", function);
}
@@ -408,6 +574,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("line-dasharray", value);
}
+ /**
+ * Specifies the lengths of the alternating dashes and gaps that form the dash pattern. The lengths are later scaled by the line width. To convert a dash length to density-independent pixels, multiply the length by the current line width.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> lineDasharray(Expression expression) {
+ return new PaintPropertyValue<>("line-dasharray", expression);
+ }
+
/**
* Specifies the lengths of the alternating dashes and gaps that form the dash pattern. The lengths are later scaled by the line width. To convert a dash length to density-independent pixels, multiply the length by the current line width.
@@ -416,6 +592,7 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for Float[]
* @return property wrapper around a Float[] function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, Float[]>> lineDasharray(CameraFunction<Z, Float[]> function) {
return new PaintPropertyValue<>("line-dasharray", function);
}
@@ -430,6 +607,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("line-pattern", value);
}
+ /**
+ * Name of image in sprite to use for drawing image lines. For seamless patterns, image width must be a factor of two (2, 4, 8, ..., 512).
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> linePattern(Expression expression) {
+ return new PaintPropertyValue<>("line-pattern", expression);
+ }
+
/**
* Name of image in sprite to use for drawing image lines. For seamless patterns, image width must be a factor of two (2, 4, 8, ..., 512).
@@ -438,6 +625,7 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> linePattern(CameraFunction<Z, String> function) {
return new PaintPropertyValue<>("line-pattern", function);
}
@@ -452,6 +640,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("icon-opacity", value);
}
+ /**
+ * The opacity at which the icon will be drawn.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> iconOpacity(Expression expression) {
+ return new PaintPropertyValue<>("icon-opacity", expression);
+ }
+
/**
* The opacity at which the icon will be drawn.
@@ -460,6 +658,7 @@ public class PropertyFactory {
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, Float>> iconOpacity(Function<T, Float> function) {
return new PaintPropertyValue<>("icon-opacity", function);
}
@@ -484,6 +683,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("icon-color", value);
}
+ /**
+ * The color of the icon. This can only be used with sdf icons.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> iconColor(Expression expression) {
+ return new PaintPropertyValue<>("icon-color", expression);
+ }
+
/**
* The color of the icon. This can only be used with sdf icons.
@@ -492,6 +701,7 @@ public class PropertyFactory {
* @param function a wrapper function for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, String>> iconColor(Function<T, String> function) {
return new PaintPropertyValue<>("icon-color", function);
}
@@ -516,6 +726,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("icon-halo-color", value);
}
+ /**
+ * The color of the icon's halo. Icon halos can only be used with SDF icons.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> iconHaloColor(Expression expression) {
+ return new PaintPropertyValue<>("icon-halo-color", expression);
+ }
+
/**
* The color of the icon's halo. Icon halos can only be used with SDF icons.
@@ -524,6 +744,7 @@ public class PropertyFactory {
* @param function a wrapper function for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, String>> iconHaloColor(Function<T, String> function) {
return new PaintPropertyValue<>("icon-halo-color", function);
}
@@ -538,6 +759,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("icon-halo-width", value);
}
+ /**
+ * Distance of halo to the icon outline.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> iconHaloWidth(Expression expression) {
+ return new PaintPropertyValue<>("icon-halo-width", expression);
+ }
+
/**
* Distance of halo to the icon outline.
@@ -546,6 +777,7 @@ public class PropertyFactory {
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, Float>> iconHaloWidth(Function<T, Float> function) {
return new PaintPropertyValue<>("icon-halo-width", function);
}
@@ -560,6 +792,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("icon-halo-blur", value);
}
+ /**
+ * Fade out the halo towards the outside.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> iconHaloBlur(Expression expression) {
+ return new PaintPropertyValue<>("icon-halo-blur", expression);
+ }
+
/**
* Fade out the halo towards the outside.
@@ -568,6 +810,7 @@ public class PropertyFactory {
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, Float>> iconHaloBlur(Function<T, Float> function) {
return new PaintPropertyValue<>("icon-halo-blur", function);
}
@@ -582,6 +825,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("icon-translate", value);
}
+ /**
+ * Distance that the icon's anchor is moved from its original placement. Positive values indicate right and down, while negative values indicate left and up.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> iconTranslate(Expression expression) {
+ return new PaintPropertyValue<>("icon-translate", expression);
+ }
+
/**
* Distance that the icon's anchor is moved from its original placement. Positive values indicate right and down, while negative values indicate left and up.
@@ -590,12 +843,13 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for Float[]
* @return property wrapper around a Float[] function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, Float[]>> iconTranslate(CameraFunction<Z, Float[]> function) {
return new PaintPropertyValue<>("icon-translate", function);
}
/**
- * Controls the translation reference point.
+ * Controls the frame of reference for {@link PropertyFactory#iconTranslate}.
*
* @param value a String value
* @return property wrapper around String
@@ -604,14 +858,25 @@ public class PropertyFactory {
return new PaintPropertyValue<>("icon-translate-anchor", value);
}
+ /**
+ * Controls the frame of reference for {@link PropertyFactory#iconTranslate}.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> iconTranslateAnchor(Expression expression) {
+ return new PaintPropertyValue<>("icon-translate-anchor", expression);
+ }
+
/**
- * Controls the translation reference point.
+ * Controls the frame of reference for {@link PropertyFactory#iconTranslate}.
*
* @param <Z> the zoom parameter type
* @param function a wrapper {@link CameraFunction} for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> iconTranslateAnchor(CameraFunction<Z, String> function) {
return new PaintPropertyValue<>("icon-translate-anchor", function);
}
@@ -626,6 +891,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("text-opacity", value);
}
+ /**
+ * The opacity at which the text will be drawn.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> textOpacity(Expression expression) {
+ return new PaintPropertyValue<>("text-opacity", expression);
+ }
+
/**
* The opacity at which the text will be drawn.
@@ -634,6 +909,7 @@ public class PropertyFactory {
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, Float>> textOpacity(Function<T, Float> function) {
return new PaintPropertyValue<>("text-opacity", function);
}
@@ -658,6 +934,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("text-color", value);
}
+ /**
+ * The color with which the text will be drawn.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> textColor(Expression expression) {
+ return new PaintPropertyValue<>("text-color", expression);
+ }
+
/**
* The color with which the text will be drawn.
@@ -666,6 +952,7 @@ public class PropertyFactory {
* @param function a wrapper function for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, String>> textColor(Function<T, String> function) {
return new PaintPropertyValue<>("text-color", function);
}
@@ -690,6 +977,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("text-halo-color", value);
}
+ /**
+ * The color of the text's halo, which helps it stand out from backgrounds.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> textHaloColor(Expression expression) {
+ return new PaintPropertyValue<>("text-halo-color", expression);
+ }
+
/**
* The color of the text's halo, which helps it stand out from backgrounds.
@@ -698,6 +995,7 @@ public class PropertyFactory {
* @param function a wrapper function for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, String>> textHaloColor(Function<T, String> function) {
return new PaintPropertyValue<>("text-halo-color", function);
}
@@ -712,6 +1010,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("text-halo-width", value);
}
+ /**
+ * Distance of halo to the font outline. Max text halo width is 1/4 of the font-size.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> textHaloWidth(Expression expression) {
+ return new PaintPropertyValue<>("text-halo-width", expression);
+ }
+
/**
* Distance of halo to the font outline. Max text halo width is 1/4 of the font-size.
@@ -720,6 +1028,7 @@ public class PropertyFactory {
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, Float>> textHaloWidth(Function<T, Float> function) {
return new PaintPropertyValue<>("text-halo-width", function);
}
@@ -734,6 +1043,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("text-halo-blur", value);
}
+ /**
+ * The halo's fadeout distance towards the outside.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> textHaloBlur(Expression expression) {
+ return new PaintPropertyValue<>("text-halo-blur", expression);
+ }
+
/**
* The halo's fadeout distance towards the outside.
@@ -742,6 +1061,7 @@ public class PropertyFactory {
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, Float>> textHaloBlur(Function<T, Float> function) {
return new PaintPropertyValue<>("text-halo-blur", function);
}
@@ -756,6 +1076,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("text-translate", value);
}
+ /**
+ * Distance that the text's anchor is moved from its original placement. Positive values indicate right and down, while negative values indicate left and up.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> textTranslate(Expression expression) {
+ return new PaintPropertyValue<>("text-translate", expression);
+ }
+
/**
* Distance that the text's anchor is moved from its original placement. Positive values indicate right and down, while negative values indicate left and up.
@@ -764,12 +1094,13 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for Float[]
* @return property wrapper around a Float[] function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, Float[]>> textTranslate(CameraFunction<Z, Float[]> function) {
return new PaintPropertyValue<>("text-translate", function);
}
/**
- * Controls the translation reference point.
+ * Controls the frame of reference for {@link PropertyFactory#textTranslate}.
*
* @param value a String value
* @return property wrapper around String
@@ -778,14 +1109,25 @@ public class PropertyFactory {
return new PaintPropertyValue<>("text-translate-anchor", value);
}
+ /**
+ * Controls the frame of reference for {@link PropertyFactory#textTranslate}.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> textTranslateAnchor(Expression expression) {
+ return new PaintPropertyValue<>("text-translate-anchor", expression);
+ }
+
/**
- * Controls the translation reference point.
+ * Controls the frame of reference for {@link PropertyFactory#textTranslate}.
*
* @param <Z> the zoom parameter type
* @param function a wrapper {@link CameraFunction} for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> textTranslateAnchor(CameraFunction<Z, String> function) {
return new PaintPropertyValue<>("text-translate-anchor", function);
}
@@ -800,6 +1142,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("circle-radius", value);
}
+ /**
+ * Circle radius.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> circleRadius(Expression expression) {
+ return new PaintPropertyValue<>("circle-radius", expression);
+ }
+
/**
* Circle radius.
@@ -808,6 +1160,7 @@ public class PropertyFactory {
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, Float>> circleRadius(Function<T, Float> function) {
return new PaintPropertyValue<>("circle-radius", function);
}
@@ -832,6 +1185,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("circle-color", value);
}
+ /**
+ * The fill color of the circle.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> circleColor(Expression expression) {
+ return new PaintPropertyValue<>("circle-color", expression);
+ }
+
/**
* The fill color of the circle.
@@ -840,6 +1203,7 @@ public class PropertyFactory {
* @param function a wrapper function for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, String>> circleColor(Function<T, String> function) {
return new PaintPropertyValue<>("circle-color", function);
}
@@ -854,6 +1218,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("circle-blur", value);
}
+ /**
+ * Amount to blur the circle. 1 blurs the circle such that only the centerpoint is full opacity.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> circleBlur(Expression expression) {
+ return new PaintPropertyValue<>("circle-blur", expression);
+ }
+
/**
* Amount to blur the circle. 1 blurs the circle such that only the centerpoint is full opacity.
@@ -862,6 +1236,7 @@ public class PropertyFactory {
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, Float>> circleBlur(Function<T, Float> function) {
return new PaintPropertyValue<>("circle-blur", function);
}
@@ -876,6 +1251,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("circle-opacity", value);
}
+ /**
+ * The opacity at which the circle will be drawn.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> circleOpacity(Expression expression) {
+ return new PaintPropertyValue<>("circle-opacity", expression);
+ }
+
/**
* The opacity at which the circle will be drawn.
@@ -884,6 +1269,7 @@ public class PropertyFactory {
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, Float>> circleOpacity(Function<T, Float> function) {
return new PaintPropertyValue<>("circle-opacity", function);
}
@@ -898,6 +1284,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("circle-translate", value);
}
+ /**
+ * The geometry's offset. Values are [x, y] where negatives indicate left and up, respectively.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> circleTranslate(Expression expression) {
+ return new PaintPropertyValue<>("circle-translate", expression);
+ }
+
/**
* The geometry's offset. Values are [x, y] where negatives indicate left and up, respectively.
@@ -906,12 +1302,13 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for Float[]
* @return property wrapper around a Float[] function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, Float[]>> circleTranslate(CameraFunction<Z, Float[]> function) {
return new PaintPropertyValue<>("circle-translate", function);
}
/**
- * Controls the translation reference point.
+ * Controls the frame of reference for {@link PropertyFactory#circleTranslate}.
*
* @param value a String value
* @return property wrapper around String
@@ -920,14 +1317,25 @@ public class PropertyFactory {
return new PaintPropertyValue<>("circle-translate-anchor", value);
}
+ /**
+ * Controls the frame of reference for {@link PropertyFactory#circleTranslate}.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> circleTranslateAnchor(Expression expression) {
+ return new PaintPropertyValue<>("circle-translate-anchor", expression);
+ }
+
/**
- * Controls the translation reference point.
+ * Controls the frame of reference for {@link PropertyFactory#circleTranslate}.
*
* @param <Z> the zoom parameter type
* @param function a wrapper {@link CameraFunction} for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> circleTranslateAnchor(CameraFunction<Z, String> function) {
return new PaintPropertyValue<>("circle-translate-anchor", function);
}
@@ -942,6 +1350,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("circle-pitch-scale", value);
}
+ /**
+ * Controls the scaling behavior of the circle when the map is pitched.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> circlePitchScale(Expression expression) {
+ return new PaintPropertyValue<>("circle-pitch-scale", expression);
+ }
+
/**
* Controls the scaling behavior of the circle when the map is pitched.
@@ -950,6 +1368,7 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> circlePitchScale(CameraFunction<Z, String> function) {
return new PaintPropertyValue<>("circle-pitch-scale", function);
}
@@ -964,6 +1383,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("circle-pitch-alignment", value);
}
+ /**
+ * Orientation of circle when map is pitched.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> circlePitchAlignment(Expression expression) {
+ return new PaintPropertyValue<>("circle-pitch-alignment", expression);
+ }
+
/**
* Orientation of circle when map is pitched.
@@ -972,6 +1401,7 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> circlePitchAlignment(CameraFunction<Z, String> function) {
return new PaintPropertyValue<>("circle-pitch-alignment", function);
}
@@ -986,6 +1416,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("circle-stroke-width", value);
}
+ /**
+ * The width of the circle's stroke. Strokes are placed outside of the {@link PropertyFactory#circleRadius}.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> circleStrokeWidth(Expression expression) {
+ return new PaintPropertyValue<>("circle-stroke-width", expression);
+ }
+
/**
* The width of the circle's stroke. Strokes are placed outside of the {@link PropertyFactory#circleRadius}.
@@ -994,6 +1434,7 @@ public class PropertyFactory {
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, Float>> circleStrokeWidth(Function<T, Float> function) {
return new PaintPropertyValue<>("circle-stroke-width", function);
}
@@ -1018,6 +1459,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("circle-stroke-color", value);
}
+ /**
+ * The stroke color of the circle.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> circleStrokeColor(Expression expression) {
+ return new PaintPropertyValue<>("circle-stroke-color", expression);
+ }
+
/**
* The stroke color of the circle.
@@ -1026,6 +1477,7 @@ public class PropertyFactory {
* @param function a wrapper function for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, String>> circleStrokeColor(Function<T, String> function) {
return new PaintPropertyValue<>("circle-stroke-color", function);
}
@@ -1040,6 +1492,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("circle-stroke-opacity", value);
}
+ /**
+ * The opacity of the circle's stroke.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> circleStrokeOpacity(Expression expression) {
+ return new PaintPropertyValue<>("circle-stroke-opacity", expression);
+ }
+
/**
* The opacity of the circle's stroke.
@@ -1048,11 +1510,144 @@ public class PropertyFactory {
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, Float>> circleStrokeOpacity(Function<T, Float> function) {
return new PaintPropertyValue<>("circle-stroke-opacity", function);
}
/**
+ * Radius of influence of one heatmap point in density-independent pixels. Increasing the value makes the heatmap smoother, but less detailed.
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Float> heatmapRadius(Float value) {
+ return new PaintPropertyValue<>("heatmap-radius", value);
+ }
+
+ /**
+ * Radius of influence of one heatmap point in density-independent pixels. Increasing the value makes the heatmap smoother, but less detailed.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> heatmapRadius(Expression expression) {
+ return new PaintPropertyValue<>("heatmap-radius", expression);
+ }
+
+
+ /**
+ * Radius of influence of one heatmap point in density-independent pixels. Increasing the value makes the heatmap smoother, but less detailed.
+ *
+ * @param <T> the function input type
+ * @param function a wrapper function for Float
+ * @return property wrapper around a Float function
+ */
+ @Deprecated
+ public static <T> PropertyValue<Function<T, Float>> heatmapRadius(Function<T, Float> function) {
+ return new PaintPropertyValue<>("heatmap-radius", function);
+ }
+
+ /**
+ * A measure of how much an individual point contributes to the heatmap. A value of 10 would be equivalent to having 10 points of weight 1 in the same spot. Especially useful when combined with clustering.
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Float> heatmapWeight(Float value) {
+ return new PaintPropertyValue<>("heatmap-weight", value);
+ }
+
+ /**
+ * A measure of how much an individual point contributes to the heatmap. A value of 10 would be equivalent to having 10 points of weight 1 in the same spot. Especially useful when combined with clustering.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> heatmapWeight(Expression expression) {
+ return new PaintPropertyValue<>("heatmap-weight", expression);
+ }
+
+
+ /**
+ * A measure of how much an individual point contributes to the heatmap. A value of 10 would be equivalent to having 10 points of weight 1 in the same spot. Especially useful when combined with clustering.
+ *
+ * @param <T> the function input type
+ * @param function a wrapper function for Float
+ * @return property wrapper around a Float function
+ */
+ @Deprecated
+ public static <T> PropertyValue<Function<T, Float>> heatmapWeight(Function<T, Float> function) {
+ return new PaintPropertyValue<>("heatmap-weight", function);
+ }
+
+ /**
+ * Similar to {@link PropertyFactory#heatmapWeight} but controls the intensity of the heatmap globally. Primarily used for adjusting the heatmap based on zoom level.
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Float> heatmapIntensity(Float value) {
+ return new PaintPropertyValue<>("heatmap-intensity", value);
+ }
+
+ /**
+ * Similar to {@link PropertyFactory#heatmapWeight} but controls the intensity of the heatmap globally. Primarily used for adjusting the heatmap based on zoom level.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> heatmapIntensity(Expression expression) {
+ return new PaintPropertyValue<>("heatmap-intensity", expression);
+ }
+
+
+ /**
+ * Similar to {@link PropertyFactory#heatmapWeight} but controls the intensity of the heatmap globally. Primarily used for adjusting the heatmap based on zoom level.
+ *
+ * @param <Z> the zoom parameter type
+ * @param function a wrapper {@link CameraFunction} for Float
+ * @return property wrapper around a Float function
+ */
+ @Deprecated
+ public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> heatmapIntensity(CameraFunction<Z, Float> function) {
+ return new PaintPropertyValue<>("heatmap-intensity", function);
+ }
+
+ /**
+ * The global opacity at which the heatmap layer will be drawn.
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Float> heatmapOpacity(Float value) {
+ return new PaintPropertyValue<>("heatmap-opacity", value);
+ }
+
+ /**
+ * The global opacity at which the heatmap layer will be drawn.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> heatmapOpacity(Expression expression) {
+ return new PaintPropertyValue<>("heatmap-opacity", expression);
+ }
+
+
+ /**
+ * The global opacity at which the heatmap layer will be drawn.
+ *
+ * @param <Z> the zoom parameter type
+ * @param function a wrapper {@link CameraFunction} for Float
+ * @return property wrapper around a Float function
+ */
+ @Deprecated
+ public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> heatmapOpacity(CameraFunction<Z, Float> function) {
+ return new PaintPropertyValue<>("heatmap-opacity", function);
+ }
+
+ /**
* The opacity of the entire fill extrusion layer. This is rendered on a per-layer, not per-feature, basis, and data-driven styling is not available.
*
* @param value a Float value
@@ -1062,6 +1657,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("fill-extrusion-opacity", value);
}
+ /**
+ * The opacity of the entire fill extrusion layer. This is rendered on a per-layer, not per-feature, basis, and data-driven styling is not available.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> fillExtrusionOpacity(Expression expression) {
+ return new PaintPropertyValue<>("fill-extrusion-opacity", expression);
+ }
+
/**
* The opacity of the entire fill extrusion layer. This is rendered on a per-layer, not per-feature, basis, and data-driven styling is not available.
@@ -1070,6 +1675,7 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> fillExtrusionOpacity(CameraFunction<Z, Float> function) {
return new PaintPropertyValue<>("fill-extrusion-opacity", function);
}
@@ -1094,6 +1700,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("fill-extrusion-color", value);
}
+ /**
+ * The base color of the extruded fill. The extrusion's surfaces will be shaded differently based on this color in combination with the root `light` settings. If this color is specified as `rgba` with an alpha component, the alpha component will be ignored; use {@link PropertyFactory#fillExtrusionOpacity} to set layer opacity.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> fillExtrusionColor(Expression expression) {
+ return new PaintPropertyValue<>("fill-extrusion-color", expression);
+ }
+
/**
* The base color of the extruded fill. The extrusion's surfaces will be shaded differently based on this color in combination with the root `light` settings. If this color is specified as `rgba` with an alpha component, the alpha component will be ignored; use {@link PropertyFactory#fillExtrusionOpacity} to set layer opacity.
@@ -1102,6 +1718,7 @@ public class PropertyFactory {
* @param function a wrapper function for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, String>> fillExtrusionColor(Function<T, String> function) {
return new PaintPropertyValue<>("fill-extrusion-color", function);
}
@@ -1116,6 +1733,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("fill-extrusion-translate", value);
}
+ /**
+ * The geometry's offset. Values are [x, y] where negatives indicate left and up (on the flat plane), respectively.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> fillExtrusionTranslate(Expression expression) {
+ return new PaintPropertyValue<>("fill-extrusion-translate", expression);
+ }
+
/**
* The geometry's offset. Values are [x, y] where negatives indicate left and up (on the flat plane), respectively.
@@ -1124,12 +1751,13 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for Float[]
* @return property wrapper around a Float[] function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, Float[]>> fillExtrusionTranslate(CameraFunction<Z, Float[]> function) {
return new PaintPropertyValue<>("fill-extrusion-translate", function);
}
/**
- * Controls the translation reference point.
+ * Controls the frame of reference for {@link PropertyFactory#fillExtrusionTranslate}.
*
* @param value a String value
* @return property wrapper around String
@@ -1138,14 +1766,25 @@ public class PropertyFactory {
return new PaintPropertyValue<>("fill-extrusion-translate-anchor", value);
}
+ /**
+ * Controls the frame of reference for {@link PropertyFactory#fillExtrusionTranslate}.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> fillExtrusionTranslateAnchor(Expression expression) {
+ return new PaintPropertyValue<>("fill-extrusion-translate-anchor", expression);
+ }
+
/**
- * Controls the translation reference point.
+ * Controls the frame of reference for {@link PropertyFactory#fillExtrusionTranslate}.
*
* @param <Z> the zoom parameter type
* @param function a wrapper {@link CameraFunction} for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> fillExtrusionTranslateAnchor(CameraFunction<Z, String> function) {
return new PaintPropertyValue<>("fill-extrusion-translate-anchor", function);
}
@@ -1160,6 +1799,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("fill-extrusion-pattern", value);
}
+ /**
+ * Name of image in sprite to use for drawing images on extruded fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512).
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> fillExtrusionPattern(Expression expression) {
+ return new PaintPropertyValue<>("fill-extrusion-pattern", expression);
+ }
+
/**
* Name of image in sprite to use for drawing images on extruded fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512).
@@ -1168,6 +1817,7 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> fillExtrusionPattern(CameraFunction<Z, String> function) {
return new PaintPropertyValue<>("fill-extrusion-pattern", function);
}
@@ -1182,6 +1832,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("fill-extrusion-height", value);
}
+ /**
+ * The height with which to extrude this layer.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> fillExtrusionHeight(Expression expression) {
+ return new PaintPropertyValue<>("fill-extrusion-height", expression);
+ }
+
/**
* The height with which to extrude this layer.
@@ -1190,6 +1850,7 @@ public class PropertyFactory {
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, Float>> fillExtrusionHeight(Function<T, Float> function) {
return new PaintPropertyValue<>("fill-extrusion-height", function);
}
@@ -1204,6 +1865,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("fill-extrusion-base", value);
}
+ /**
+ * The height with which to extrude the base of this layer. Must be less than or equal to {@link PropertyFactory#fillExtrusionHeight}.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> fillExtrusionBase(Expression expression) {
+ return new PaintPropertyValue<>("fill-extrusion-base", expression);
+ }
+
/**
* The height with which to extrude the base of this layer. Must be less than or equal to {@link PropertyFactory#fillExtrusionHeight}.
@@ -1212,6 +1883,7 @@ public class PropertyFactory {
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, Float>> fillExtrusionBase(Function<T, Float> function) {
return new PaintPropertyValue<>("fill-extrusion-base", function);
}
@@ -1226,6 +1898,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("raster-opacity", value);
}
+ /**
+ * The opacity at which the image will be drawn.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> rasterOpacity(Expression expression) {
+ return new PaintPropertyValue<>("raster-opacity", expression);
+ }
+
/**
* The opacity at which the image will be drawn.
@@ -1234,6 +1916,7 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> rasterOpacity(CameraFunction<Z, Float> function) {
return new PaintPropertyValue<>("raster-opacity", function);
}
@@ -1248,6 +1931,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("raster-hue-rotate", value);
}
+ /**
+ * Rotates hues around the color wheel.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> rasterHueRotate(Expression expression) {
+ return new PaintPropertyValue<>("raster-hue-rotate", expression);
+ }
+
/**
* Rotates hues around the color wheel.
@@ -1256,6 +1949,7 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> rasterHueRotate(CameraFunction<Z, Float> function) {
return new PaintPropertyValue<>("raster-hue-rotate", function);
}
@@ -1270,6 +1964,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("raster-brightness-min", value);
}
+ /**
+ * Increase or reduce the brightness of the image. The value is the minimum brightness.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> rasterBrightnessMin(Expression expression) {
+ return new PaintPropertyValue<>("raster-brightness-min", expression);
+ }
+
/**
* Increase or reduce the brightness of the image. The value is the minimum brightness.
@@ -1278,6 +1982,7 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> rasterBrightnessMin(CameraFunction<Z, Float> function) {
return new PaintPropertyValue<>("raster-brightness-min", function);
}
@@ -1292,6 +1997,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("raster-brightness-max", value);
}
+ /**
+ * Increase or reduce the brightness of the image. The value is the maximum brightness.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> rasterBrightnessMax(Expression expression) {
+ return new PaintPropertyValue<>("raster-brightness-max", expression);
+ }
+
/**
* Increase or reduce the brightness of the image. The value is the maximum brightness.
@@ -1300,6 +2015,7 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> rasterBrightnessMax(CameraFunction<Z, Float> function) {
return new PaintPropertyValue<>("raster-brightness-max", function);
}
@@ -1314,6 +2030,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("raster-saturation", value);
}
+ /**
+ * Increase or reduce the saturation of the image.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> rasterSaturation(Expression expression) {
+ return new PaintPropertyValue<>("raster-saturation", expression);
+ }
+
/**
* Increase or reduce the saturation of the image.
@@ -1322,6 +2048,7 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> rasterSaturation(CameraFunction<Z, Float> function) {
return new PaintPropertyValue<>("raster-saturation", function);
}
@@ -1336,6 +2063,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("raster-contrast", value);
}
+ /**
+ * Increase or reduce the contrast of the image.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> rasterContrast(Expression expression) {
+ return new PaintPropertyValue<>("raster-contrast", expression);
+ }
+
/**
* Increase or reduce the contrast of the image.
@@ -1344,6 +2081,7 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> rasterContrast(CameraFunction<Z, Float> function) {
return new PaintPropertyValue<>("raster-contrast", function);
}
@@ -1358,6 +2096,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("raster-fade-duration", value);
}
+ /**
+ * Fade duration when a new tile is added.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> rasterFadeDuration(Expression expression) {
+ return new PaintPropertyValue<>("raster-fade-duration", expression);
+ }
+
/**
* Fade duration when a new tile is added.
@@ -1366,11 +2114,240 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> rasterFadeDuration(CameraFunction<Z, Float> function) {
return new PaintPropertyValue<>("raster-fade-duration", function);
}
/**
+ * The direction of the light source used to generate the hillshading with 0 as the top of the viewport if {@link Property.HILLSHADE_ILLUMINATION_ANCHOR} is set to `viewport` and due north if {@link Property.HILLSHADE_ILLUMINATION_ANCHOR} is set to `map`.
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Float> hillshadeIlluminationDirection(Float value) {
+ return new PaintPropertyValue<>("hillshade-illumination-direction", value);
+ }
+
+ /**
+ * The direction of the light source used to generate the hillshading with 0 as the top of the viewport if {@link Property.HILLSHADE_ILLUMINATION_ANCHOR} is set to `viewport` and due north if {@link Property.HILLSHADE_ILLUMINATION_ANCHOR} is set to `map`.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> hillshadeIlluminationDirection(Expression expression) {
+ return new PaintPropertyValue<>("hillshade-illumination-direction", expression);
+ }
+
+
+ /**
+ * The direction of the light source used to generate the hillshading with 0 as the top of the viewport if {@link Property.HILLSHADE_ILLUMINATION_ANCHOR} is set to `viewport` and due north if {@link Property.HILLSHADE_ILLUMINATION_ANCHOR} is set to `map`.
+ *
+ * @param <Z> the zoom parameter type
+ * @param function a wrapper {@link CameraFunction} for Float
+ * @return property wrapper around a Float function
+ */
+ @Deprecated
+ public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> hillshadeIlluminationDirection(CameraFunction<Z, Float> function) {
+ return new PaintPropertyValue<>("hillshade-illumination-direction", function);
+ }
+
+ /**
+ * Direction of light source when map is rotated.
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<String> hillshadeIlluminationAnchor(@Property.HILLSHADE_ILLUMINATION_ANCHOR String value) {
+ return new PaintPropertyValue<>("hillshade-illumination-anchor", value);
+ }
+
+ /**
+ * Direction of light source when map is rotated.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> hillshadeIlluminationAnchor(Expression expression) {
+ return new PaintPropertyValue<>("hillshade-illumination-anchor", expression);
+ }
+
+
+ /**
+ * Direction of light source when map is rotated.
+ *
+ * @param <Z> the zoom parameter type
+ * @param function a wrapper {@link CameraFunction} for String
+ * @return property wrapper around a String function
+ */
+ @Deprecated
+ public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> hillshadeIlluminationAnchor(CameraFunction<Z, String> function) {
+ return new PaintPropertyValue<>("hillshade-illumination-anchor", function);
+ }
+
+ /**
+ * Intensity of the hillshade
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Float> hillshadeExaggeration(Float value) {
+ return new PaintPropertyValue<>("hillshade-exaggeration", value);
+ }
+
+ /**
+ * Intensity of the hillshade
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> hillshadeExaggeration(Expression expression) {
+ return new PaintPropertyValue<>("hillshade-exaggeration", expression);
+ }
+
+
+ /**
+ * Intensity of the hillshade
+ *
+ * @param <Z> the zoom parameter type
+ * @param function a wrapper {@link CameraFunction} for Float
+ * @return property wrapper around a Float function
+ */
+ @Deprecated
+ public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> hillshadeExaggeration(CameraFunction<Z, Float> function) {
+ return new PaintPropertyValue<>("hillshade-exaggeration", function);
+ }
+
+ /**
+ * The shading color of areas that face away from the light source.
+ *
+ * @param value a int color value
+ * @return property wrapper around String color
+ */
+ public static PropertyValue<String> hillshadeShadowColor(@ColorInt int value) {
+ return new PaintPropertyValue<>("hillshade-shadow-color", colorToRgbaString(value));
+ }
+
+ /**
+ * The shading color of areas that face away from the light source.
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<String> hillshadeShadowColor(String value) {
+ return new PaintPropertyValue<>("hillshade-shadow-color", value);
+ }
+
+ /**
+ * The shading color of areas that face away from the light source.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> hillshadeShadowColor(Expression expression) {
+ return new PaintPropertyValue<>("hillshade-shadow-color", expression);
+ }
+
+
+ /**
+ * The shading color of areas that face away from the light source.
+ *
+ * @param <Z> the zoom parameter type
+ * @param function a wrapper {@link CameraFunction} for String
+ * @return property wrapper around a String function
+ */
+ @Deprecated
+ public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> hillshadeShadowColor(CameraFunction<Z, String> function) {
+ return new PaintPropertyValue<>("hillshade-shadow-color", function);
+ }
+
+ /**
+ * The shading color of areas that faces towards the light source.
+ *
+ * @param value a int color value
+ * @return property wrapper around String color
+ */
+ public static PropertyValue<String> hillshadeHighlightColor(@ColorInt int value) {
+ return new PaintPropertyValue<>("hillshade-highlight-color", colorToRgbaString(value));
+ }
+
+ /**
+ * The shading color of areas that faces towards the light source.
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<String> hillshadeHighlightColor(String value) {
+ return new PaintPropertyValue<>("hillshade-highlight-color", value);
+ }
+
+ /**
+ * The shading color of areas that faces towards the light source.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> hillshadeHighlightColor(Expression expression) {
+ return new PaintPropertyValue<>("hillshade-highlight-color", expression);
+ }
+
+
+ /**
+ * The shading color of areas that faces towards the light source.
+ *
+ * @param <Z> the zoom parameter type
+ * @param function a wrapper {@link CameraFunction} for String
+ * @return property wrapper around a String function
+ */
+ @Deprecated
+ public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> hillshadeHighlightColor(CameraFunction<Z, String> function) {
+ return new PaintPropertyValue<>("hillshade-highlight-color", function);
+ }
+
+ /**
+ * The shading color used to accentuate rugged terrain like sharp cliffs and gorges.
+ *
+ * @param value a int color value
+ * @return property wrapper around String color
+ */
+ public static PropertyValue<String> hillshadeAccentColor(@ColorInt int value) {
+ return new PaintPropertyValue<>("hillshade-accent-color", colorToRgbaString(value));
+ }
+
+ /**
+ * The shading color used to accentuate rugged terrain like sharp cliffs and gorges.
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<String> hillshadeAccentColor(String value) {
+ return new PaintPropertyValue<>("hillshade-accent-color", value);
+ }
+
+ /**
+ * The shading color used to accentuate rugged terrain like sharp cliffs and gorges.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> hillshadeAccentColor(Expression expression) {
+ return new PaintPropertyValue<>("hillshade-accent-color", expression);
+ }
+
+
+ /**
+ * The shading color used to accentuate rugged terrain like sharp cliffs and gorges.
+ *
+ * @param <Z> the zoom parameter type
+ * @param function a wrapper {@link CameraFunction} for String
+ * @return property wrapper around a String function
+ */
+ @Deprecated
+ public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> hillshadeAccentColor(CameraFunction<Z, String> function) {
+ return new PaintPropertyValue<>("hillshade-accent-color", function);
+ }
+
+ /**
* The color with which the background will be drawn.
*
* @param value a int color value
@@ -1390,6 +2367,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("background-color", value);
}
+ /**
+ * The color with which the background will be drawn.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> backgroundColor(Expression expression) {
+ return new PaintPropertyValue<>("background-color", expression);
+ }
+
/**
* The color with which the background will be drawn.
@@ -1398,6 +2385,7 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> backgroundColor(CameraFunction<Z, String> function) {
return new PaintPropertyValue<>("background-color", function);
}
@@ -1412,6 +2400,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("background-pattern", value);
}
+ /**
+ * Name of image in sprite to use for drawing an image background. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512).
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> backgroundPattern(Expression expression) {
+ return new PaintPropertyValue<>("background-pattern", expression);
+ }
+
/**
* Name of image in sprite to use for drawing an image background. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512).
@@ -1420,6 +2418,7 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> backgroundPattern(CameraFunction<Z, String> function) {
return new PaintPropertyValue<>("background-pattern", function);
}
@@ -1434,6 +2433,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("background-opacity", value);
}
+ /**
+ * The opacity at which the background will be drawn.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> backgroundOpacity(Expression expression) {
+ return new PaintPropertyValue<>("background-opacity", expression);
+ }
+
/**
* The opacity at which the background will be drawn.
@@ -1442,6 +2451,7 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> backgroundOpacity(CameraFunction<Z, Float> function) {
return new PaintPropertyValue<>("background-opacity", function);
}
@@ -1456,6 +2466,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("line-cap", value);
}
+ /**
+ * The display of line endings.
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<Expression> lineCap(Expression value) {
+ return new LayoutPropertyValue<>("line-cap", value);
+ }
/**
@@ -1479,6 +2498,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("line-join", value);
}
+ /**
+ * The display of lines when joining.
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<Expression> lineJoin(Expression value) {
+ return new LayoutPropertyValue<>("line-join", value);
+ }
/**
@@ -1502,6 +2530,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("line-miter-limit", value);
}
+ /**
+ * Used to automatically convert miter joins to bevel joins for sharp angles.
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Expression> lineMiterLimit(Expression value) {
+ return new LayoutPropertyValue<>("line-miter-limit", value);
+ }
/**
@@ -1525,6 +2562,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("line-round-limit", value);
}
+ /**
+ * Used to automatically convert round joins to miter joins for shallow angles.
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Expression> lineRoundLimit(Expression value) {
+ return new LayoutPropertyValue<>("line-round-limit", value);
+ }
/**
@@ -1548,6 +2594,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("symbol-placement", value);
}
+ /**
+ * Label placement relative to its geometry.
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<Expression> symbolPlacement(Expression value) {
+ return new LayoutPropertyValue<>("symbol-placement", value);
+ }
/**
@@ -1571,6 +2626,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("symbol-spacing", value);
}
+ /**
+ * Distance between two symbol anchors.
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Expression> symbolSpacing(Expression value) {
+ return new LayoutPropertyValue<>("symbol-spacing", value);
+ }
/**
@@ -1594,6 +2658,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("symbol-avoid-edges", value);
}
+ /**
+ * If true, the symbols will not cross tile edges to avoid mutual collisions. Recommended in layers that don't have enough padding in the vector tile to prevent collisions, or if it is a point symbol layer placed after a line symbol layer.
+ *
+ * @param value a Boolean value
+ * @return property wrapper around Boolean
+ */
+ public static PropertyValue<Expression> symbolAvoidEdges(Expression value) {
+ return new LayoutPropertyValue<>("symbol-avoid-edges", value);
+ }
/**
@@ -1617,6 +2690,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("icon-allow-overlap", value);
}
+ /**
+ * If true, the icon will be visible even if it collides with other previously drawn symbols.
+ *
+ * @param value a Boolean value
+ * @return property wrapper around Boolean
+ */
+ public static PropertyValue<Expression> iconAllowOverlap(Expression value) {
+ return new LayoutPropertyValue<>("icon-allow-overlap", value);
+ }
/**
@@ -1640,6 +2722,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("icon-ignore-placement", value);
}
+ /**
+ * If true, other symbols can be visible even if they collide with the icon.
+ *
+ * @param value a Boolean value
+ * @return property wrapper around Boolean
+ */
+ public static PropertyValue<Expression> iconIgnorePlacement(Expression value) {
+ return new LayoutPropertyValue<>("icon-ignore-placement", value);
+ }
/**
@@ -1663,6 +2754,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("icon-optional", value);
}
+ /**
+ * If true, text will display without their corresponding icons when the icon collides with other symbols and the text does not.
+ *
+ * @param value a Boolean value
+ * @return property wrapper around Boolean
+ */
+ public static PropertyValue<Expression> iconOptional(Expression value) {
+ return new LayoutPropertyValue<>("icon-optional", value);
+ }
/**
@@ -1686,6 +2786,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("icon-rotation-alignment", value);
}
+ /**
+ * In combination with {@link Property.SYMBOL_PLACEMENT}, determines the rotation behavior of icons.
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<Expression> iconRotationAlignment(Expression value) {
+ return new LayoutPropertyValue<>("icon-rotation-alignment", value);
+ }
/**
@@ -1709,6 +2818,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("icon-size", value);
}
+ /**
+ * Scales the original size of the icon by the provided factor. The new pixel size of the image will be the original pixel size multiplied by {@link PropertyFactory#iconSize}. 1 is the original size; 3 triples the size of the image.
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Expression> iconSize(Expression value) {
+ return new LayoutPropertyValue<>("icon-size", value);
+ }
/**
@@ -1732,6 +2850,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("icon-text-fit", value);
}
+ /**
+ * Scales the icon to fit around the associated text.
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<Expression> iconTextFit(Expression value) {
+ return new LayoutPropertyValue<>("icon-text-fit", value);
+ }
/**
@@ -1755,6 +2882,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("icon-text-fit-padding", value);
}
+ /**
+ * Size of the additional area added to dimensions determined by {@link Property.ICON_TEXT_FIT}, in clockwise order: top, right, bottom, left.
+ *
+ * @param value a Float[] value
+ * @return property wrapper around Float[]
+ */
+ public static PropertyValue<Expression> iconTextFitPadding(Expression value) {
+ return new LayoutPropertyValue<>("icon-text-fit-padding", value);
+ }
/**
@@ -1769,7 +2905,7 @@ public class PropertyFactory {
}
/**
- * Name of image in sprite to use for drawing an image background. A string with {tokens} replaced, referencing the data property to pull from.
+ * Name of image in sprite to use for drawing an image background. Within literal values and zoom functions, property names enclosed in curly brackets (e.g. `{token}`) are replaced with the value of the named property. Expressions and property functions do not support this syntax; for equivalent functionality in expressions, use the [`concat`](#expressions-concat) and [`get`](#expressions-get) operators.
*
* @param value a String value
* @return property wrapper around String
@@ -1778,10 +2914,19 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("icon-image", value);
}
+ /**
+ * Name of image in sprite to use for drawing an image background. Within literal values and zoom functions, property names enclosed in curly brackets (e.g. `{token}`) are replaced with the value of the named property. Expressions and property functions do not support this syntax; for equivalent functionality in expressions, use the [`concat`](#expressions-concat) and [`get`](#expressions-get) operators.
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<Expression> iconImage(Expression value) {
+ return new LayoutPropertyValue<>("icon-image", value);
+ }
/**
- * Name of image in sprite to use for drawing an image background. A string with {tokens} replaced, referencing the data property to pull from.
+ * Name of image in sprite to use for drawing an image background. Within literal values and zoom functions, property names enclosed in curly brackets (e.g. `{token}`) are replaced with the value of the named property. Expressions and property functions do not support this syntax; for equivalent functionality in expressions, use the [`concat`](#expressions-concat) and [`get`](#expressions-get) operators.
*
* @param <T> the function input type
* @param function a wrapper function for String
@@ -1801,6 +2946,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("icon-rotate", value);
}
+ /**
+ * Rotates the icon clockwise.
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Expression> iconRotate(Expression value) {
+ return new LayoutPropertyValue<>("icon-rotate", value);
+ }
/**
@@ -1824,6 +2978,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("icon-padding", value);
}
+ /**
+ * Size of the additional area around the icon bounding box used for detecting symbol collisions.
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Expression> iconPadding(Expression value) {
+ return new LayoutPropertyValue<>("icon-padding", value);
+ }
/**
@@ -1847,6 +3010,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("icon-keep-upright", value);
}
+ /**
+ * If true, the icon may be flipped to prevent it from being rendered upside-down.
+ *
+ * @param value a Boolean value
+ * @return property wrapper around Boolean
+ */
+ public static PropertyValue<Expression> iconKeepUpright(Expression value) {
+ return new LayoutPropertyValue<>("icon-keep-upright", value);
+ }
/**
@@ -1861,7 +3033,7 @@ public class PropertyFactory {
}
/**
- * Offset distance of icon from its anchor. Positive values indicate right and down, while negative values indicate left and up. When combined with {@link PropertyFactory#iconRotate} the offset will be as if the rotated direction was up.
+ * Offset distance of icon from its anchor. Positive values indicate right and down, while negative values indicate left and up. Each component is multiplied by the value of {@link PropertyFactory#iconSize} to obtain the final offset in density-independent pixels. When combined with {@link PropertyFactory#iconRotate} the offset will be as if the rotated direction was up.
*
* @param value a Float[] value
* @return property wrapper around Float[]
@@ -1870,10 +3042,19 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("icon-offset", value);
}
+ /**
+ * Offset distance of icon from its anchor. Positive values indicate right and down, while negative values indicate left and up. Each component is multiplied by the value of {@link PropertyFactory#iconSize} to obtain the final offset in density-independent pixels. When combined with {@link PropertyFactory#iconRotate} the offset will be as if the rotated direction was up.
+ *
+ * @param value a Float[] value
+ * @return property wrapper around Float[]
+ */
+ public static PropertyValue<Expression> iconOffset(Expression value) {
+ return new LayoutPropertyValue<>("icon-offset", value);
+ }
/**
- * Offset distance of icon from its anchor. Positive values indicate right and down, while negative values indicate left and up. When combined with {@link PropertyFactory#iconRotate} the offset will be as if the rotated direction was up.
+ * Offset distance of icon from its anchor. Positive values indicate right and down, while negative values indicate left and up. Each component is multiplied by the value of {@link PropertyFactory#iconSize} to obtain the final offset in density-independent pixels. When combined with {@link PropertyFactory#iconRotate} the offset will be as if the rotated direction was up.
*
* @param <T> the function input type
* @param function a wrapper function for Float[]
@@ -1893,6 +3074,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("icon-anchor", value);
}
+ /**
+ * Part of the icon placed closest to the anchor.
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<Expression> iconAnchor(Expression value) {
+ return new LayoutPropertyValue<>("icon-anchor", value);
+ }
/**
@@ -1916,6 +3106,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("icon-pitch-alignment", value);
}
+ /**
+ * Orientation of icon when map is pitched.
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<Expression> iconPitchAlignment(Expression value) {
+ return new LayoutPropertyValue<>("icon-pitch-alignment", value);
+ }
/**
@@ -1939,6 +3138,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-pitch-alignment", value);
}
+ /**
+ * Orientation of text when map is pitched.
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<Expression> textPitchAlignment(Expression value) {
+ return new LayoutPropertyValue<>("text-pitch-alignment", value);
+ }
/**
@@ -1962,6 +3170,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-rotation-alignment", value);
}
+ /**
+ * In combination with {@link Property.SYMBOL_PLACEMENT}, determines the rotation behavior of the individual glyphs forming the text.
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<Expression> textRotationAlignment(Expression value) {
+ return new LayoutPropertyValue<>("text-rotation-alignment", value);
+ }
/**
@@ -1976,7 +3193,7 @@ public class PropertyFactory {
}
/**
- * Value to use for a text label. Feature properties are specified using tokens like {field_name}. (Token replacement is only supported for literal {@link PropertyFactory#textField} values--not for property functions.)
+ * Value to use for a text label. Within literal values and zoom functions, property names enclosed in curly brackets (e.g. `{token}`) are replaced with the value of the named property. Expressions and property functions do not support this syntax; for equivalent functionality in expressions, use the [`concat`](#expressions-concat) and [`get`](#expressions-get) operators.
*
* @param value a String value
* @return property wrapper around String
@@ -1985,10 +3202,19 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-field", value);
}
+ /**
+ * Value to use for a text label. Within literal values and zoom functions, property names enclosed in curly brackets (e.g. `{token}`) are replaced with the value of the named property. Expressions and property functions do not support this syntax; for equivalent functionality in expressions, use the [`concat`](#expressions-concat) and [`get`](#expressions-get) operators.
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<Expression> textField(Expression value) {
+ return new LayoutPropertyValue<>("text-field", value);
+ }
/**
- * Value to use for a text label. Feature properties are specified using tokens like {field_name}. (Token replacement is only supported for literal {@link PropertyFactory#textField} values--not for property functions.)
+ * Value to use for a text label. Within literal values and zoom functions, property names enclosed in curly brackets (e.g. `{token}`) are replaced with the value of the named property. Expressions and property functions do not support this syntax; for equivalent functionality in expressions, use the [`concat`](#expressions-concat) and [`get`](#expressions-get) operators.
*
* @param <T> the function input type
* @param function a wrapper function for String
@@ -2008,16 +3234,25 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-font", value);
}
+ /**
+ * Font stack to use for displaying text.
+ *
+ * @param value a String[] value
+ * @return property wrapper around String[]
+ */
+ public static PropertyValue<Expression> textFont(Expression value) {
+ return new LayoutPropertyValue<>("text-font", value);
+ }
/**
* Font stack to use for displaying text.
*
- * @param <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for String[]
+ * @param <T> the function input type
+ * @param function a wrapper function for String[]
* @return property wrapper around a String[] function
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, String[]>> textFont(CameraFunction<Z, String[]> function) {
+ public static <T> PropertyValue<Function<T, String[]>> textFont(Function<T, String[]> function) {
return new LayoutPropertyValue<>("text-font", function);
}
@@ -2031,6 +3266,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-size", value);
}
+ /**
+ * Font size.
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Expression> textSize(Expression value) {
+ return new LayoutPropertyValue<>("text-size", value);
+ }
/**
@@ -2054,6 +3298,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-max-width", value);
}
+ /**
+ * The maximum line width for text wrapping.
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Expression> textMaxWidth(Expression value) {
+ return new LayoutPropertyValue<>("text-max-width", value);
+ }
/**
@@ -2077,6 +3330,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-line-height", value);
}
+ /**
+ * Text leading value for multi-line text.
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Expression> textLineHeight(Expression value) {
+ return new LayoutPropertyValue<>("text-line-height", value);
+ }
/**
@@ -2100,6 +3362,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-letter-spacing", value);
}
+ /**
+ * Text tracking amount.
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Expression> textLetterSpacing(Expression value) {
+ return new LayoutPropertyValue<>("text-letter-spacing", value);
+ }
/**
@@ -2123,6 +3394,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-justify", value);
}
+ /**
+ * Text justification options.
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<Expression> textJustify(Expression value) {
+ return new LayoutPropertyValue<>("text-justify", value);
+ }
/**
@@ -2146,6 +3426,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-anchor", value);
}
+ /**
+ * Part of the text placed closest to the anchor.
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<Expression> textAnchor(Expression value) {
+ return new LayoutPropertyValue<>("text-anchor", value);
+ }
/**
@@ -2169,6 +3458,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-max-angle", value);
}
+ /**
+ * Maximum angle change between adjacent characters.
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Expression> textMaxAngle(Expression value) {
+ return new LayoutPropertyValue<>("text-max-angle", value);
+ }
/**
@@ -2192,6 +3490,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-rotate", value);
}
+ /**
+ * Rotates the text clockwise.
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Expression> textRotate(Expression value) {
+ return new LayoutPropertyValue<>("text-rotate", value);
+ }
/**
@@ -2215,6 +3522,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-padding", value);
}
+ /**
+ * Size of the additional area around the text bounding box used for detecting symbol collisions.
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Expression> textPadding(Expression value) {
+ return new LayoutPropertyValue<>("text-padding", value);
+ }
/**
@@ -2238,6 +3554,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-keep-upright", value);
}
+ /**
+ * If true, the text may be flipped vertically to prevent it from being rendered upside-down.
+ *
+ * @param value a Boolean value
+ * @return property wrapper around Boolean
+ */
+ public static PropertyValue<Expression> textKeepUpright(Expression value) {
+ return new LayoutPropertyValue<>("text-keep-upright", value);
+ }
/**
@@ -2261,6 +3586,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-transform", value);
}
+ /**
+ * Specifies how to capitalize text, similar to the CSS {@link PropertyFactory#textTransform} property.
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<Expression> textTransform(Expression value) {
+ return new LayoutPropertyValue<>("text-transform", value);
+ }
/**
@@ -2284,6 +3618,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-offset", value);
}
+ /**
+ * Offset distance of text from its anchor. Positive values indicate right and down, while negative values indicate left and up.
+ *
+ * @param value a Float[] value
+ * @return property wrapper around Float[]
+ */
+ public static PropertyValue<Expression> textOffset(Expression value) {
+ return new LayoutPropertyValue<>("text-offset", value);
+ }
/**
@@ -2307,6 +3650,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-allow-overlap", value);
}
+ /**
+ * If true, the text will be visible even if it collides with other previously drawn symbols.
+ *
+ * @param value a Boolean value
+ * @return property wrapper around Boolean
+ */
+ public static PropertyValue<Expression> textAllowOverlap(Expression value) {
+ return new LayoutPropertyValue<>("text-allow-overlap", value);
+ }
/**
@@ -2330,6 +3682,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-ignore-placement", value);
}
+ /**
+ * If true, other symbols can be visible even if they collide with the text.
+ *
+ * @param value a Boolean value
+ * @return property wrapper around Boolean
+ */
+ public static PropertyValue<Expression> textIgnorePlacement(Expression value) {
+ return new LayoutPropertyValue<>("text-ignore-placement", value);
+ }
/**
@@ -2353,6 +3714,16 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-optional", value);
}
+ /**
+ * If true, icons will display without their corresponding text when the text collides with other symbols and the icon does not.
+ *
+ * @param value a Boolean value
+ * @return property wrapper around Boolean
+ */
+ public static PropertyValue<Expression> textOptional(Expression value) {
+ return new LayoutPropertyValue<>("text-optional", value);
+ }
+
/**
* If true, icons will display without their corresponding text when the text collides with other symbols and the icon does not.
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 e67e71b61d..a0f45535f6 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
@@ -250,24 +250,6 @@ public class RasterLayer extends Layer {
return (PropertyValue<Float>) new PropertyValue("raster-fade-duration", nativeGetRasterFadeDuration());
}
- /**
- * Get the RasterFadeDuration property transition options
- *
- * @return transition options for Float
- */
- public TransitionOptions getRasterFadeDurationTransition() {
- return nativeGetRasterFadeDurationTransition();
- }
-
- /**
- * Set the RasterFadeDuration property transition options
- *
- * @param options transition options for Float
- */
- public void setRasterFadeDurationTransition(TransitionOptions options) {
- nativeSetRasterFadeDurationTransition(options.getDuration(), options.getDelay());
- }
-
private native Object nativeGetRasterOpacity();
private native TransitionOptions nativeGetRasterOpacityTransition();
@@ -306,10 +288,6 @@ public class RasterLayer extends Layer {
private native Object nativeGetRasterFadeDuration();
- private native TransitionOptions nativeGetRasterFadeDurationTransition();
-
- private native void nativeSetRasterFadeDurationTransition(long duration, long delay);
-
@Override
protected native void finalize() throws Throwable;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/layer.java.ejs b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/layer.java.ejs
index 56e0af8b45..77fa11808e 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
@@ -78,7 +78,7 @@ public class <%- camelize(type) %>Layer extends Layer {
}
<% } -%>
-<% if (type !== 'background' && type !== 'raster') { -%>
+<% if (type !== 'background' && type !== 'raster' && type !== 'hillshade') { -%>
/**
* Get the source layer.
*
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/property_factory.java.ejs b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/property_factory.java.ejs
index 2d3421d1d9..283d4e4189 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/property_factory.java.ejs
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/property_factory.java.ejs
@@ -6,11 +6,13 @@
package com.mapbox.mapboxsdk.style.layers;
-import android.annotation.SuppressLint;
import android.support.annotation.ColorInt;
-import com.mapbox.mapboxsdk.style.functions.Function;
import com.mapbox.mapboxsdk.style.functions.CameraFunction;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
+import com.mapbox.mapboxsdk.style.functions.Function;
+
+import java.util.Locale;
/**
* Constructs paint/layout properties for Layers
@@ -36,6 +38,7 @@ public class PropertyFactory {
* @param function the visibility function
* @return property wrapper around a String function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, String>> visibility(Function<T, String> function) {
return new LayoutPropertyValue<>("visibility", function);
}
@@ -63,6 +66,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("<%- property.name %>", value);
}
+ /**
+ * <%- propertyFactoryMethodDoc(property) %>
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> <%- camelizeWithLeadingLowercase(property.name) %>(Expression expression) {
+ return new PaintPropertyValue<>("<%- property.name %>", expression);
+ }
+
<% if (supportsPropertyFunction(property)) { -%>
/**
@@ -72,6 +85,7 @@ public class PropertyFactory {
* @param function a wrapper function for <%- propertyType(property) %>
* @return property wrapper around a <%- propertyType(property) %> function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, <%- propertyType(property) %>>> <%- camelizeWithLeadingLowercase(property.name) %>(Function<T, <%- propertyType(property) %>> function) {
return new PaintPropertyValue<>("<%- property.name %>", function);
}
@@ -85,6 +99,7 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for <%- propertyType(property) %>
* @return property wrapper around a <%- propertyType(property) %> function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, <%- propertyType(property) %>>> <%- camelizeWithLeadingLowercase(property.name) %>(CameraFunction<Z, <%- propertyType(property) %>> function) {
return new PaintPropertyValue<>("<%- property.name %>", function);
}
@@ -102,6 +117,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("<%- property.name %>", value);
}
+ /**
+ * <%- propertyFactoryMethodDoc(property) %>
+ *
+ * @param value a <%- propertyType(property) %> value
+ * @return property wrapper around <%- propertyType(property) %>
+ */
+ public static PropertyValue<Expression> <%- camelizeWithLeadingLowercase(property.name) %>(Expression value) {
+ return new LayoutPropertyValue<>("<%- property.name %>", value);
+ }
<% if (supportsPropertyFunction(property)) { -%>
@@ -131,9 +155,9 @@ public class PropertyFactory {
<% } -%>
<% } -%>
- @SuppressLint("DefaultLocale")
public static String colorToRgbaString(@ColorInt int value) {
- return String.format("rgba(%d, %d, %d, %d)", (value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF, (value >> 24) & 0xFF);
+ return String.format(Locale.US,"rgba(%d, %d, %d, %d)",
+ (value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF, (value >> 24) & 0xFF);
}
}
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
new file mode 100644
index 0000000000..1b0999ae2e
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySource.java
@@ -0,0 +1,203 @@
+package com.mapbox.mapboxsdk.style.sources;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.UiThread;
+import android.support.annotation.WorkerThread;
+
+import com.mapbox.geojson.Feature;
+import com.mapbox.geojson.FeatureCollection;
+import com.mapbox.mapboxsdk.geometry.LatLngBounds;
+import com.mapbox.mapboxsdk.style.layers.Filter;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Custom Vector Source, allows using FeatureCollections.
+ *
+ */
+@UiThread
+public class CustomGeometrySource extends Source {
+ private ExecutorService executor;
+ private GeometryTileProvider provider;
+ private final Map<TileID, AtomicBoolean> cancelledTileRequests = new ConcurrentHashMap<>();
+
+ /**
+ * Create a CustomGeometrySource
+ *
+ * @param id The source id.
+ * @param provider The tile provider that returns geometry data for this source.
+ */
+ public CustomGeometrySource(String id, GeometryTileProvider provider) {
+ this(id, provider, new CustomGeometrySourceOptions());
+ }
+
+ /**
+ * Create a CustomGeometrySource with non-default CustomGeometrySourceOptions.
+ * <p>Supported options are minZoom, maxZoom, buffer, and tolerance.</p>
+ *
+ * @param id The source id.
+ * @param provider The tile provider that returns geometry data for this source.
+ * @param options CustomGeometrySourceOptions.
+ */
+ public CustomGeometrySource(String id, GeometryTileProvider provider, CustomGeometrySourceOptions options) {
+ this.provider = provider;
+ executor = Executors.newFixedThreadPool(4);
+ initialize(id, options);
+ }
+
+ /**
+ * Invalidate previously provided features within a given bounds at all zoom levels.
+ * Invoking this method will result in new requests to `GeometryTileProvider` for regions
+ * that contain, include, or intersect with the provided bounds.
+ *
+ * @param bounds The region in which features should be invalidated at all zoom levels
+ */
+ public void invalidateRegion(LatLngBounds bounds) {
+ nativeInvalidateBounds(bounds);
+ }
+
+ /**
+ * Invalidate the geometry contents of a specific tile. Invoking this method will result
+ * in new requests to `GeometryTileProvider` for visible tiles.
+ *
+ * @param zoomLevel Tile zoom level.
+ * @param x Tile X coordinate.
+ * @param y Tile Y coordinate.
+ */
+ public void invalidateTile(int zoomLevel, int x, int y) {
+ nativeInvalidateTile(zoomLevel, x, y);
+ }
+
+ /**
+ * Set or update geometry contents of a specific tile. Use this method to update tiles
+ * for which `GeometryTileProvider` was previously invoked. This method can be called from
+ * background threads.
+ *
+ * @param zoomLevel Tile zoom level.
+ * @param x Tile X coordinate.
+ * @param y Tile Y coordinate.
+ * @param data Feature collection for the tile.
+ */
+ public void setTileData(int zoomLevel, int x, int y, FeatureCollection data) {
+ nativeSetTileData(zoomLevel, x, y, data);
+ }
+
+ /**
+ * Queries the source for features.
+ *
+ * @param filter an optional filter statement to filter the returned Features
+ * @return the features
+ */
+ @NonNull
+ public List<Feature> querySourceFeatures(@Nullable Filter.Statement filter) {
+ Feature[] features = querySourceFeatures(filter != null ? filter.toArray() : null);
+ return features != null ? Arrays.asList(features) : new ArrayList<Feature>();
+ }
+
+ protected native void initialize(String sourceId, Object options);
+
+ private native Feature[] querySourceFeatures(Object[] filter);
+
+ private native void nativeSetTileData(int z, int x, int y, FeatureCollection data);
+
+ private native void nativeInvalidateTile(int z, int x, int y);
+
+ private native void nativeInvalidateBounds(LatLngBounds bounds);
+
+ @Override
+ protected native void finalize() throws Throwable;
+
+ private void setTileData(TileID tileId, FeatureCollection data) {
+ cancelledTileRequests.remove(tileId);
+ nativeSetTileData(tileId.z, tileId.x, tileId.y, data);
+ }
+
+ @WorkerThread
+ private void fetchTile(int z, int x, int y) {
+ AtomicBoolean cancelFlag = new AtomicBoolean(false);
+ TileID tileID = new TileID(z, x, y);
+ cancelledTileRequests.put(tileID, cancelFlag);
+ GeometryTileRequest request = new GeometryTileRequest(tileID, provider, this, cancelFlag);
+ executor.execute(request);
+ }
+
+ @WorkerThread
+ private void cancelTile(int z, int x, int y) {
+ AtomicBoolean cancelFlag = cancelledTileRequests.get(new TileID(z, x, y));
+ if (cancelFlag != null) {
+ cancelFlag.compareAndSet(false, true);
+ }
+ }
+
+ private static class TileID {
+ public int z;
+ public int x;
+ public int y;
+
+ public TileID(int _z, int _x, int _y) {
+ z = _z;
+ x = _x;
+ y = _y;
+ }
+
+ public int hashCode() {
+ return Arrays.hashCode(new int[]{z, x, y});
+ }
+
+ public boolean equals(Object object) {
+ if (object == this) {
+ return true;
+ }
+
+ if (object == null || getClass() != object.getClass()) {
+ return false;
+ }
+
+ if (object instanceof TileID) {
+ TileID other = (TileID)object;
+ return this.z == other.z && this.x == other.x && this.y == other.y;
+ }
+ return false;
+ }
+ }
+
+ private static class GeometryTileRequest implements Runnable {
+ private TileID id;
+ private GeometryTileProvider provider;
+ private WeakReference<CustomGeometrySource> sourceRef;
+ private AtomicBoolean cancelled;
+
+ public GeometryTileRequest(TileID _id, GeometryTileProvider p,
+ CustomGeometrySource _source, AtomicBoolean _cancelled) {
+ id = _id;
+ provider = p;
+ sourceRef = new WeakReference<>(_source);
+ cancelled = _cancelled;
+ }
+
+ public void run() {
+ if (isCancelled()) {
+ return;
+ }
+
+ FeatureCollection data = provider.getFeaturesForBounds(LatLngBounds.from(id.z, id.x, id.y), id.z);
+ CustomGeometrySource source = sourceRef.get();
+ if (!isCancelled() && source != null && data != null) {
+ source.setTileData(id, data);
+ }
+ }
+
+ private Boolean isCancelled() {
+ return cancelled.get();
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySourceOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySourceOptions.java
new file mode 100644
index 0000000000..4ada38c238
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySourceOptions.java
@@ -0,0 +1,31 @@
+package com.mapbox.mapboxsdk.style.sources;
+
+/**
+ * Builder class for composing CustomGeometrySource objects.
+ */
+public class CustomGeometrySourceOptions extends GeoJsonOptions {
+
+ /**
+ * If the data includes wrapped coordinates, setting this to true unwraps the coordinates.
+ *
+ * @param wrap defaults to false
+ * @return the current instance for chaining
+ */
+ public CustomGeometrySourceOptions withWrap(boolean wrap) {
+ this.put("wrap", wrap);
+ return this;
+ }
+
+ /**
+ * If the data includes geometry outside the tile boundaries, setting this to true clips the geometry
+ * to the tile boundaries.
+ *
+ * @param clip defaults to false
+ * @return the current instance for chaining
+ */
+ public CustomGeometrySourceOptions withClip(boolean clip) {
+ this.put("clip", clip);
+ return this;
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonOptions.java
index 1a1711e547..79cde7429c 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonOptions.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonOptions.java
@@ -13,6 +13,17 @@ public class GeoJsonOptions extends HashMap<String, Object> {
/**
* Maximum zoom level at which to create vector tiles (higher means greater detail at high zoom levels).
*
+ * @param minZoom the maximum zoom - Defaults to 18.
+ * @return the current instance for chaining
+ */
+ public GeoJsonOptions withMinZoom(int minZoom) {
+ this.put("minzoom", minZoom);
+ return this;
+ }
+
+ /**
+ * Maximum zoom level at which to create vector tiles (higher means greater detail at high zoom levels).
+ *
* @param maxZoom the maximum zoom - Defaults to 18.
* @return the current instance for chaining
*/
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java
index 10ecb945ad..5c740554cd 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java
@@ -4,10 +4,10 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
+import com.mapbox.geojson.Feature;
+import com.mapbox.geojson.FeatureCollection;
+import com.mapbox.geojson.Geometry;
import com.mapbox.mapboxsdk.style.layers.Filter;
-import com.mapbox.services.commons.geojson.Feature;
-import com.mapbox.services.commons.geojson.FeatureCollection;
-import com.mapbox.services.commons.geojson.Geometry;
import java.net.URL;
import java.util.ArrayList;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeometryTileProvider.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeometryTileProvider.java
new file mode 100644
index 0000000000..17e7f0f5e4
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeometryTileProvider.java
@@ -0,0 +1,22 @@
+package com.mapbox.mapboxsdk.style.sources;
+
+import android.support.annotation.WorkerThread;
+
+import com.mapbox.geojson.FeatureCollection;
+import com.mapbox.mapboxsdk.geometry.LatLngBounds;
+
+/**
+ * Interface that defines methods for working with {@link CustomGeometrySource}.
+ */
+public interface GeometryTileProvider {
+
+ /***
+ * Interface method called by {@link CustomGeometrySource} to request features for a tile.
+ *
+ * @param bounds {@link LatLngBounds} of the tile.
+ * @param zoomLevel Tile zoom level.
+ * @return Return a @{link FeatureCollection} to be displayed in the requested tile.
+ */
+ @WorkerThread
+ FeatureCollection getFeaturesForBounds(LatLngBounds bounds, int zoomLevel);
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/RasterDemSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/RasterDemSource.java
new file mode 100644
index 0000000000..ee6fc5d7b7
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/RasterDemSource.java
@@ -0,0 +1,93 @@
+package com.mapbox.mapboxsdk.style.sources;
+
+import android.support.annotation.Nullable;
+import android.support.annotation.UiThread;
+
+import java.net.URL;
+
+/**
+ * A raster DEM source. Currently only supports Mapbox Terrain RGB (mapbox://mapbox.terrain-rgb)
+ *
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#sources-raster-dem">The style specification</a>
+ */
+@UiThread
+public class RasterDemSource extends Source {
+ public static final int DEFAULT_TILE_SIZE = 512;
+
+ /**
+ * Internal use
+ *
+ * @param nativePtr - pointer to native peer
+ */
+ private RasterDemSource(long nativePtr) {
+ super(nativePtr);
+ }
+
+ /**
+ * Create the raster dem source from an URL
+ *
+ * @param id the source id
+ * @param url the source url
+ */
+ public RasterDemSource(String id, URL url) {
+ this(id, url.toExternalForm());
+ }
+
+ /**
+ * Create the raster dem source from an URL
+ *
+ * @param id the source id
+ * @param url the source url
+ */
+ public RasterDemSource(String id, String url) {
+ initialize(id, url, DEFAULT_TILE_SIZE);
+ }
+
+ /**
+ * Create the raster source from an URL with a specific tile size
+ *
+ * @param id the source id
+ * @param url the source url
+ * @param tileSize the tile size
+ */
+ public RasterDemSource(String id, String url, int tileSize) {
+ initialize(id, url, tileSize);
+ }
+
+ /**
+ * Create the raster dem source from a {@link TileSet}
+ *
+ * @param id the source id
+ * @param tileSet the {@link TileSet}
+ */
+ public RasterDemSource(String id, TileSet tileSet) {
+ initialize(id, tileSet.toValueObject(), DEFAULT_TILE_SIZE);
+ }
+
+ /**
+ * Create the raster source from a {@link TileSet} with a specific tile size
+ *
+ * @param id the source id
+ * @param tileSet the {@link TileSet}
+ * @param tileSize tje tile size
+ */
+ public RasterDemSource(String id, TileSet tileSet, int tileSize) {
+ initialize(id, tileSet.toValueObject(), tileSize);
+ }
+
+ /**
+ * @return The url or null
+ */
+ @Nullable
+ public String getUrl() {
+ return nativeGetUrl();
+ }
+
+ protected native void initialize(String layerId, Object payload, int tileSize);
+
+ @Override
+ protected native void finalize() throws Throwable;
+
+ protected native String nativeGetUrl();
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/VectorSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/VectorSource.java
index 9b59cf8967..62b08a90ed 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/VectorSource.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/VectorSource.java
@@ -5,8 +5,8 @@ import android.support.annotation.Nullable;
import android.support.annotation.Size;
import android.support.annotation.UiThread;
+import com.mapbox.geojson.Feature;
import com.mapbox.mapboxsdk.style.layers.Filter;
-import com.mapbox.services.commons.geojson.Feature;
import java.net.URL;
import java.util.ArrayList;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MathUtils.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MathUtils.java
new file mode 100644
index 0000000000..0c90e4b244
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MathUtils.java
@@ -0,0 +1,49 @@
+package com.mapbox.mapboxsdk.utils;
+
+// TODO Remove this class if we finally include it within MAS 3.x (GeoJSON)
+public class MathUtils {
+
+ /**
+ * Test a value in specified range, returning minimum if it's below, and maximum if it's above
+ *
+ * @param value Value to test
+ * @param min Minimum value of range
+ * @param max Maximum value of range
+ * @return value if it's between min and max, min if it's below, max if it's above
+ */
+ public static double clamp(double value, double min, double max) {
+ return Math.max(min, Math.min(max, value));
+ }
+
+ /**
+ * Test a value in specified range, returning minimum if it's below, and maximum if it's above
+ *
+ * @param value Value to test
+ * @param min Minimum value of range
+ * @param max Maximum value of range
+ * @return value if it's between min and max, min if it's below, max if it's above
+ */
+ public static float clamp(float value, float min, float max) {
+ return Math.max(min, Math.min(max, value));
+ }
+
+ /**
+ * Constrains value to the given range (including min, excluding max) via modular arithmetic.
+ * <p>
+ * Same formula as used in Core GL (wrap.hpp)
+ * std::fmod((std::fmod((value - min), d) + d), d) + min;
+ *
+ * @param value Value to wrap
+ * @param min Minimum value
+ * @param max Maximum value
+ * @return Wrapped value
+ */
+ public static double wrap(double value, double min, double max) {
+ double delta = max - min;
+
+ double firstMod = (value - min) % delta;
+ double secondMod = (firstMod + delta) % delta;
+
+ return secondMod + min;
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values-fr/strings.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values-fr/strings.xml
index 48d90c3324..9548bf9b3d 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values-fr/strings.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values-fr/strings.xml
@@ -1,15 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <string name="mapbox_compassContentDescription">Boussole. Activer pour rétablir l\'orientation de la carte vers le nord.</string>
- <string name="mapbox_attributionsIconContentDescription">Icone d\'attribution. Activer pour montrer le dialogue d\'attribution.</string>
+ <string name="mapbox_compassContentDescription">Boussole. Activer pour rétablir l’orientation de la carte vers le nord.</string>
+ <string name="mapbox_attributionsIconContentDescription">Icône d’attribution. Activer pour afficher le dialogue d’attribution.</string>
<string name="mapbox_myLocationViewContentDescription">Vue de géolocalisation. Ceci affiche votre localisation sur la carte.</string>
- <string name="mapbox_mapActionDescription">Affichage d\'une carte créée avec Mapbox. Faites la glisser en traînant deux doigts. Zoomez ou dézoomez en écartant ou rapprochant deux doigts.</string>
- <string name="mapbox_attributionsDialogTitle">SDK Mapbox pour Android</string>
+ <string name="mapbox_mapActionDescription">Affichage d’une carte créée avec Mapbox. Faites la glisser en traînant deux doigts. Zoomez ou dézoomez en écartant ou rapprochant deux doigts.</string>
+ <string name="mapbox_attributionsDialogTitle">SDK Mapbox Maps pour Android</string>
<string name="mapbox_attributionTelemetryTitle">Faire de meilleures cartes Mapbox</string>
<string name="mapbox_attributionTelemetryMessage">Vous aidez à améliorer les cartes OpenStreetMap et Mapbox en contribuant des données d\'utilisation anonymes.</string>
- <string name="mapbox_attributionTelemetryPositive">D\'accord</string>
- <string name="mapbox_attributionTelemetryNegative">Pas d\'accord</string>
- <string name="mapbox_attributionTelemetryNeutral">Plus d\'informations</string>
+ <string name="mapbox_attributionTelemetryPositive">D’accord</string>
+ <string name="mapbox_attributionTelemetryNegative">Pas d’accord</string>
+ <string name="mapbox_attributionTelemetryNeutral">Plus d’informations</string>
+ <string name="mapbox_attributionErrorNoBrowser">Aucun navigateur web installé sur l’appareil, impossible d’ouvrir une page web.</string>
<string name="mapbox_offline_error_region_definition_invalid">Le cadre OfflineRegionDefinition pour définir la région de navigation ne tient pas dans les limites du monde : %s</string>
-
+ <string name="mapbox_telemetrySettings">Paramètres de télémétrie</string>
</resources>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values-iw/strings.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values-iw/strings.xml
new file mode 100644
index 0000000000..11b20f5dc5
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values-iw/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="mapbox_compassContentDescription">מצפן. הפעל בכדי לקבע את כיוון המפה צפונה.</string>
+ <string name="mapbox_attributionsIconContentDescription">סמל שיוך. הפעל כדי להציג תיבת דו-שיח של שיוך.</string>
+ <string name="mapbox_myLocationViewContentDescription">סמן מיקום. מציג את המיקום הנוכחי שלך על המפה.</string>
+ <string name="mapbox_mapActionDescription">מציג מפה שנוצרה עם Mapbox. גלול באמצעות גרירה עם שתי אצבעות, זום באמצעות צביטה עם שתי אצבעות.</string>
+ <string name="mapbox_attributionsDialogTitle">Mapbox Maps SDK for Android</string>
+ <string name="mapbox_attributionTelemetryTitle">שפר את המפות של Mapbox </string>
+ <string name="mapbox_attributionTelemetryMessage">אתם מסייעים לשפר את המפות של OpenStreetMap ו Mapbox באמצעות שיתוף אנונימי של נתוני השימוש.</string>
+ <string name="mapbox_attributionTelemetryPositive">מסכים/מה</string>
+ <string name="mapbox_attributionTelemetryNegative">לא מסכים/מה</string>
+ <string name="mapbox_attributionTelemetryNeutral">מידע נוסף</string>
+ <string name="mapbox_attributionErrorNoBrowser">לא מותקן דפדפן אינטרנט במכשיר, לא ניתן לפתוח את דף האינטרנט.</string>
+ <string name="mapbox_offline_error_region_definition_invalid">בתנאי ש- OfflineRegionDefinition אינו מתאים לגבולות העולם: %s</string>
+ <string name="mapbox_telemetrySettings">הגדרות טלמטריות</string>
+ </resources>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values-sv/strings.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values-sv/strings.xml
index c9474fa473..9537299a72 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values-sv/strings.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values-sv/strings.xml
@@ -4,12 +4,13 @@
<string name="mapbox_attributionsIconContentDescription">Tillskrivningsikon. Aktivera för att visa tillskrivningsdialog.</string>
<string name="mapbox_myLocationViewContentDescription">Positionsvy. Denna visar din position på kartan.</string>
<string name="mapbox_mapActionDescription">Visar en karta skapad med Mapbox. Scrolla genom att dra med två fingrar. Zooma genom att nypa med två fingrar.</string>
- <string name="mapbox_attributionsDialogTitle">Mapbox Android SDK</string>
+ <string name="mapbox_attributionsDialogTitle">Mapbox Maps SDK for Android</string>
<string name="mapbox_attributionTelemetryTitle">Gör Mapbox kartor bättre</string>
<string name="mapbox_attributionTelemetryMessage">Du hjälper till att göra OpenStreetMap och Mapbox karttjänster bättre genom att bidra med anonymiserad användningsdata.</string>
<string name="mapbox_attributionTelemetryPositive">Godkänn</string>
<string name="mapbox_attributionTelemetryNegative">Avböj</string>
<string name="mapbox_attributionTelemetryNeutral">Visa mer information</string>
+ <string name="mapbox_attributionErrorNoBrowser">Ingen webbläsare installerad på enheten. Kan inte visa sidan.</string>
<string name="mapbox_offline_error_region_definition_invalid">Försedd OfflineRegionDefinition passar inte världens gränser: %s</string>
-
+ <string name="mapbox_telemetrySettings">Telemetri-inställningar</string>
</resources>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values-uk/strings.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values-uk/strings.xml
index 64c7e3efcc..17e13a1fe6 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values-uk/strings.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values-uk/strings.xml
@@ -4,12 +4,13 @@
<string name="mapbox_attributionsIconContentDescription">Значок атрибуції. Натисніть, щоб показати діалог атрибуції.</string>
<string name="mapbox_myLocationViewContentDescription">Визначення положення. Показує ваше місцеположення на мапі.</string>
<string name="mapbox_mapActionDescription">Показує мапи створені за допомоги Mapbox. Пересувайте мапу двома пальцями. Змінюйте масштаб стуляючи/розводячи два пальці.</string>
- <string name="mapbox_attributionsDialogTitle">Mapbox Android SDK</string>
+ <string name="mapbox_attributionsDialogTitle">Mapbox Maps SDK для Android</string>
<string name="mapbox_attributionTelemetryTitle">Допоможіть зробити мапи Mapbox краще</string>
<string name="mapbox_attributionTelemetryMessage">Ви допомагаєте робити мапи OpenStreetMap та Mapbox краще пощирюючи анонімні дані про користування мапами.</string>
<string name="mapbox_attributionTelemetryPositive">Погоджуюсь</string>
<string name="mapbox_attributionTelemetryNegative">Не погоджуюсь</string>
<string name="mapbox_attributionTelemetryNeutral">Додаткова інформація</string>
+ <string name="mapbox_attributionErrorNoBrowser">Веб-оглядач відсутній на цьому пристрої, неможливо відкрити веб-сторінку</string>
<string name="mapbox_offline_error_region_definition_invalid">Межі ділянки для оффлайнового користування даними за межами світу: %s</string>
-
+ <string name="mapbox_telemetrySettings">Налаштування телеметрії</string>
</resources>
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/MapboxTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/MapboxTest.java
index 7a28d846ea..d9e3ae427d 100644
--- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/MapboxTest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/MapboxTest.java
@@ -5,7 +5,7 @@ import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import com.mapbox.mapboxsdk.exceptions.MapboxConfigurationException;
-import com.mapbox.services.android.telemetry.location.LocationEngine;
+import com.mapbox.android.core.location.LocationEngine;
import org.junit.Before;
import org.junit.Test;
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java
index bb96c9939d..f03bbdb11c 100644
--- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java
@@ -2,11 +2,14 @@ package com.mapbox.mapboxsdk.geometry;
import android.os.Parcelable;
+import com.mapbox.mapboxsdk.constants.GeometryConstants;
import com.mapbox.mapboxsdk.exceptions.InvalidLatLngBoundsException;
import com.mapbox.mapboxsdk.utils.MockParcel;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.ExpectedException;
import java.util.ArrayList;
import java.util.List;
@@ -68,6 +71,82 @@ public class LatLngBoundsTest {
}
@Test
+ public void dateLineSpanBuilder1() {
+ latLngBounds = new LatLngBounds.Builder()
+ .include(new LatLng(10, -170))
+ .include(new LatLng(-10, 170))
+ .build();
+
+ LatLngSpan latLngSpan = latLngBounds.getSpan();
+ assertEquals("LatLngSpan should be shortest distance", new LatLngSpan(20, 20),
+ latLngSpan);
+ }
+
+ @Test
+ public void dateLineSpanBuilder2() {
+ latLngBounds = new LatLngBounds.Builder()
+ .include(new LatLng(-10, -170))
+ .include(new LatLng(10, 170))
+ .build();
+
+ LatLngSpan latLngSpan = latLngBounds.getSpan();
+ assertEquals("LatLngSpan should be shortest distance", new LatLngSpan(20, 20),
+ latLngSpan);
+ }
+
+ @Test
+ public void dateLineSpanFrom1() {
+ latLngBounds = LatLngBounds.from(10, -170, -10, 170);
+ LatLngSpan latLngSpan = latLngBounds.getSpan();
+ assertEquals("LatLngSpan should be shortest distance", new LatLngSpan(20, 20),
+ latLngSpan);
+ }
+
+ @Test
+ public void dateLineSpanFrom2() {
+ latLngBounds = LatLngBounds.from(10, 170, -10, -170);
+ LatLngSpan latLngSpan = latLngBounds.getSpan();
+ assertEquals("LatLngSpan should be shortest distance", new LatLngSpan(20, 340),
+ latLngSpan);
+ }
+
+ @Test
+ public void nearDateLineCenter1() {
+ latLngBounds = LatLngBounds.from(10, -175, -10, 165);
+ LatLng center = latLngBounds.getCenter();
+ assertEquals("Center should match", new LatLng(0, 175), center);
+ }
+
+ @Test
+ public void nearDateLineCenter2() {
+ latLngBounds = LatLngBounds.from(10, -165, -10, 175);
+ LatLng center = latLngBounds.getCenter();
+ assertEquals("Center should match", new LatLng(0, -175), center);
+ }
+
+ @Test
+ public void nearDateLineCenter3() {
+ latLngBounds = LatLngBounds.from(10, -170, -10, 170);
+ LatLng center = latLngBounds.getCenter();
+ assertEquals("Center should match", new LatLng(0, -180), center);
+ }
+
+ @Test
+ public void nearDateLineCenter4() {
+ latLngBounds = LatLngBounds.from(10, -180, -10, 0);
+ LatLng center = latLngBounds.getCenter();
+ assertEquals("Center should match", new LatLng(0, 90), center);
+ }
+
+ @Test
+ public void nearDateLineCenter5() {
+ latLngBounds = LatLngBounds.from(10, 180, -10, 0);
+ LatLng center = latLngBounds.getCenter();
+ assertEquals("Center should match", new LatLng(0, 90), center);
+ }
+
+
+ @Test
public void center() {
LatLng center = latLngBounds.getCenter();
assertEquals("Center should match", new LatLng(1, 1), center);
@@ -118,6 +197,46 @@ public class LatLngBoundsTest {
}
@Test
+ public void includesOverDateline1() {
+
+ LatLngBounds latLngBounds = new LatLngBounds.Builder()
+ .include(new LatLng(10, -170))
+ .include(new LatLng(-10, -175))
+ .include(new LatLng(0, 170))
+ .build();
+
+ assertEquals("LatLngSpan should be the same",
+ new LatLngSpan(20, 20), latLngBounds.getSpan());
+ }
+
+ @Test
+ public void includesOverDateline2() {
+
+ LatLngBounds latLngBounds = new LatLngBounds.Builder()
+ .include(new LatLng(10, 170))
+ .include(new LatLng(-10, 175))
+ .include(new LatLng(0, -170))
+ .build();
+
+ assertEquals("LatLngSpan should be the same",
+ new LatLngSpan(20, 20), latLngBounds.getSpan());
+ }
+
+ @Test
+ public void includesOverDateline3() {
+
+ LatLngBounds latLngBounds = new LatLngBounds.Builder()
+ .include(new LatLng(10, 170))
+ .include(new LatLng(-10, -170))
+ .include(new LatLng(0, -180))
+ .include(new LatLng(5, 180))
+ .build();
+
+ assertEquals("LatLngSpan should be the same",
+ new LatLngSpan(20, 20), latLngBounds.getSpan());
+ }
+
+ @Test
public void containsNot() {
assertFalse("LatLng should not be included", latLngBounds.contains(new LatLng(3, 1)));
}
@@ -128,6 +247,21 @@ public class LatLngBoundsTest {
}
@Test
+ public void worldSpan() {
+ assertEquals("LatLngBounds world span should be 180, 360",
+ GeometryConstants.LATITUDE_SPAN, LatLngBounds.world().getLatitudeSpan(), DELTA);
+ assertEquals("LatLngBounds world span should be 180, 360",
+ GeometryConstants.LONGITUDE_SPAN, LatLngBounds.world().getLongitudeSpan(), DELTA);
+ }
+
+ @Test
+ public void emptySpan() {
+ LatLngBounds latLngBounds = LatLngBounds.from(GeometryConstants.MIN_LATITUDE, GeometryConstants.MAX_LONGITUDE,
+ GeometryConstants.MIN_LATITUDE, GeometryConstants.MAX_LONGITUDE);
+ assertTrue("LatLngBounds empty span", latLngBounds.isEmptySpan());
+ }
+
+ @Test
public void containsBounds() {
LatLngBounds inner = new LatLngBounds.Builder()
.include(new LatLng(-5, -5))
@@ -272,4 +406,94 @@ public class LatLngBoundsTest {
Parcelable parcel = MockParcel.obtain(latLngBounds);
assertEquals("Parcel should match original object", parcel, latLngBounds);
}
+
+ @Test
+ public void fromTileID() {
+ LatLngBounds bounds = LatLngBounds.from(0, 0, 0);
+ assertEquals(GeometryConstants.MIN_LONGITUDE, bounds.getLonWest(), DELTA);
+ assertEquals(GeometryConstants.MIN_MERCATOR_LATITUDE, bounds.getLatSouth(), DELTA);
+ assertEquals(GeometryConstants.MAX_LONGITUDE, bounds.getLonEast(), DELTA);
+ assertEquals(GeometryConstants.MAX_MERCATOR_LATITUDE, bounds.getLatNorth(), DELTA);
+
+ bounds = LatLngBounds.from(10, 288, 385);
+ assertEquals(-78.75, bounds.getLonWest(), DELTA);
+ assertEquals(40.446947059600497, bounds.getLatSouth(), DELTA);
+ assertEquals(-78.3984375, bounds.getLonEast(), DELTA);
+ assertEquals(40.713955826286039, bounds.getLatNorth(), DELTA);
+
+ }
+
+ @Rule
+ public final ExpectedException exception = ExpectedException.none();
+
+ @Test
+ public void testConstructorChecksNorthLatitudeNaN() {
+ exception.expect(IllegalArgumentException.class);
+ exception.expectMessage("latitude must not be NaN");
+ LatLngBounds.from(Double.NaN, 0, -20, -20);
+ }
+
+ @Test
+ public void testConstructorChecksEastLongitudeNaN() {
+ exception.expect(IllegalArgumentException.class);
+ exception.expectMessage("longitude must not be NaN");
+ LatLngBounds.from(0, Double.NaN, -20, -20);
+ }
+
+ @Test
+ public void testConstructorChecksNorthLatitudeGreaterThan90() {
+ exception.expect(IllegalArgumentException.class);
+ exception.expectMessage("latitude must be between -90 and 90");
+ LatLngBounds.from(95, 0, -20, -20);
+ }
+
+ @Test
+ public void testConstructorChecksNorthLatitudeLessThanThanNegative90() {
+ exception.expect(IllegalArgumentException.class);
+ exception.expectMessage("latitude must be between -90 and 90");
+ LatLngBounds.from(-95, 0, -20, -20);
+ }
+
+ @Test
+ public void testConstructorChecksEastLongitudeInfinity() {
+ exception.expect(IllegalArgumentException.class);
+ exception.expectMessage("longitude must not be infinite");
+ LatLngBounds.from(0, Double.POSITIVE_INFINITY, -20, -20);
+ }
+
+
+ @Test
+ public void testConstructorChecksSouthLatitudeNaN() {
+ exception.expect(IllegalArgumentException.class);
+ exception.expectMessage("latitude must not be NaN");
+ LatLngBounds.from(20, 20, Double.NaN, 0);
+ }
+
+ @Test
+ public void testConstructorChecksWesttLongitudeNaN() {
+ exception.expect(IllegalArgumentException.class);
+ exception.expectMessage("longitude must not be NaN");
+ LatLngBounds.from(20, 20, 0, Double.NaN);
+ }
+
+ @Test
+ public void testConstructorChecksSouthLatitudeGreaterThan90() {
+ exception.expect(IllegalArgumentException.class);
+ exception.expectMessage("latitude must be between -90 and 90");
+ LatLngBounds.from(20, 20,95, 0);
+ }
+
+ @Test
+ public void testConstructorChecksSouthLatitudeLessThanThanNegative90() {
+ exception.expect(IllegalArgumentException.class);
+ exception.expectMessage("latitude must be between -90 and 90");
+ LatLngBounds.from(20, 20, -95, 0);
+ }
+
+ @Test
+ public void testConstructorChecksWestLongitudeInfinity() {
+ exception.expect(IllegalArgumentException.class);
+ exception.expectMessage("longitude must not be infinite");
+ LatLngBounds.from(20, 20, 0, Double.POSITIVE_INFINITY);
+ }
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngTest.java
index 06e93b9d2f..8e47f069c3 100644
--- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngTest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngTest.java
@@ -12,6 +12,7 @@ import org.junit.rules.ExpectedException;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -255,8 +256,17 @@ public class LatLngTest {
@Test
public void testWrapped() {
- LatLng latLng = new LatLng(45.0, -185.0).wrap();
- assertEquals("longitude wrapped value", latLng.getLongitude(), 175.0, DELTA);
+ LatLng originalLatLng = new LatLng(45.0, -185.0);
+ LatLng newLatlng = originalLatLng.wrap();
+ assertNotSame(" new wrapped LatLng is created", originalLatLng, newLatlng);
+ assertEquals("longitude wrapped value", originalLatLng.getLongitude(), -185.0, DELTA);
+ assertEquals("longitude wrapped value", newLatlng.getLongitude(), 175.0, DELTA);
+
+ newLatlng = new LatLng(45.0, 180.0).wrap();
+ assertEquals("longitude wrapped max value", newLatlng.getLongitude(), 180.0, DELTA);
+
+ newLatlng = new LatLng(45.0, -180.0).wrap();
+ assertEquals("longitude wrapped min value", newLatlng.getLongitude(), -180.0, DELTA);
}
@Test
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
new file mode 100644
index 0000000000..c0b7832727
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/expressions/ExpressionTest.java
@@ -0,0 +1,1033 @@
+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;
+
+import static com.mapbox.mapboxsdk.style.expressions.Expression.acos;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.all;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.any;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.array;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.asin;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.at;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.atan;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.bool;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.coalesce;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.color;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.concat;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.cos;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.cubicBezier;
+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.e;
+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.geometryType;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.get;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.gt;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.gte;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.has;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.heatmapDensity;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.id;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.interpolate;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.length;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.let;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.linear;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.literal;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.ln;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.ln2;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.log10;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.log2;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.lt;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.lte;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.match;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.max;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.min;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.mod;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.neq;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.not;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.number;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.object;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.pi;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.pow;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.product;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.properties;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.rgb;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.rgba;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.sin;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.sqrt;
+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.subtract;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.sum;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.switchCase;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.tan;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.toBool;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.toColor;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.toNumber;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.toRgba;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.typeOf;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.upcase;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.var;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.zoom;
+import static junit.framework.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Expression unit tests that validate the expression output with the expected Object[]array representation.
+ */
+public class ExpressionTest {
+
+ @Test
+ public void testRgb() throws Exception {
+ Object[] expected = new Object[] {"rgb", 0, 0, 0};
+ Object[] actual = rgb(literal(0), literal(0), literal(0)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testRgbLiteral() throws Exception {
+ Object[] expected = new Object[] {"rgb", 0, 0, 0};
+ Object[] actual = rgb(0, 0, 0).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testRgba() throws Exception {
+ Object[] expected = new Object[] {"rgba", 0, 0, 0, 1};
+ Object[] actual = rgba(literal(0), literal(0), literal(0), literal(1)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testRgbaLiteral() throws Exception {
+ Object[] expected = new Object[] {"rgba", 0, 0, 0, 1};
+ Object[] actual = rgba(0, 0, 0, 1).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testToRgba() throws Exception {
+ Object[] expected = new Object[] {"to-rgba", PropertyFactory.colorToRgbaString(Color.RED)};
+ Object[] actual = toRgba(color(Color.RED)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testEq() throws Exception {
+ Object[] expected = new Object[] {"==", 1, 1};
+ Object[] actual = eq(literal(1), literal(1)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testEqLiteral() throws Exception {
+ Object[] expected = new Object[] {"==", 1, 1};
+ Object[] actual = eq(1, 1).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testNeq() throws Exception {
+ Object[] expected = new Object[] {"!=", 0, 1};
+ Object[] actual = neq(literal(0), literal(1)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testNeqLiteral() throws Exception {
+ Object[] expected = new Object[] {"!=", 0, 1};
+ Object[] actual = neq(0, 1).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testGt() throws Exception {
+ Object[] expected = new Object[] {">", 0, 1};
+ Object[] actual = gt(literal(0), literal(1)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testGtLiteral() throws Exception {
+ Object[] expected = new Object[] {">", 0, 1};
+ Object[] actual = gt(0, 1).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testLt() throws Exception {
+ Object[] expected = new Object[] {"<", 1, 0};
+ Object[] actual = lt(literal(1), literal(0)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testLtLiteral() throws Exception {
+ Object[] expected = new Object[] {"<", 1, 0};
+ Object[] actual = lt(1, 0).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testGte() throws Exception {
+ Object[] expected = new Object[] {">=", 1, 1};
+ Object[] actual = gte(literal(1), literal(1)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testGteLiteral() throws Exception {
+ Object[] expected = new Object[] {">=", 1, 1};
+ Object[] actual = gte(1, 1).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testLte() throws Exception {
+ Object[] expected = new Object[] {"<=", 1, 1};
+ Object[] actual = lte(literal(1), literal(1)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testLteLiteral() throws Exception {
+ Object[] expected = new Object[] {"<=", 1, 1};
+ Object[] actual = lte(1, 1).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testAll() throws Exception {
+ Object[] expected = new Object[] {"all", true, true, true};
+ Object[] actual = all(literal(true), literal(true), literal(true)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testAny() throws Exception {
+ Object[] expected = new Object[] {"any", true, false, false};
+ Object[] actual = any(literal(true), literal(false), literal(false)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testNot() throws Exception {
+ Object[] expected = new Object[] {"!", false};
+ Object[] actual = not(literal(false)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testNotLiteral() throws Exception {
+ Object[] expected = new Object[] {"!", false};
+ Object[] actual = not(false).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testSwitchCase() throws Exception {
+ Object[] expectedCaseOneGet = new Object[] {"get", "key1"};
+ Object[] expectedCaseOne = new Object[] {"==", expectedCaseOneGet, "value1"};
+ Object[] expectedCaseTwoGet = new Object[] {"get", "key2"};
+ Object[] expectedCaseTwo = new Object[] {"!=", expectedCaseTwoGet, "value2"};
+ Object[] expected = new Object[] {"case", expectedCaseOne, expectedCaseTwo};
+
+ Object[] actual = switchCase(
+ eq(get(literal("key1")), literal("value1")),
+ neq(get(literal("key2")), literal("value2"))
+ ).toArray();
+
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testSwitchCaseLiteral() throws Exception {
+ Object[] expectedCaseOneGet = new Object[] {"get", "key1"};
+ Object[] expectedCaseOne = new Object[] {"==", expectedCaseOneGet, "value1"};
+ Object[] expectedCaseTwoGet = new Object[] {"get", "key2"};
+ Object[] expectedCaseTwo = new Object[] {"!=", expectedCaseTwoGet, "value2"};
+ Object[] expected = new Object[] {"case", expectedCaseOne, expectedCaseTwo};
+
+ Object[] actual = switchCase(
+ eq(get("key1"), literal("value1")),
+ neq(get("key2"), literal("value2"))
+ ).toArray();
+
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testMatch() throws Exception {
+ Object[] labelZero = new Object[] {"a", "output"};
+ Object[] labelOne = new Object[] {"b", "output2"};
+ Object[] labelTwo = new Object[] {"c", "output3"};
+
+ Object[] expected = new Object[] {"match", labelZero, labelOne, labelTwo};
+ Object[] actual = match(literal(labelZero), literal(labelOne), literal(labelTwo)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testCoalesce() throws Exception {
+ Object[] expectedGetOne = new Object[] {"get", "invalidKey"};
+ Object[] expectedGetTwo = new Object[] {"get", "validKey"};
+ Object[] expected = new Object[] {"coalesce", expectedGetOne, expectedGetTwo};
+
+ Object[] actual = coalesce(
+ get(literal("invalidKey")),
+ get(literal("validKey"))
+ ).toArray();
+
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testCoalesceLiteral() throws Exception {
+ Object[] expectedGetOne = new Object[] {"get", "invalidKey"};
+ Object[] expectedGetTwo = new Object[] {"get", "validKey"};
+ Object[] expected = new Object[] {"coalesce", expectedGetOne, expectedGetTwo};
+
+ Object[] actual = coalesce(
+ get("invalidKey"),
+ get("validKey")
+ ).toArray();
+
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testProperties() throws Exception {
+ Object[] expected = new Object[] {"properties"};
+ Object[] actual = properties().toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testGeometryType() throws Exception {
+ Object[] expected = new Object[] {"geometry-type"};
+ Object[] actual = geometryType().toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testId() throws Exception {
+ Object[] expected = new Object[] {"id"};
+ Object[] actual = id().toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testHeatmapDensity() throws Exception {
+ Object[] expected = new Object[] {"heatmap-density"};
+ Object[] actual = heatmapDensity().toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testAt() throws Exception {
+ Object[] expected = new Object[] {"at", 3, new Object[] {"one", "two"}};
+ Object[] actual = at(literal(3), literal(new Object[] {"one", "two"})).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testAtLiteral() throws Exception {
+ Object[] expected = new Object[] {"at", 3, new Object[] {"one", "two"}};
+ Object[] actual = at(3, literal(new Object[] {"one", "two"})).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testAtExpression() throws Exception {
+ Object[] expected = new Object[] {"at", 3, new Object[] {"properties"}};
+ Object[] actual = at(literal(3), properties()).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testGet() throws Exception {
+ Object[] expected = new Object[] {"get", "key"};
+ Object[] actual = get(literal("key")).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testGetLiteral() throws Exception {
+ Object[] expected = new Object[] {"get", "key"};
+ Object[] actual = get("key").toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testGetObject() throws Exception {
+ Object[] expected = new Object[] {"get", "key", new Object[] {"properties"}};
+ Object[] actual = get(literal("key"), properties()).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testGetObjectLiteral() throws Exception {
+ Object[] expected = new Object[] {"get", "key", new Object[] {"properties"}};
+ Object[] actual = get("key", properties()).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testHas() throws Exception {
+ Object[] expected = new Object[] {"has", "key"};
+ Object[] actual = has(literal("key")).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testHasLiteral() throws Exception {
+ Object[] expected = new Object[] {"has", "key"};
+ Object[] actual = has("key").toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testHasObject() throws Exception {
+ Object[] expected = new Object[] {"has", "key", new Object[] {"properties"}};
+ Object[] actual = has(literal("key"), properties()).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testHasObjectLiteral() throws Exception {
+ Object[] expected = new Object[] {"has", "key", new Object[] {"properties"}};
+ Object[] actual = has("key", properties()).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testHasExpression() throws Exception {
+ Object[] expected = new Object[] {"has", new Object[] {"get", "key"}, new Object[] {"properties"}};
+ Object[] actual = has(get(literal("key")), properties()).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testHasExpressionLiteral() throws Exception {
+ Object[] expected = new Object[] {"has", new Object[] {"get", "key"}, new Object[] {"properties"}};
+ Object[] actual = has(get("key"), properties()).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testLength() throws Exception {
+ Object[] expected = new Object[] {"length", "key"};
+ Object[] actual = length(literal("key")).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testLengthLiteral() throws Exception {
+ Object[] expected = new Object[] {"length", "key"};
+ Object[] actual = length("key").toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testLengthExpression() throws Exception {
+ Object[] expected = new Object[] {"length", new Object[] {"get", "key"}};
+ Object[] actual = length(get(literal("key"))).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testLn2() throws Exception {
+ Object[] expected = new Object[] {"ln2"};
+ Object[] actual = ln2().toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testPi() throws Exception {
+ Object[] expected = new Object[] {"pi"};
+ Object[] actual = pi().toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testE() throws Exception {
+ Object[] expected = new Object[] {"e"};
+ Object[] actual = e().toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testSum() throws Exception {
+ Object[] expected = new Object[] {"+", 1, 2};
+ Object[] actual = sum(literal(1), literal(2)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testSumLiteral() throws Exception {
+ Object[] expected = new Object[] {"+", 1, 2};
+ Object[] actual = sum(1, 2).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testProduct() throws Exception {
+ Object[] expected = new Object[] {"*", 1, 2};
+ Object[] actual = product(literal(1), literal(2)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testProductLiteral() throws Exception {
+ Object[] expected = new Object[] {"*", 1, 2};
+ Object[] actual = product(1, 2).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testSubtract() throws Exception {
+ Object[] expected = new Object[] {"-", 2, 1};
+ Object[] actual = subtract(literal(2), literal(1)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testSubtractLiteral() throws Exception {
+ Object[] expected = new Object[] {"-", 2, 1};
+ Object[] actual = subtract(2, 1).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testDivision() throws Exception {
+ Object[] expected = new Object[] {"/", 2, 1};
+ Object[] actual = division(literal(2), literal(1)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testDivisionLiteral() throws Exception {
+ Object[] expected = new Object[] {"/", 2, 1};
+ Object[] actual = division(2, 1).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testDivisionWithNestedGet() throws Exception {
+ Object nestedGet = new Object[] {"get", "key"};
+ Object[] expected = new Object[] {"/", 2, nestedGet};
+ Object[] actual = division(literal(2), get(literal("key"))).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testMod() throws Exception {
+ Object[] expected = new Object[] {"%", 1, 3};
+ Object[] actual = mod(literal(1), literal(3)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testModLiteral() throws Exception {
+ Object[] expected = new Object[] {"%", 1, 3};
+ Object[] actual = mod(1, 3).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testPow() throws Exception {
+ Object[] expected = new Object[] {"^", 2, 3};
+ Object[] actual = pow(literal(2), literal(3)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testPowLiteral() throws Exception {
+ Object[] expected = new Object[] {"^", 2, 3};
+ Object[] actual = pow(2, 3).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testSqrt() throws Exception {
+ Object[] expected = new Object[] {"sqrt", 4};
+ Object[] actual = sqrt(literal(4)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testSqrtLiteral() throws Exception {
+ Object[] expected = new Object[] {"sqrt", 4};
+ Object[] actual = sqrt(4).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testLog10() throws Exception {
+ Object[] expected = new Object[] {"log10", 10};
+ Object[] actual = log10(literal(10)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testLog10Literal() throws Exception {
+ Object[] expected = new Object[] {"log10", 10};
+ Object[] actual = log10(10).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testLn() throws Exception {
+ Object[] expected = new Object[] {"ln", 2};
+ Object[] actual = ln(literal(2)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testLnLiteral() throws Exception {
+ Object[] expected = new Object[] {"ln", 2};
+ Object[] actual = ln(2).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testLog2() throws Exception {
+ Object[] expected = new Object[] {"log2", 16};
+ Object[] actual = log2(literal(16)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testLog2Literal() throws Exception {
+ Object[] expected = new Object[] {"log2", 16};
+ Object[] actual = log2(16).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testSin() throws Exception {
+ Object[] expected = new Object[] {"sin", 45};
+ Object[] actual = sin(literal(45)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testSinLiteral() throws Exception {
+ Object[] expected = new Object[] {"sin", 45};
+ Object[] actual = sin(45).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testCos() throws Exception {
+ Object[] expected = new Object[] {"cos", 45};
+ Object[] actual = cos(literal(45)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testCosLiteral() throws Exception {
+ Object[] expected = new Object[] {"cos", 45};
+ Object[] actual = cos(45).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testTan() throws Exception {
+ Object[] expected = new Object[] {"tan", 45};
+ Object[] actual = tan(literal(45)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testTanLiteral() throws Exception {
+ Object[] expected = new Object[] {"tan", 45};
+ Object[] actual = tan(45).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testAsin() throws Exception {
+ Object[] expected = new Object[] {"asin", 45};
+ Object[] actual = asin(literal(45)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testAsinLiteral() throws Exception {
+ Object[] expected = new Object[] {"asin", 45};
+ Object[] actual = asin(45).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testAcos() throws Exception {
+ Object[] expected = new Object[] {"acos", 45};
+ Object[] actual = acos(literal(45)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testAcosLiteral() throws Exception {
+ Object[] expected = new Object[] {"acos", 45};
+ Object[] actual = acos(45).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testAtan() throws Exception {
+ Object[] expected = new Object[] {"atan", 45};
+ Object[] actual = atan(literal(45)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testAtanLiteral() throws Exception {
+ Object[] expected = new Object[] {"atan", 45};
+ Object[] actual = atan(45).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testMin() throws Exception {
+ Object[] expected = new Object[] {"min", 0, 1, 2, 3};
+ Object[] actual = min(literal(0), literal(1), literal(2), literal(3)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testMinLiteral() throws Exception {
+ Object[] expected = new Object[] {"min", 0, 1, 2, 3};
+ Object[] actual = min(0, 1, 2, 3).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testMax() throws Exception {
+ Object[] expected = new Object[] {"max", 0, 1, 2, 3};
+ Object[] actual = max(literal(0), literal(1), literal(2), literal(3)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testMaxLiteral() throws Exception {
+ Object[] expected = new Object[] {"max", 0, 1, 2, 3};
+ Object[] actual = max(0, 1, 2, 3).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testUpcase() throws Exception {
+ Object[] expected = new Object[] {"upcase", "string"};
+ Object[] actual = upcase(literal("string")).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testUpcaseLiteral() throws Exception {
+ Object[] expected = new Object[] {"upcase", "string"};
+ Object[] actual = upcase("string").toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testDowncase() throws Exception {
+ Object[] expected = new Object[] {"downcase", "string"};
+ Object[] actual = downcase(literal("string")).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testDowncaseLiteral() throws Exception {
+ Object[] expected = new Object[] {"downcase", "string"};
+ Object[] actual = downcase("string").toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testConcat() throws Exception {
+ Object[] expected = new Object[] {"concat", "foo", "bar"};
+ Object[] actual = concat(literal("foo"), literal("bar")).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testConcatLiteral() throws Exception {
+ Object[] expected = new Object[] {"concat", "foo", "bar"};
+ Object[] actual = concat("foo", "bar").toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testArray() throws Exception {
+ Object[] get = new Object[] {"get", "keyToArray"};
+ Object[] expected = new Object[] {"array", get};
+ Object[] actual = array(get(literal("keyToArray"))).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testArrayLiteral() throws Exception {
+ Object[] get = new Object[] {"get", "keyToArray"};
+ Object[] expected = new Object[] {"array", get};
+ Object[] actual = array(get("keyToArray")).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testTypeOf() throws Exception {
+ Object[] expected = new Object[] {"typeof", "value"};
+ Object[] actual = typeOf(literal("value")).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testString() throws Exception {
+ Object[] expected = new Object[] {"string", "value"};
+ Object[] actual = string(literal("value")).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testNumber() throws Exception {
+ Object[] expected = new Object[] {"number", 1};
+ Object[] actual = number(literal(1)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testBool() throws Exception {
+ Object[] expected = new Object[] {"boolean", true};
+ Object[] actual = bool(literal(true)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testObject() throws Exception {
+ Object object = new Object();
+ Object[] expected = new Object[] {"object", object};
+ Object[] actual = object(literal(object)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testToString() throws Exception {
+ Object[] expected = new Object[] {"to-string", 3};
+ Object[] actual = Expression.toString(literal(3)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testToNumber() throws Exception {
+ Object[] expected = new Object[] {"to-number", "3"};
+ Object[] actual = toNumber(literal("3")).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testToBool() throws Exception {
+ Object[] expected = new Object[] {"to-boolean", "true"};
+ Object[] actual = toBool(literal("true")).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testToColor() throws Exception {
+ Object[] expected = new Object[] {"to-color", "value"};
+ Object[] actual = toColor(literal("value")).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testLet() throws Exception {
+ Object[] expected = new Object[] {"let", "letName", "value"};
+ Object[] actual = let(literal("letName"), literal("value")).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testVar() throws Exception {
+ Object[] expected = new Object[] {"var", "letName"};
+ Object[] actual = var(literal("letName")).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testVarLiteral() throws Exception {
+ Object[] expected = new Object[] {"var", "letName"};
+ Object[] actual = var("letName").toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testVarExpression() throws Exception {
+ Object[] expected = new Object[] {"var", new Object[] {"get", "letName"}};
+ Object[] actual = var(get(literal("letName"))).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testVarExpressionLiteral() throws Exception {
+ Object[] expected = new Object[] {"var", new Object[] {"get", "letName"}};
+ Object[] actual = var(get("letName")).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testZoom() throws Exception {
+ Object[] expected = new Object[] {"zoom"};
+ Object[] actual = zoom().toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testStepBasic() throws Exception {
+ Object[] expected = new Object[] {"step", 12, 11, 0, 111, 1, 1111};
+ Object[] actual = step(literal(12), literal(11), literal(0), literal(111), literal(1), literal(1111)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testStepBasicLiteral() throws Exception {
+ Object[] expected = new Object[] {"step", new Object[] {"get", "line-width"}, 11, 0, 111, 1, 1111};
+ Object[] actual = step(get("line-width"), literal(11), stop(0, 111), stop(1, 1111)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testStepExpression() throws Exception {
+ Object[] input = new Object[] {"get", "key"};
+ Object[] number = new Object[] {"to-number", input};
+ Object[] expected = new Object[] {"step", number, 11, 0, 111, 1, 1111};
+ Object[] actual = step(toNumber(get(literal("key"))),
+ literal(11), literal(0), literal(111), literal(1), literal(1111)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testStepExpressionLiteral() throws Exception {
+ Object[] input = new Object[] {"get", "key"};
+ Object[] number = new Object[] {"to-number", input};
+ Object[] expected = new Object[] {"step", number, 11, 0, 111, 1, 1111};
+ Object[] actual = step(toNumber(get("key")), literal(11), stop(0, 111), stop(1, 1111)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testLinear() throws Exception {
+ Object[] stopZero = new Object[] {0, 1};
+ Object[] stopOne = new Object[] {1, 2};
+ Object[] stopTwo = new Object[] {2, 3};
+ Object[] expected = new Object[] {"interpolate", new Object[] {"linear"}, 12, stopZero, stopOne, stopTwo};
+ Object[] actual = interpolate(linear(), literal(12),
+ literal(stopZero), literal(stopOne), literal(stopTwo)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testLinearStops() throws Exception {
+ Object[] expected = new Object[] {"interpolate", new Object[] {"linear"}, 12, 0, 1, 1, 2, 2, 3};
+ Object[] actual = interpolate(linear(), literal(12), stop(0, 1), stop(1, 2), stop(2, 3)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testExponential() throws Exception {
+ Object[] exponential = new Object[] {"exponential", 12};
+ Object[] get = new Object[] {"get", "x"};
+ Object[] expected = new Object[] {"interpolate", exponential, get, 0, 100, 200};
+ Object[] actual = interpolate(exponential(literal(12)),
+ get(literal("x")), literal(0), literal(100), literal(200)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testExponentialLiteral() throws Exception {
+ Object[] exponential = new Object[] {"exponential", 12};
+ Object[] get = new Object[] {"get", "x"};
+ Object[] expected = new Object[] {"interpolate", exponential, get, 0, 100, 100, 200};
+ Object[] actual = interpolate(exponential(12), get("x"), stop(0, 100), stop(100, 200)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testExponentialExpressionLiteral() throws Exception {
+ Object[] getX = new Object[] {"get", "x"};
+ Object[] exponential = new Object[] {"exponential", getX};
+ Object[] getY = new Object[] {"get", "y"};
+ Object[] expected = new Object[] {"interpolate", exponential, getY, 0, 100, 100, 200};
+ Object[] actual = interpolate(exponential(get("x")), get("y"), stop(0, 100), stop(100, 200)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testCubicBezier() throws Exception {
+ Object[] cubicBezier = new Object[] {"cubic-bezier", 1, 1, 1, 1};
+ Object[] get = new Object[] {"get", "x"};
+ Object[] expected = new Object[] {"interpolate", cubicBezier, get, 0, 100, 100, 200};
+ Object[] actual = interpolate(cubicBezier(literal(1), literal(1), literal(1), literal(1)),
+ get(literal("x")), literal(0), literal(100), literal(100), literal(200)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testCubicBezierLiteral() throws Exception {
+ Object[] cubicBezier = new Object[] {"cubic-bezier", 1, 1, 1, 1};
+ Object[] get = new Object[] {"get", "x"};
+ Object[] expected = new Object[] {"interpolate", cubicBezier, get, 0, 100, 100, 200};
+ Object[] actual = interpolate(cubicBezier(1, 1, 1, 1), get("x"), stop(0, 100), stop(100, 200)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testCubicBezierExpression() throws Exception {
+ Object[] getX = new Object[] {"get", "x"};
+ Object[] getY = new Object[] {"get", "y"};
+ Object[] getZ = new Object[] {"get", "z"};
+ Object[] cubicBezier = new Object[] {"cubic-bezier", getZ, 1, getY, 1};
+ Object[] expected = new Object[] {"interpolate", cubicBezier, getX, 0, 100, 200};
+ Object[] actual = interpolate(cubicBezier(get(literal("z")), literal(1),
+ get(literal("y")), literal(1)), get(literal("x")), literal(0), literal(100), literal(200)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testCubicBezierExpressionLiteral() throws Exception {
+ Object[] getX = new Object[] {"get", "x"};
+ Object[] getY = new Object[] {"get", "y"};
+ Object[] getZ = new Object[] {"get", "z"};
+ Object[] cubicBezier = new Object[] {"cubic-bezier", getZ, 1, getY, 1};
+ Object[] expected = new Object[] {"interpolate", cubicBezier, getX, 0, 100, 100, 200};
+ Object[] actual = interpolate(cubicBezier(get("z"), literal(1), get("y"),
+ literal(1)), get("x"), stop(0, 100), stop(100, 200)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testExpressionConcatToString() throws Exception {
+ String expected = "[\"concat\", foo, bar]";
+ String actual = concat(literal("foo"), literal("bar")).toString();
+ assertEquals("toString should match", expected, actual);
+ }
+
+ @Test
+ public void testExpressionMinToString() throws Exception {
+ String expected = "[\"min\", 0, 1, 2, 3]";
+ String actual = min(0, 1, 2, 3).toString();
+ assertEquals("toString should match", expected, actual);
+ }
+
+ @Test
+ public void testExpressionExponentialToString() throws Exception {
+ String expected = "[\"interpolate\", [\"cubic-bezier\", 1, 1, 1, 1], [\"get\", x], 0, 100, 100, 200]";
+ String actual = interpolate(cubicBezier(literal(1), literal(1), literal(1), literal(1)),
+ get(literal("x")), literal(0), literal(100), literal(100), literal(200)).toString();
+ assertEquals("toString should match", expected, actual);
+ }
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle
index f45ad3dc3a..caff70e543 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle
+++ b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle
@@ -1,21 +1,21 @@
apply plugin: 'com.android.application'
android {
- compileSdkVersion rootProject.ext.compileSdkVersion
- buildToolsVersion rootProject.ext.buildToolsVersion
+ compileSdkVersion androidVersions.compileSdkVersion
+ buildToolsVersion androidVersions.buildToolsVersion
defaultConfig {
applicationId "com.mapbox.mapboxsdk.testapp"
- minSdkVersion rootProject.ext.minSdkVersion
- targetSdkVersion rootProject.ext.targetSdkVersion
- versionCode rootProject.ext.versionCode
- versionName rootProject.ext.versionName
+ minSdkVersion androidVersions.minSdkVersion
+ targetSdkVersion androidVersions.targetSdkVersion
+ versionCode 12
+ versionName "6.0.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_7
- targetCompatibility JavaVersion.VERSION_1_7
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
}
packagingOptions {
@@ -28,7 +28,7 @@ android {
baseline file("lint-baseline-local.xml")
checkAllWarnings true
warningsAsErrors true
- disable 'MissingTranslation', 'GoogleAppIndexingWarning', 'UnpackedNativeCode', 'IconDipSize', 'TypographyQuotes'
+ disable 'MissingTranslation', 'GoogleAppIndexingWarning', 'UnpackedNativeCode', 'IconDipSize', 'TypographyQuotes'
abortOnError false
}
@@ -52,36 +52,31 @@ android {
}
dependencies {
- compile(project(':MapboxGLAndroidSDK')) {
- transitive = true
- }
+ api(project(':MapboxGLAndroidSDK'))
+ implementation dependenciesList.mapboxJavaServices
- // Support libraries
- compile rootProject.ext.dep.supportAppcompatV7
- compile rootProject.ext.dep.supportRecyclerView
- compile rootProject.ext.dep.supportDesign
+ implementation dependenciesList.mapboxJavaTurf
- // Leak Canary
- debugCompile rootProject.ext.dep.leakCanaryDebug
- releaseCompile rootProject.ext.dep.leakCanaryRelease
+ implementation dependenciesList.supportAppcompatV7
+ implementation dependenciesList.supportRecyclerView
+ implementation dependenciesList.supportDesign
- // Mapbox Android Services (Java component)
- compile(rootProject.ext.dep.mapboxJavaServices) {
- transitive = true
- }
- compile rootProject.ext.dep.lost
+ // implementation dependenciesList.lost
+ implementation dependenciesList.gmsLocation
+ implementation dependenciesList.timber
+ debugImplementation dependenciesList.leakCanaryDebug
+ releaseImplementation dependenciesList.leakCanaryRelease
- // Testing dependencies
- androidTestCompile rootProject.ext.dep.supportAnnotations
- androidTestCompile rootProject.ext.dep.testRunner
- androidTestCompile rootProject.ext.dep.testRules
- androidTestCompile rootProject.ext.dep.testEspressoCore
- androidTestCompile rootProject.ext.dep.testEspressoIntents
+ androidTestImplementation dependenciesList.supportAnnotations
+ androidTestImplementation dependenciesList.testRunner
+ androidTestImplementation dependenciesList.testRules
+ androidTestImplementation dependenciesList.testEspressoCore
+ androidTestImplementation dependenciesList.testEspressoIntents
}
-apply from: 'gradle-make.gradle'
-apply from: 'gradle-config.gradle'
-apply from: 'gradle-checkstyle.gradle'
-apply from: '../gradle-lint.gradle'
+apply from: "${rootDir}/gradle/gradle-make.gradle"
+apply from: "${rootDir}/gradle/gradle-config.gradle"
+apply from: "${rootDir}/gradle/gradle-checkstyle.gradle"
+apply from: "${rootDir}/gradle/gradle-lint.gradle"
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/gradle-checkstyle.gradle b/platform/android/MapboxGLAndroidSDKTestApp/gradle-checkstyle.gradle
deleted file mode 100644
index e4e1ba0453..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/gradle-checkstyle.gradle
+++ /dev/null
@@ -1,19 +0,0 @@
-apply plugin: 'checkstyle'
-
-checkstyle {
- toolVersion = "7.1.1" // 7.3
- configFile = "../checkstyle.xml" as File
-}
-
-task checkstyle(type: Checkstyle) {
- description 'Checks if the code adheres to coding standards'
- group 'verification'
- configFile file("../checkstyle.xml")
- source 'src'
- include '**/*.java'
- exclude '**/gen/**'
- exclude '**/style/*LayerTest.java'
- exclude '**/style/LightTest.java'
- classpath = files()
- ignoreFailures = false
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java
index 3dbbaceb1a..b4bc118129 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java
@@ -1,8 +1,6 @@
package com.mapbox.mapboxsdk.maps;
import android.graphics.Color;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
import android.support.test.espresso.UiController;
import android.support.test.espresso.ViewAction;
import android.view.View;
@@ -70,26 +68,24 @@ public class MapboxMapTest extends BaseActivityTest {
@Test
public void testTransitionDuration() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- long transitionDuration = 600;
- mapboxMap.setTransitionDuration(transitionDuration);
- assertEquals("TransitionDuration should match", transitionDuration, mapboxMap.getTransitionDuration(), 0);
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ long transitionDuration = 600;
+ mapboxMap.setTransitionDuration(transitionDuration);
+ assertEquals(
+ "TransitionDuration should match", transitionDuration, mapboxMap.getTransitionDuration(), 0
+ );
}));
}
@Test
public void testTransitionDelay() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- long transitionDelay = 50;
- mapboxMap.setTransitionDelay(transitionDelay);
- assertEquals("TransitionDelay should match", transitionDelay, mapboxMap.getTransitionDelay(), 0);
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ long transitionDelay = 50;
+ mapboxMap.setTransitionDelay(transitionDelay);
+ assertEquals(
+ "TransitionDelay should match", transitionDelay, mapboxMap.getTransitionDelay(), 0
+ );
}));
}
@@ -99,40 +95,34 @@ public class MapboxMapTest extends BaseActivityTest {
@Test
public void testCameraPositionOnFinish() {
ViewUtils.checkViewIsDisplayed(R.id.mapView);
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
- final LatLng latLng = new LatLng(30.0, 30.0);
- mapboxMap.moveCamera(CameraUpdateFactory.newLatLng(latLng), new MapboxMap.CancelableCallback() {
- @Override
- public void onCancel() {
- }
+ final LatLng latLng = new LatLng(30.0, 30.0);
+ mapboxMap.moveCamera(CameraUpdateFactory.newLatLng(latLng), new MapboxMap.CancelableCallback() {
+ @Override
+ public void onCancel() {
+ }
- @Override
- public void onFinish() {
- LatLng cameraPositionLatLng = mapboxMap.getCameraPosition().target;
- Timber.d(cameraPositionLatLng.toString());
- assertEquals(cameraPositionLatLng.getLatitude(), latLng.getLatitude(), LAT_LNG_DELTA);
- assertEquals(cameraPositionLatLng.getLongitude(), latLng.getLongitude(), LAT_LNG_DELTA);
- }
- });
- }
+ @Override
+ public void onFinish() {
+ LatLng cameraPositionLatLng = mapboxMap.getCameraPosition().target;
+ Timber.d(cameraPositionLatLng.toString());
+ assertEquals(cameraPositionLatLng.getLatitude(), latLng.getLatitude(), LAT_LNG_DELTA);
+ assertEquals(cameraPositionLatLng.getLongitude(), latLng.getLongitude(), LAT_LNG_DELTA);
+ }
+ });
}));
}
@Test
public void testCameraForLatLngBounds() {
ViewUtils.checkViewIsDisplayed(R.id.mapView);
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- // set
- mapboxMap.setLatLngBoundsForCameraTarget(
- new LatLngBounds.Builder().include(new LatLng()).include(new LatLng(1, 1)).build());
- // reset
- mapboxMap.setLatLngBoundsForCameraTarget(null);
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ // set
+ mapboxMap.setLatLngBoundsForCameraTarget(
+ new LatLngBounds.Builder().include(new LatLng()).include(new LatLng(1, 1)).build());
+ // reset
+ mapboxMap.setLatLngBoundsForCameraTarget(null);
}));
}
@@ -143,12 +133,9 @@ public class MapboxMapTest extends BaseActivityTest {
@Test
public void testMinZoom() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- mapboxMap.setMinZoomPreference(10);
- assertEquals("MinZoom should match", 10, mapboxMap.getMinZoomLevel(), 10);
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ mapboxMap.setMinZoomPreference(10);
+ assertEquals("MinZoom should match", 10, mapboxMap.getMinZoomLevel(), 10);
}));
}
@@ -156,12 +143,9 @@ public class MapboxMapTest extends BaseActivityTest {
public void testMaxZoom() {
validateTestSetup();
final double zoom = 10;
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- mapboxMap.setMaxZoomPreference(zoom);
- assertEquals("MaxZoom should match", zoom, mapboxMap.getMaxZoomLevel(), 10);
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ mapboxMap.setMaxZoomPreference(zoom);
+ assertEquals("MaxZoom should match", zoom, mapboxMap.getMaxZoomLevel(), 10);
}));
}
@@ -169,14 +153,11 @@ public class MapboxMapTest extends BaseActivityTest {
@Ignore
public void testInitialZoomLevels() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- assertEquals("MaxZoom should match", MapboxConstants.MAXIMUM_ZOOM, mapboxMap.getMaxZoomLevel(),
- TestConstants.ZOOM_DELTA);
- assertEquals("MinZoom should match", MapboxConstants.MINIMUM_ZOOM, mapboxMap.getMinZoomLevel(),
- TestConstants.ZOOM_DELTA);
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ assertEquals("MaxZoom should match", MapboxConstants.MAXIMUM_ZOOM, mapboxMap.getMaxZoomLevel(),
+ TestConstants.ZOOM_DELTA);
+ assertEquals("MinZoom should match", MapboxConstants.MINIMUM_ZOOM, mapboxMap.getMinZoomLevel(),
+ TestConstants.ZOOM_DELTA);
}));
}
@@ -197,43 +178,28 @@ public class MapboxMapTest extends BaseActivityTest {
@Test
public void testConcurrentInfoWindowEnabled() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- mapboxMap.setAllowConcurrentMultipleOpenInfoWindows(true);
- assertTrue("ConcurrentWindows should be true", mapboxMap.isAllowConcurrentMultipleOpenInfoWindows());
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ mapboxMap.setAllowConcurrentMultipleOpenInfoWindows(true);
+ assertTrue("ConcurrentWindows should be true", mapboxMap.isAllowConcurrentMultipleOpenInfoWindows());
}));
}
@Test
public void testConcurrentInfoWindowDisabled() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- mapboxMap.setAllowConcurrentMultipleOpenInfoWindows(false);
- assertFalse("ConcurrentWindows should be false", mapboxMap.isAllowConcurrentMultipleOpenInfoWindows());
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ mapboxMap.setAllowConcurrentMultipleOpenInfoWindows(false);
+ assertFalse("ConcurrentWindows should be false", mapboxMap.isAllowConcurrentMultipleOpenInfoWindows());
}));
}
@Test
public void testInfoWindowAdapter() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- MapboxMap.InfoWindowAdapter infoWindowAdapter = new MapboxMap.InfoWindowAdapter() {
- @Nullable
- @Override
- public View getInfoWindow(@NonNull Marker marker) {
- return null;
- }
- };
- mapboxMap.setInfoWindowAdapter(infoWindowAdapter);
- assertEquals("InfoWindowAdpter should be the same", infoWindowAdapter, mapboxMap.getInfoWindowAdapter());
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ MapboxMap.InfoWindowAdapter infoWindowAdapter = marker -> null;
+ mapboxMap.setInfoWindowAdapter(infoWindowAdapter);
+ assertEquals("InfoWindowAdpter should be the same", infoWindowAdapter, mapboxMap.getInfoWindowAdapter());
}));
}
@@ -245,12 +211,9 @@ public class MapboxMapTest extends BaseActivityTest {
@Ignore /* disabled due to enabling permissions during test #7177 */
public void testMyLocationEnabled() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- mapboxMap.setMyLocationEnabled(true);
- assertTrue("MyLocationEnabled should be true", mapboxMap.isMyLocationEnabled());
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ mapboxMap.setMyLocationEnabled(true);
+ assertTrue("MyLocationEnabled should be true", mapboxMap.isMyLocationEnabled());
}));
}
@@ -258,12 +221,9 @@ public class MapboxMapTest extends BaseActivityTest {
@Ignore /* can't create handler inside thread that not called Looper.prepare() */
public void testMyLocationDisabled() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- mapboxMap.setMyLocationEnabled(false);
- assertFalse("MyLocationEnabled should be false", mapboxMap.isMyLocationEnabled());
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ mapboxMap.setMyLocationEnabled(false);
+ assertFalse("MyLocationEnabled should be false", mapboxMap.isMyLocationEnabled());
}));
}
@@ -274,73 +234,49 @@ public class MapboxMapTest extends BaseActivityTest {
@Test
public void testFpsListener() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- MapboxMap.OnFpsChangedListener fpsChangedListener = new MapboxMap.OnFpsChangedListener() {
- @Override
- public void onFpsChanged(double fps) {
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ MapboxMap.OnFpsChangedListener fpsChangedListener = fps -> {
- }
- };
- mapboxMap.setOnFpsChangedListener(fpsChangedListener);
- assertEquals("FpsListener should match", fpsChangedListener, mapboxMap.getOnFpsChangedListener());
- }
+ };
+ mapboxMap.setOnFpsChangedListener(fpsChangedListener);
+ assertEquals("FpsListener should match", fpsChangedListener, mapboxMap.getOnFpsChangedListener());
}));
}
@Test
public void testInfoWindowClickListener() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- MapboxMap.OnInfoWindowClickListener clickListener = new MapboxMap.OnInfoWindowClickListener() {
- @Override
- public boolean onInfoWindowClick(@NonNull Marker marker) {
- return false;
- }
- };
- mapboxMap.setOnInfoWindowClickListener(clickListener);
- assertEquals("InfoWidowClickListener should match", clickListener, mapboxMap.getOnInfoWindowClickListener());
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ MapboxMap.OnInfoWindowClickListener clickListener = marker -> false;
+ mapboxMap.setOnInfoWindowClickListener(clickListener);
+ assertEquals(
+ "InfoWidowClickListener should match", clickListener, mapboxMap.getOnInfoWindowClickListener()
+ );
}));
}
@Test
public void testInfoWindowCloseListener() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- MapboxMap.OnInfoWindowCloseListener listener = new MapboxMap.OnInfoWindowCloseListener() {
- @Override
- public void onInfoWindowClose(Marker marker) {
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ MapboxMap.OnInfoWindowCloseListener listener = marker -> {
- }
- };
- mapboxMap.setOnInfoWindowCloseListener(listener);
- assertEquals("InfoWindowCloseListener should match", listener, mapboxMap.getOnInfoWindowCloseListener());
- }
+ };
+ mapboxMap.setOnInfoWindowCloseListener(listener);
+ assertEquals("InfoWindowCloseListener should match", listener, mapboxMap.getOnInfoWindowCloseListener());
}));
}
@Test
public void testInfoWindowLongClickListener() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- MapboxMap.OnInfoWindowLongClickListener listener = new MapboxMap.OnInfoWindowLongClickListener() {
- @Override
- public void onInfoWindowLongClick(Marker marker) {
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ MapboxMap.OnInfoWindowLongClickListener listener = marker -> {
- }
- };
- mapboxMap.setOnInfoWindowLongClickListener(listener);
- assertEquals("InfoWindowLongClickListener should match", listener,
- mapboxMap.getOnInfoWindowLongClickListener());
- }
+ };
+ mapboxMap.setOnInfoWindowLongClickListener(listener);
+ assertEquals("InfoWindowLongClickListener should match", listener,
+ mapboxMap.getOnInfoWindowLongClickListener());
}));
}
@@ -351,13 +287,10 @@ public class MapboxMapTest extends BaseActivityTest {
@Test
public void testAddMarker() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- MarkerOptions markerOptions = new MarkerOptions().position(new LatLng());
- Marker marker = mapboxMap.addMarker(markerOptions);
- assertTrue("Marker should be contained", mapboxMap.getMarkers().contains(marker));
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ MarkerOptions markerOptions = new MarkerOptions().position(new LatLng());
+ Marker marker = mapboxMap.addMarker(markerOptions);
+ assertTrue("Marker should be contained", mapboxMap.getMarkers().contains(marker));
}));
}
@@ -369,442 +302,356 @@ public class MapboxMapTest extends BaseActivityTest {
@Test
public void testAddMarkers() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- List<BaseMarkerOptions> markerList = new ArrayList<>();
- MarkerOptions markerOptions1 = new MarkerOptions().position(new LatLng()).title("a");
- MarkerOptions markerOptions2 = new MarkerOptions().position(new LatLng()).title("b");
- markerList.add(markerOptions1);
- markerList.add(markerOptions2);
- List<Marker> markers = mapboxMap.addMarkers(markerList);
- assertEquals("Markers size should be 2", 2, mapboxMap.getMarkers().size());
- assertTrue(mapboxMap.getMarkers().contains(markers.get(0)));
- assertTrue(mapboxMap.getMarkers().contains(markers.get(1)));
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ List<BaseMarkerOptions> markerList = new ArrayList<>();
+ MarkerOptions markerOptions1 = new MarkerOptions().position(new LatLng()).title("a");
+ MarkerOptions markerOptions2 = new MarkerOptions().position(new LatLng()).title("b");
+ markerList.add(markerOptions1);
+ markerList.add(markerOptions2);
+ List<Marker> markers = mapboxMap.addMarkers(markerList);
+ assertEquals("Markers size should be 2", 2, mapboxMap.getMarkers().size());
+ assertTrue(mapboxMap.getMarkers().contains(markers.get(0)));
+ assertTrue(mapboxMap.getMarkers().contains(markers.get(1)));
}));
}
@Test
public void testAddMarkersEmpty() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- List<BaseMarkerOptions> markerList = new ArrayList<>();
- mapboxMap.addMarkers(markerList);
- assertEquals("Markers size should be 0", 0, mapboxMap.getMarkers().size());
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ List<BaseMarkerOptions> markerList = new ArrayList<>();
+ mapboxMap.addMarkers(markerList);
+ assertEquals("Markers size should be 0", 0, mapboxMap.getMarkers().size());
}));
}
@Test
public void testAddMarkersSingleMarker() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- List<BaseMarkerOptions> markerList = new ArrayList<>();
- MarkerOptions markerOptions = new MarkerOptions().title("a").position(new LatLng());
- markerList.add(markerOptions);
- List<Marker> markers = mapboxMap.addMarkers(markerList);
- assertEquals("Markers size should be 1", 1, mapboxMap.getMarkers().size());
- assertTrue(mapboxMap.getMarkers().contains(markers.get(0)));
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ List<BaseMarkerOptions> markerList = new ArrayList<>();
+ MarkerOptions markerOptions = new MarkerOptions().title("a").position(new LatLng());
+ markerList.add(markerOptions);
+ List<Marker> markers = mapboxMap.addMarkers(markerList);
+ assertEquals("Markers size should be 1", 1, mapboxMap.getMarkers().size());
+ assertTrue(mapboxMap.getMarkers().contains(markers.get(0)));
}));
}
@Test
public void testAddPolygon() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- PolygonOptions polygonOptions = new PolygonOptions().add(new LatLng());
- Polygon polygon = mapboxMap.addPolygon(polygonOptions);
- assertTrue("Polygon should be contained", mapboxMap.getPolygons().contains(polygon));
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ PolygonOptions polygonOptions = new PolygonOptions().add(new LatLng());
+ Polygon polygon = mapboxMap.addPolygon(polygonOptions);
+ assertTrue("Polygon should be contained", mapboxMap.getPolygons().contains(polygon));
}));
}
@Test
public void testAddEmptyPolygon() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- PolygonOptions polygonOptions = new PolygonOptions();
- Polygon polygon = mapboxMap.addPolygon(polygonOptions);
- assertTrue("Polygon should be ignored", !mapboxMap.getPolygons().contains(polygon));
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ PolygonOptions polygonOptions = new PolygonOptions();
+ Polygon polygon = mapboxMap.addPolygon(polygonOptions);
+ assertTrue("Polygon should be ignored", !mapboxMap.getPolygons().contains(polygon));
}));
}
@Test
public void testAddPolygons() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- List<PolygonOptions> polygonList = new ArrayList<>();
- PolygonOptions polygonOptions1 = new PolygonOptions().fillColor(Color.BLACK).add(new LatLng());
- PolygonOptions polygonOptions2 = new PolygonOptions().fillColor(Color.WHITE).add(new LatLng());
- PolygonOptions polygonOptions3 = new PolygonOptions();
- polygonList.add(polygonOptions1);
- polygonList.add(polygonOptions2);
- polygonList.add(polygonOptions3);
- mapboxMap.addPolygons(polygonList);
- assertEquals("Polygons size should be 2", 2, mapboxMap.getPolygons().size());
- assertTrue(mapboxMap.getPolygons().contains(polygonOptions1.getPolygon()));
- assertTrue(mapboxMap.getPolygons().contains(polygonOptions2.getPolygon()));
- assertTrue("Polygon should be ignored", !mapboxMap.getPolygons().contains(polygonOptions3.getPolygon()));
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ List<PolygonOptions> polygonList = new ArrayList<>();
+ PolygonOptions polygonOptions1 = new PolygonOptions().fillColor(Color.BLACK).add(new LatLng());
+ PolygonOptions polygonOptions2 = new PolygonOptions().fillColor(Color.WHITE).add(new LatLng());
+ PolygonOptions polygonOptions3 = new PolygonOptions();
+ polygonList.add(polygonOptions1);
+ polygonList.add(polygonOptions2);
+ polygonList.add(polygonOptions3);
+ mapboxMap.addPolygons(polygonList);
+ assertEquals("Polygons size should be 2", 2, mapboxMap.getPolygons().size());
+ assertTrue(mapboxMap.getPolygons().contains(polygonOptions1.getPolygon()));
+ assertTrue(mapboxMap.getPolygons().contains(polygonOptions2.getPolygon()));
+ assertTrue("Polygon should be ignored", !mapboxMap.getPolygons().contains(polygonOptions3.getPolygon()));
}));
}
@Test
public void addPolygonsEmpty() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- mapboxMap.addPolygons(new ArrayList<PolygonOptions>());
- assertEquals("Polygons size should be 0", 0, mapboxMap.getPolygons().size());
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ mapboxMap.addPolygons(new ArrayList<PolygonOptions>());
+ assertEquals("Polygons size should be 0", 0, mapboxMap.getPolygons().size());
}));
}
@Test
public void addPolygonsSingle() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- List<PolygonOptions> polygonList = new ArrayList<>();
- PolygonOptions polygonOptions = new PolygonOptions().fillColor(Color.BLACK).add(new LatLng());
- polygonList.add(polygonOptions);
- mapboxMap.addPolygons(polygonList);
- assertEquals("Polygons size should be 1", 1, mapboxMap.getPolygons().size());
- assertTrue(mapboxMap.getPolygons().contains(polygonOptions.getPolygon()));
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ List<PolygonOptions> polygonList = new ArrayList<>();
+ PolygonOptions polygonOptions = new PolygonOptions().fillColor(Color.BLACK).add(new LatLng());
+ polygonList.add(polygonOptions);
+ mapboxMap.addPolygons(polygonList);
+ assertEquals("Polygons size should be 1", 1, mapboxMap.getPolygons().size());
+ assertTrue(mapboxMap.getPolygons().contains(polygonOptions.getPolygon()));
}));
}
@Test
public void testAddPolyline() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- PolylineOptions polylineOptions = new PolylineOptions().add(new LatLng());
- Polyline polyline = mapboxMap.addPolyline(polylineOptions);
- assertTrue("Polyline should be contained", mapboxMap.getPolylines().contains(polyline));
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ PolylineOptions polylineOptions = new PolylineOptions().add(new LatLng());
+ Polyline polyline = mapboxMap.addPolyline(polylineOptions);
+ assertTrue("Polyline should be contained", mapboxMap.getPolylines().contains(polyline));
}));
}
@Test
public void testAddEmptyPolyline() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- PolylineOptions polylineOptions = new PolylineOptions();
- Polyline polyline = mapboxMap.addPolyline(polylineOptions);
- assertTrue("Polyline should be ignored", !mapboxMap.getPolylines().contains(polyline));
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ PolylineOptions polylineOptions = new PolylineOptions();
+ Polyline polyline = mapboxMap.addPolyline(polylineOptions);
+ assertTrue("Polyline should be ignored", !mapboxMap.getPolylines().contains(polyline));
}));
}
@Test
public void testAddPolylines() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- List<PolylineOptions> polylineList = new ArrayList<>();
- PolylineOptions polygonOptions1 = new PolylineOptions().color(Color.BLACK).add(new LatLng());
- PolylineOptions polygonOptions2 = new PolylineOptions().color(Color.WHITE).add(new LatLng());
- PolylineOptions polygonOptions3 = new PolylineOptions();
- polylineList.add(polygonOptions1);
- polylineList.add(polygonOptions2);
- polylineList.add(polygonOptions3);
- mapboxMap.addPolylines(polylineList);
- assertEquals("Polygons size should be 2", 2, mapboxMap.getPolylines().size());
- assertTrue(mapboxMap.getPolylines().contains(polygonOptions1.getPolyline()));
- assertTrue(mapboxMap.getPolylines().contains(polygonOptions2.getPolyline()));
- assertTrue("Polyline should be ignored", !mapboxMap.getPolylines().contains(polygonOptions3.getPolyline()));
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ List<PolylineOptions> polylineList = new ArrayList<>();
+ PolylineOptions polygonOptions1 = new PolylineOptions().color(Color.BLACK).add(new LatLng());
+ PolylineOptions polygonOptions2 = new PolylineOptions().color(Color.WHITE).add(new LatLng());
+ PolylineOptions polygonOptions3 = new PolylineOptions();
+ polylineList.add(polygonOptions1);
+ polylineList.add(polygonOptions2);
+ polylineList.add(polygonOptions3);
+ mapboxMap.addPolylines(polylineList);
+ assertEquals("Polygons size should be 2", 2, mapboxMap.getPolylines().size());
+ assertTrue(mapboxMap.getPolylines().contains(polygonOptions1.getPolyline()));
+ assertTrue(mapboxMap.getPolylines().contains(polygonOptions2.getPolyline()));
+ assertTrue(
+ "Polyline should be ignored", !mapboxMap.getPolylines().contains(polygonOptions3.getPolyline())
+ );
}));
}
@Test
public void testAddPolylinesEmpty() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- mapboxMap.addPolylines(new ArrayList<PolylineOptions>());
- assertEquals("Polygons size should be 0", 0, mapboxMap.getPolylines().size());
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ mapboxMap.addPolylines(new ArrayList<PolylineOptions>());
+ assertEquals("Polygons size should be 0", 0, mapboxMap.getPolylines().size());
}));
}
@Test
public void testAddPolylinesSingle() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- List<PolylineOptions> polylineList = new ArrayList<>();
- PolylineOptions polygonOptions = new PolylineOptions().color(Color.BLACK).add(new LatLng());
- polylineList.add(polygonOptions);
- mapboxMap.addPolylines(polylineList);
- assertEquals("Polygons size should be 1", 1, mapboxMap.getPolylines().size());
- assertTrue(mapboxMap.getPolylines().contains(polygonOptions.getPolyline()));
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ List<PolylineOptions> polylineList = new ArrayList<>();
+ PolylineOptions polygonOptions = new PolylineOptions().color(Color.BLACK).add(new LatLng());
+ polylineList.add(polygonOptions);
+ mapboxMap.addPolylines(polylineList);
+ assertEquals("Polygons size should be 1", 1, mapboxMap.getPolylines().size());
+ assertTrue(mapboxMap.getPolylines().contains(polygonOptions.getPolyline()));
}));
}
@Test
public void testRemoveMarker() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- MarkerOptions markerOptions = new MarkerOptions().position(new LatLng());
- Marker marker = mapboxMap.addMarker(markerOptions);
- mapboxMap.removeMarker(marker);
- assertTrue("Markers should be empty", mapboxMap.getMarkers().isEmpty());
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ MarkerOptions markerOptions = new MarkerOptions().position(new LatLng());
+ Marker marker = mapboxMap.addMarker(markerOptions);
+ mapboxMap.removeMarker(marker);
+ assertTrue("Markers should be empty", mapboxMap.getMarkers().isEmpty());
}));
}
@Test
public void testRemovePolygon() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- PolygonOptions polygonOptions = new PolygonOptions();
- Polygon polygon = mapboxMap.addPolygon(polygonOptions);
- mapboxMap.removePolygon(polygon);
- assertTrue("Polygons should be empty", mapboxMap.getPolylines().isEmpty());
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ PolygonOptions polygonOptions = new PolygonOptions();
+ Polygon polygon = mapboxMap.addPolygon(polygonOptions);
+ mapboxMap.removePolygon(polygon);
+ assertTrue("Polygons should be empty", mapboxMap.getPolylines().isEmpty());
}));
}
@Test
public void testRemovePolyline() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- PolylineOptions polylineOptions = new PolylineOptions();
- Polyline polyline = mapboxMap.addPolyline(polylineOptions);
- mapboxMap.removePolyline(polyline);
- assertTrue("Polylines should be empty", mapboxMap.getPolylines().isEmpty());
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ PolylineOptions polylineOptions = new PolylineOptions();
+ Polyline polyline = mapboxMap.addPolyline(polylineOptions);
+ mapboxMap.removePolyline(polyline);
+ assertTrue("Polylines should be empty", mapboxMap.getPolylines().isEmpty());
}));
}
@Test
public void testRemoveAnnotation() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- MarkerOptions markerOptions = new MarkerOptions().position(new LatLng());
- Marker marker = mapboxMap.addMarker(markerOptions);
- mapboxMap.removeAnnotation(marker);
- assertTrue("Annotations should be empty", mapboxMap.getAnnotations().isEmpty());
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ MarkerOptions markerOptions = new MarkerOptions().position(new LatLng());
+ Marker marker = mapboxMap.addMarker(markerOptions);
+ mapboxMap.removeAnnotation(marker);
+ assertTrue("Annotations should be empty", mapboxMap.getAnnotations().isEmpty());
}));
}
@Test
public void testRemoveAnnotationById() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- MarkerOptions markerOptions = new MarkerOptions().position(new LatLng());
- mapboxMap.addMarker(markerOptions);
- // id will always be 0 in unit tests
- mapboxMap.removeAnnotation(0);
- assertTrue("Annotations should be empty", mapboxMap.getAnnotations().isEmpty());
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ MarkerOptions markerOptions = new MarkerOptions().position(new LatLng());
+ mapboxMap.addMarker(markerOptions);
+ // id will always be 0 in unit tests
+ mapboxMap.removeAnnotation(0);
+ assertTrue("Annotations should be empty", mapboxMap.getAnnotations().isEmpty());
}));
}
@Test
public void testRemoveAnnotations() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- List<BaseMarkerOptions> markerList = new ArrayList<>();
- MarkerOptions markerOptions1 = new MarkerOptions().title("a").position(new LatLng());
- MarkerOptions markerOptions2 = new MarkerOptions().title("b").position(new LatLng());
- markerList.add(markerOptions1);
- markerList.add(markerOptions2);
- mapboxMap.addMarkers(markerList);
- mapboxMap.removeAnnotations();
- assertTrue("Annotations should be empty", mapboxMap.getAnnotations().isEmpty());
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ List<BaseMarkerOptions> markerList = new ArrayList<>();
+ MarkerOptions markerOptions1 = new MarkerOptions().title("a").position(new LatLng());
+ MarkerOptions markerOptions2 = new MarkerOptions().title("b").position(new LatLng());
+ markerList.add(markerOptions1);
+ markerList.add(markerOptions2);
+ mapboxMap.addMarkers(markerList);
+ mapboxMap.removeAnnotations();
+ assertTrue("Annotations should be empty", mapboxMap.getAnnotations().isEmpty());
}));
}
@Test
public void testClear() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- List<BaseMarkerOptions> markerList = new ArrayList<>();
- MarkerOptions markerOptions1 = new MarkerOptions().title("a").position(new LatLng());
- MarkerOptions markerOptions2 = new MarkerOptions().title("b").position(new LatLng());
- markerList.add(markerOptions1);
- markerList.add(markerOptions2);
- mapboxMap.addMarkers(markerList);
- mapboxMap.clear();
- assertTrue("Annotations should be empty", mapboxMap.getAnnotations().isEmpty());
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ List<BaseMarkerOptions> markerList = new ArrayList<>();
+ MarkerOptions markerOptions1 = new MarkerOptions().title("a").position(new LatLng());
+ MarkerOptions markerOptions2 = new MarkerOptions().title("b").position(new LatLng());
+ markerList.add(markerOptions1);
+ markerList.add(markerOptions2);
+ mapboxMap.addMarkers(markerList);
+ mapboxMap.clear();
+ assertTrue("Annotations should be empty", mapboxMap.getAnnotations().isEmpty());
}));
}
@Test
public void testRemoveAnnotationsByList() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- List<BaseMarkerOptions> markerList = new ArrayList<>();
- MarkerOptions markerOptions1 = new MarkerOptions().title("a").position(new LatLng());
- MarkerOptions markerOptions2 = new MarkerOptions().title("b").position(new LatLng());
- markerList.add(markerOptions1);
- markerList.add(markerOptions2);
- List<Marker> markers = mapboxMap.addMarkers(markerList);
- Marker marker = mapboxMap.addMarker(new MarkerOptions().position(new LatLng()).title("c"));
- mapboxMap.removeAnnotations(markers);
- assertTrue("Annotations should not be empty", mapboxMap.getAnnotations().size() == 1);
- assertTrue("Marker should be contained", mapboxMap.getAnnotations().contains(marker));
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ List<BaseMarkerOptions> markerList = new ArrayList<>();
+ MarkerOptions markerOptions1 = new MarkerOptions().title("a").position(new LatLng());
+ MarkerOptions markerOptions2 = new MarkerOptions().title("b").position(new LatLng());
+ markerList.add(markerOptions1);
+ markerList.add(markerOptions2);
+ List<Marker> markers = mapboxMap.addMarkers(markerList);
+ Marker marker = mapboxMap.addMarker(new MarkerOptions().position(new LatLng()).title("c"));
+ mapboxMap.removeAnnotations(markers);
+ assertTrue("Annotations should not be empty", mapboxMap.getAnnotations().size() == 1);
+ assertTrue("Marker should be contained", mapboxMap.getAnnotations().contains(marker));
}));
}
@Test
public void testGetAnnotationById() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- MarkerOptions markerOptions = new MarkerOptions().position(new LatLng());
- Marker initialMarker = mapboxMap.addMarker(markerOptions);
- Marker retrievedMarker = (Marker) mapboxMap.getAnnotation(0);
- assertEquals("Markers should match", initialMarker, retrievedMarker);
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ MarkerOptions markerOptions = new MarkerOptions().position(new LatLng());
+ Marker initialMarker = mapboxMap.addMarker(markerOptions);
+ Marker retrievedMarker = (Marker) mapboxMap.getAnnotation(0);
+ assertEquals("Markers should match", initialMarker, retrievedMarker);
}));
}
@Test
public void testGetAnnotations() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- assertNotNull("Annotations should be non null", mapboxMap.getAnnotations());
- }
- }));
+ onView(withId(R.id.mapView)).perform(
+ new MapboxMapAction((uiController, view) ->
+ assertNotNull("Annotations should be non null", mapboxMap.getAnnotations()))
+ );
}
@Test
public void testGetMarkers() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- assertNotNull("Markers should be non null", mapboxMap.getMarkers());
- }
- }));
+ onView(withId(R.id.mapView)).perform(
+ new MapboxMapAction((uiController, view) ->
+ assertNotNull("Markers should be non null", mapboxMap.getMarkers()))
+ );
}
@Test
public void testGetPolygons() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- assertNotNull("Polygons should be non null", mapboxMap.getPolygons());
- }
- }));
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) ->
+ assertNotNull("Polygons should be non null", mapboxMap.getPolygons()))
+ );
}
@Test
public void testGetPolylines() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- assertNotNull("Polylines should be non null", mapboxMap.getPolylines());
- }
- }));
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) ->
+ assertNotNull("Polylines should be non null", mapboxMap.getPolylines()))
+ );
}
@Test
public void testGetSelectedMarkers() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- assertNotNull("Selected markers should be non null", mapboxMap.getSelectedMarkers());
- }
- }));
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) ->
+ assertNotNull("Selected markers should be non null", mapboxMap.getSelectedMarkers()))
+ );
}
@Test
public void testSelectMarker() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- MarkerOptions markerOptions = new MarkerOptions().position(new LatLng());
- Marker marker = mapboxMap.addMarker(markerOptions);
- mapboxMap.selectMarker(marker);
- assertTrue("Marker should be contained", mapboxMap.getSelectedMarkers().contains(marker));
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ MarkerOptions markerOptions = new MarkerOptions().position(new LatLng());
+ Marker marker = mapboxMap.addMarker(markerOptions);
+ mapboxMap.selectMarker(marker);
+ assertTrue("Marker should be contained", mapboxMap.getSelectedMarkers().contains(marker));
}));
}
@Test
public void testDeselectMarker() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- MarkerOptions markerOptions = new MarkerOptions().position(new LatLng());
- Marker marker = mapboxMap.addMarker(markerOptions);
- mapboxMap.selectMarker(marker);
- mapboxMap.deselectMarker(marker);
- assertTrue("Selected markers should be empty", mapboxMap.getSelectedMarkers().isEmpty());
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ MarkerOptions markerOptions = new MarkerOptions().position(new LatLng());
+ Marker marker = mapboxMap.addMarker(markerOptions);
+ mapboxMap.selectMarker(marker);
+ mapboxMap.deselectMarker(marker);
+ assertTrue("Selected markers should be empty", mapboxMap.getSelectedMarkers().isEmpty());
}));
}
@Test
public void testDeselectMarkers() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- MarkerOptions markerOptions = new MarkerOptions().position(new LatLng());
- Marker marker1 = mapboxMap.addMarker(markerOptions);
- Marker marker2 = mapboxMap.addMarker(markerOptions);
- mapboxMap.selectMarker(marker1);
- mapboxMap.selectMarker(marker2);
- mapboxMap.deselectMarkers();
- assertTrue("Selected markers should be empty", mapboxMap.getSelectedMarkers().isEmpty());
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ MarkerOptions markerOptions = new MarkerOptions().position(new LatLng());
+ Marker marker1 = mapboxMap.addMarker(markerOptions);
+ Marker marker2 = mapboxMap.addMarker(markerOptions);
+ mapboxMap.selectMarker(marker1);
+ mapboxMap.selectMarker(marker2);
+ mapboxMap.deselectMarkers();
+ assertTrue("Selected markers should be empty", mapboxMap.getSelectedMarkers().isEmpty());
}));
}
@@ -813,14 +660,11 @@ public class MapboxMapTest extends BaseActivityTest {
@Test
public void testTilePrefetch() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- mapboxMap.setPrefetchesTiles(true);
- assertTrue(mapboxMap.getPrefetchesTiles());
- mapboxMap.setPrefetchesTiles(false);
- assertFalse(mapboxMap.getPrefetchesTiles());
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ mapboxMap.setPrefetchesTiles(true);
+ assertTrue(mapboxMap.getPrefetchesTiles());
+ mapboxMap.setPrefetchesTiles(false);
+ assertFalse(mapboxMap.getPrefetchesTiles());
}));
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/OrientationTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/OrientationTest.java
new file mode 100644
index 0000000000..7a1fcbf5f3
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/OrientationTest.java
@@ -0,0 +1,41 @@
+package com.mapbox.mapboxsdk.maps;
+
+import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest;
+import com.mapbox.mapboxsdk.testapp.activity.camera.CameraAnimationTypeActivity;
+import org.junit.Test;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.matcher.ViewMatchers.isRoot;
+import static com.mapbox.mapboxsdk.testapp.action.OrientationChangeAction.orientationLandscape;
+import static com.mapbox.mapboxsdk.testapp.action.OrientationChangeAction.orientationLandscapeReverse;
+import static com.mapbox.mapboxsdk.testapp.action.OrientationChangeAction.orientationPortrait;
+import static com.mapbox.mapboxsdk.testapp.action.OrientationChangeAction.orientationPortraitReverse;
+
+public class OrientationTest extends BaseActivityTest {
+
+ @Test
+ public void testChangeDeviceOrientation() {
+ onView(isRoot()).perform(orientationLandscape());
+ waitLoop(2200);
+ onView(isRoot()).perform(orientationPortrait());
+ waitLoop(2500);
+ onView(isRoot()).perform(orientationLandscapeReverse());
+ waitLoop(500);
+ onView(isRoot()).perform(orientationPortraitReverse());
+ waitLoop(1250);
+ onView(isRoot()).perform(orientationLandscape());
+ waitLoop(750);
+ onView(isRoot()).perform(orientationPortrait());
+ waitLoop(950);
+ onView(isRoot()).perform(orientationLandscapeReverse());
+ onView(isRoot()).perform(orientationPortraitReverse());
+ onView(isRoot()).perform(orientationLandscape());
+ onView(isRoot()).perform(orientationPortrait());
+ }
+
+ @Override
+ protected Class getActivityClass() {
+ return CameraAnimationTypeActivity.class;
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/action/OrientationChangeAction.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/action/OrientationChangeAction.java
new file mode 100644
index 0000000000..7f73d6a7f3
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/action/OrientationChangeAction.java
@@ -0,0 +1,74 @@
+package com.mapbox.mapboxsdk.testapp.action;
+
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.ActivityInfo;
+import android.support.test.espresso.UiController;
+import android.support.test.espresso.ViewAction;
+import android.view.View;
+import android.view.ViewGroup;
+import org.hamcrest.Matcher;
+
+import static android.support.test.espresso.matcher.ViewMatchers.isRoot;
+
+public class OrientationChangeAction implements ViewAction {
+
+ private final int orientation;
+
+ private OrientationChangeAction(int orientation) {
+ this.orientation = orientation;
+ }
+
+ public static ViewAction orientationLandscape() {
+ return new OrientationChangeAction(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+ }
+
+ public static ViewAction orientationPortrait() {
+ return new OrientationChangeAction(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+ }
+
+ public static ViewAction orientationLandscapeReverse() {
+ return new OrientationChangeAction(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
+ }
+
+ public static ViewAction orientationPortraitReverse() {
+ return new OrientationChangeAction(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
+ }
+
+ @Override
+ public Matcher<View> getConstraints() {
+ return isRoot();
+ }
+
+ @Override
+ public String getDescription() {
+ return "change orientation to " + orientation;
+ }
+
+ @Override
+ public void perform(UiController uiController, View view) {
+ uiController.loopMainThreadUntilIdle();
+ Activity activity = getActivity(view.getContext());
+ if (activity == null && view instanceof ViewGroup) {
+ ViewGroup v = (ViewGroup) view;
+ int c = v.getChildCount();
+ for (int i = 0; i < c && activity == null; ++i) {
+ activity = getActivity(v.getChildAt(i).getContext());
+ }
+ }
+ activity.setRequestedOrientation(orientation);
+ }
+
+ private Activity getActivity(Context context) {
+ while (context instanceof ContextWrapper) {
+ if (context instanceof Activity) {
+ return (Activity) context;
+ }
+ context = ((ContextWrapper) context).getBaseContext();
+ }
+ return null;
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/BaseActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/BaseActivityTest.java
index 61bff1f113..3f32443021 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/BaseActivityTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/BaseActivityTest.java
@@ -10,18 +10,14 @@ import android.support.test.espresso.UiController;
import android.support.test.espresso.ViewAction;
import android.support.test.rule.ActivityTestRule;
import android.view.View;
-
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.testapp.R;
import com.mapbox.mapboxsdk.testapp.utils.OnMapReadyIdlingResource;
-
import junit.framework.Assert;
-
import org.hamcrest.Matcher;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
-
import timber.log.Timber;
import static android.support.test.espresso.Espresso.onView;
@@ -72,7 +68,11 @@ public abstract class BaseActivityTest {
}
protected void waitLoop() {
- onView(withId(R.id.mapView)).perform(new LoopAction(500));
+ waitLoop(500);
+ }
+
+ protected void waitLoop(long waitTime) {
+ onView(withId(R.id.mapView)).perform(new LoopAction(waitTime));
}
static boolean isConnected(Context context) {
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/MarkerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/MarkerTest.java
index e511135b99..11756d3d32 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/MarkerTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/MarkerTest.java
@@ -1,11 +1,8 @@
package com.mapbox.mapboxsdk.testapp.annotations;
-import android.support.test.espresso.UiController;
-
import com.mapbox.mapboxsdk.annotations.Marker;
import com.mapbox.mapboxsdk.annotations.MarkerOptions;
import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.testapp.action.MapboxMapAction;
import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest;
import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity;
@@ -34,25 +31,22 @@ public class MarkerTest extends BaseActivityTest {
@Ignore
public void addMarkerTest() {
validateTestSetup();
- MapboxMapAction.invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertEquals("Markers should be empty", 0, mapboxMap.getMarkers().size());
+ MapboxMapAction.invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertEquals("Markers should be empty", 0, mapboxMap.getMarkers().size());
- MarkerOptions options = new MarkerOptions();
- options.setPosition(new LatLng());
- options.setSnippet(TestConstants.TEXT_MARKER_SNIPPET);
- options.setTitle(TestConstants.TEXT_MARKER_TITLE);
- marker = mapboxMap.addMarker(options);
+ MarkerOptions options = new MarkerOptions();
+ options.setPosition(new LatLng());
+ options.setSnippet(TestConstants.TEXT_MARKER_SNIPPET);
+ options.setTitle(TestConstants.TEXT_MARKER_TITLE);
+ marker = mapboxMap.addMarker(options);
- assertEquals("Markers size should be 1, ", 1, mapboxMap.getMarkers().size());
- assertEquals("Marker id should be 0", 0, marker.getId());
- assertEquals("Marker target should match", new LatLng(), marker.getPosition());
- assertEquals("Marker snippet should match", TestConstants.TEXT_MARKER_SNIPPET, marker.getSnippet());
- assertEquals("Marker target should match", TestConstants.TEXT_MARKER_TITLE, marker.getTitle());
- mapboxMap.clear();
- assertEquals("Markers should be empty", 0, mapboxMap.getMarkers().size());
- }
+ assertEquals("Markers size should be 1, ", 1, mapboxMap.getMarkers().size());
+ assertEquals("Marker id should be 0", 0, marker.getId());
+ assertEquals("Marker target should match", new LatLng(), marker.getPosition());
+ assertEquals("Marker snippet should match", TestConstants.TEXT_MARKER_SNIPPET, marker.getSnippet());
+ assertEquals("Marker target should match", TestConstants.TEXT_MARKER_TITLE, marker.getTitle());
+ mapboxMap.clear();
+ assertEquals("Markers should be empty", 0, mapboxMap.getMarkers().size());
});
}
@@ -60,16 +54,13 @@ public class MarkerTest extends BaseActivityTest {
@Ignore
public void showInfoWindowTest() {
validateTestSetup();
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- final MarkerOptions options = new MarkerOptions();
- options.setPosition(new LatLng());
- options.setSnippet(TestConstants.TEXT_MARKER_SNIPPET);
- options.setTitle(TestConstants.TEXT_MARKER_TITLE);
- marker = mapboxMap.addMarker(options);
- mapboxMap.selectMarker(marker);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ final MarkerOptions options = new MarkerOptions();
+ options.setPosition(new LatLng());
+ options.setSnippet(TestConstants.TEXT_MARKER_SNIPPET);
+ options.setTitle(TestConstants.TEXT_MARKER_TITLE);
+ marker = mapboxMap.addMarker(options);
+ mapboxMap.selectMarker(marker);
});
onView(withText(TestConstants.TEXT_MARKER_TITLE)).check(matches(isDisplayed()));
onView(withText(TestConstants.TEXT_MARKER_SNIPPET)).check(matches(isDisplayed()));
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/MarkerViewTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/MarkerViewTest.java
index 8bf847c2e7..ad153336a4 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/MarkerViewTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/MarkerViewTest.java
@@ -1,11 +1,7 @@
package com.mapbox.mapboxsdk.testapp.annotations;
-import android.support.test.espresso.UiController;
-
import com.mapbox.mapboxsdk.annotations.Marker;
import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.testapp.action.MapboxMapAction;
import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest;
import com.mapbox.mapboxsdk.testapp.activity.annotation.MarkerViewActivity;
import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity;
@@ -36,24 +32,21 @@ public class MarkerViewTest extends BaseActivityTest {
public void addMarkerViewTest() {
validateTestSetup();
addAdapter();
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertEquals("Markers should be empty", 0, mapboxMap.getMarkers().size());
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertEquals("Markers should be empty", 0, mapboxMap.getMarkers().size());
- TextMarkerViewOptions options = new TextMarkerViewOptions();
- options.text(TestConstants.TEXT_MARKER_TEXT);
- options.position(new LatLng());
- options.snippet(TestConstants.TEXT_MARKER_SNIPPET);
- options.title(TestConstants.TEXT_MARKER_TITLE);
- marker = mapboxMap.addMarker(options);
- assertEquals("Markers size should be 1, ", 1, mapboxMap.getMarkers().size());
- assertEquals("Marker id should be 0", 0, marker.getId());
- assertEquals("Marker target should match", new LatLng(), marker.getPosition());
- assertEquals("Marker snippet should match", TestConstants.TEXT_MARKER_SNIPPET, marker.getSnippet());
- assertEquals("Marker target should match", TestConstants.TEXT_MARKER_TITLE, marker.getTitle());
- uiController.loopMainThreadForAtLeast(500);
- }
+ TextMarkerViewOptions options = new TextMarkerViewOptions();
+ options.text(TestConstants.TEXT_MARKER_TEXT);
+ options.position(new LatLng());
+ options.snippet(TestConstants.TEXT_MARKER_SNIPPET);
+ options.title(TestConstants.TEXT_MARKER_TITLE);
+ marker = mapboxMap.addMarker(options);
+ assertEquals("Markers size should be 1, ", 1, mapboxMap.getMarkers().size());
+ assertEquals("Marker id should be 0", 0, marker.getId());
+ assertEquals("Marker target should match", new LatLng(), marker.getPosition());
+ assertEquals("Marker snippet should match", TestConstants.TEXT_MARKER_SNIPPET, marker.getSnippet());
+ assertEquals("Marker target should match", TestConstants.TEXT_MARKER_TITLE, marker.getTitle());
+ uiController.loopMainThreadForAtLeast(500);
});
onView(withText(TestConstants.TEXT_MARKER_TEXT)).check(matches(isDisplayed()));
}
@@ -63,18 +56,15 @@ public class MarkerViewTest extends BaseActivityTest {
public void showInfoWindowTest() {
validateTestSetup();
addAdapter();
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- final TextMarkerViewOptions options = new TextMarkerViewOptions();
- options.position(new LatLng());
- options.text(TestConstants.TEXT_MARKER_TEXT);
- options.snippet(TestConstants.TEXT_MARKER_SNIPPET);
- options.title(TestConstants.TEXT_MARKER_TITLE);
- marker = mapboxMap.addMarker(options);
- uiController.loopMainThreadForAtLeast(500);
- mapboxMap.selectMarker(marker);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ final TextMarkerViewOptions options = new TextMarkerViewOptions();
+ options.position(new LatLng());
+ options.text(TestConstants.TEXT_MARKER_TEXT);
+ options.snippet(TestConstants.TEXT_MARKER_SNIPPET);
+ options.title(TestConstants.TEXT_MARKER_TITLE);
+ marker = mapboxMap.addMarker(options);
+ uiController.loopMainThreadForAtLeast(500);
+ mapboxMap.selectMarker(marker);
});
onView(withText(TestConstants.TEXT_MARKER_TEXT)).check(matches(isDisplayed()));
onView(withText(TestConstants.TEXT_MARKER_TITLE)).check(matches(isDisplayed()));
@@ -82,13 +72,8 @@ public class MarkerViewTest extends BaseActivityTest {
}
private void addAdapter() {
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- mapboxMap.getMarkerViewManager().addMarkerViewAdapter(
- new MarkerViewActivity.TextAdapter(rule.getActivity(), mapboxMap));
- }
- });
+ invoke(mapboxMap, (uiController, mapboxMap) -> mapboxMap.getMarkerViewManager().addMarkerViewAdapter(
+ new MarkerViewActivity.TextAdapter(rule.getActivity(), mapboxMap)));
}
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/PolygonTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/PolygonTest.java
index c90a5f60c6..be969f29c3 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/PolygonTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/PolygonTest.java
@@ -1,13 +1,10 @@
package com.mapbox.mapboxsdk.testapp.annotations;
import android.graphics.Color;
-import android.support.test.espresso.UiController;
import com.mapbox.mapboxsdk.annotations.Polygon;
import com.mapbox.mapboxsdk.annotations.PolygonOptions;
import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.testapp.action.MapboxMapAction;
import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest;
import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity;
@@ -28,31 +25,28 @@ public class PolygonTest extends BaseActivityTest {
@Ignore
public void addPolygonTest() {
validateTestSetup();
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- LatLng latLngOne = new LatLng();
- LatLng latLngTwo = new LatLng(1, 0);
- LatLng latLngThree = new LatLng(1, 1);
-
- assertEquals("Polygons should be empty", 0, mapboxMap.getPolygons().size());
-
- final PolygonOptions options = new PolygonOptions();
- options.strokeColor(Color.BLUE);
- options.fillColor(Color.RED);
- options.add(latLngOne);
- options.add(latLngTwo);
- options.add(latLngThree);
- Polygon polygon = mapboxMap.addPolygon(options);
-
- assertEquals("Polygons should be 1", 1, mapboxMap.getPolygons().size());
- assertEquals("Polygon id should be 0", 0, polygon.getId());
- assertEquals("Polygon points size should match", 3, polygon.getPoints().size());
- assertEquals("Polygon stroke color should match", Color.BLUE, polygon.getStrokeColor());
- assertEquals("Polygon target should match", Color.RED, polygon.getFillColor());
- mapboxMap.clear();
- assertEquals("Polygons should be empty", 0, mapboxMap.getPolygons().size());
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ LatLng latLngOne = new LatLng();
+ LatLng latLngTwo = new LatLng(1, 0);
+ LatLng latLngThree = new LatLng(1, 1);
+
+ assertEquals("Polygons should be empty", 0, mapboxMap.getPolygons().size());
+
+ final PolygonOptions options = new PolygonOptions();
+ options.strokeColor(Color.BLUE);
+ options.fillColor(Color.RED);
+ options.add(latLngOne);
+ options.add(latLngTwo);
+ options.add(latLngThree);
+ Polygon polygon = mapboxMap.addPolygon(options);
+
+ assertEquals("Polygons should be 1", 1, mapboxMap.getPolygons().size());
+ assertEquals("Polygon id should be 0", 0, polygon.getId());
+ assertEquals("Polygon points size should match", 3, polygon.getPoints().size());
+ assertEquals("Polygon stroke color should match", Color.BLUE, polygon.getStrokeColor());
+ assertEquals("Polygon target should match", Color.RED, polygon.getFillColor());
+ mapboxMap.clear();
+ assertEquals("Polygons should be empty", 0, mapboxMap.getPolygons().size());
});
}
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/PolylineTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/PolylineTest.java
index e6aa661729..b9c68ceab7 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/PolylineTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/PolylineTest.java
@@ -1,13 +1,10 @@
package com.mapbox.mapboxsdk.testapp.annotations;
import android.graphics.Color;
-import android.support.test.espresso.UiController;
import com.mapbox.mapboxsdk.annotations.Polyline;
import com.mapbox.mapboxsdk.annotations.PolylineOptions;
import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.testapp.action.MapboxMapAction;
import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest;
import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity;
@@ -28,27 +25,24 @@ public class PolylineTest extends BaseActivityTest {
@Ignore
public void addPolylineTest() {
validateTestSetup();
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- LatLng latLngOne = new LatLng();
- LatLng latLngTwo = new LatLng(1, 0);
-
- assertEquals("Polygons should be empty", 0, mapboxMap.getPolygons().size());
-
- final PolylineOptions options = new PolylineOptions();
- options.color(Color.BLUE);
- options.add(latLngOne);
- options.add(latLngTwo);
- Polyline polyline = mapboxMap.addPolyline(options);
-
- assertEquals("Polylines should be 1", 1, mapboxMap.getPolylines().size());
- assertEquals("Polyline id should be 0", 0, polyline.getId());
- assertEquals("Polyline points size should match", 2, polyline.getPoints().size());
- assertEquals("Polyline stroke color should match", Color.BLUE, polyline.getColor());
- mapboxMap.clear();
- assertEquals("Polyline should be empty", 0, mapboxMap.getPolylines().size());
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ LatLng latLngOne = new LatLng();
+ LatLng latLngTwo = new LatLng(1, 0);
+
+ assertEquals("Polygons should be empty", 0, mapboxMap.getPolygons().size());
+
+ final PolylineOptions options = new PolylineOptions();
+ options.color(Color.BLUE);
+ options.add(latLngOne);
+ options.add(latLngTwo);
+ Polyline polyline = mapboxMap.addPolyline(options);
+
+ assertEquals("Polylines should be 1", 1, mapboxMap.getPolylines().size());
+ assertEquals("Polyline id should be 0", 0, polyline.getId());
+ assertEquals("Polyline points size should match", 2, polyline.getPoints().size());
+ assertEquals("Polyline stroke color should match", Color.BLUE, polyline.getColor());
+ mapboxMap.clear();
+ assertEquals("Polyline should be empty", 0, mapboxMap.getPolylines().size());
});
}
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraAnimateTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraAnimateTest.java
index c16c8f05bb..94aec734a4 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraAnimateTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraAnimateTest.java
@@ -1,14 +1,11 @@
package com.mapbox.mapboxsdk.testapp.camera;
import android.graphics.PointF;
-import android.support.test.espresso.UiController;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-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.TestConstants;
@@ -30,23 +27,20 @@ public class CameraAnimateTest extends BaseActivityTest {
@Ignore
public void testAnimateToCameraPositionTarget() {
validateTestSetup();
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- float zoom = 1.0f;
- LatLng moveTarget = new LatLng(1, 1);
- CameraPosition initialPosition = new CameraPosition.Builder().target(
- new LatLng()).zoom(zoom).bearing(0).tilt(0).build();
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Default camera position should match default", cameraPosition, initialPosition);
- mapboxMap.animateCamera(CameraUpdateFactory.newLatLng(moveTarget));
- uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
- cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Moved camera position latitude should match", cameraPosition.target.getLatitude(),
- moveTarget.getLatitude(), TestConstants.LAT_LNG_DELTA);
- assertEquals("Moved camera position longitude should match", cameraPosition.target.getLongitude(),
- moveTarget.getLongitude(), TestConstants.LAT_LNG_DELTA);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ float zoom = 1.0f;
+ LatLng moveTarget = new LatLng(1, 1);
+ CameraPosition initialPosition = new CameraPosition.Builder().target(
+ new LatLng()).zoom(zoom).bearing(0).tilt(0).build();
+ CameraPosition cameraPosition = mapboxMap.getCameraPosition();
+ assertEquals("Default camera position should match default", cameraPosition, initialPosition);
+ mapboxMap.animateCamera(CameraUpdateFactory.newLatLng(moveTarget));
+ uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
+ cameraPosition = mapboxMap.getCameraPosition();
+ assertEquals("Moved camera position latitude should match", cameraPosition.target.getLatitude(),
+ moveTarget.getLatitude(), TestConstants.LAT_LNG_DELTA);
+ assertEquals("Moved camera position longitude should match", cameraPosition.target.getLongitude(),
+ moveTarget.getLongitude(), TestConstants.LAT_LNG_DELTA);
});
}
@@ -54,20 +48,17 @@ public class CameraAnimateTest extends BaseActivityTest {
@Ignore
public void testAnimateToCameraPositionTargetZoom() {
validateTestSetup();
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- final float moveZoom = 15.5f;
- final LatLng moveTarget = new LatLng(1.0000000001, 1.0000000003);
- mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(moveTarget, moveZoom));
- uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Moved camera position latitude should match", cameraPosition.target.getLatitude(),
- moveTarget.getLatitude(), TestConstants.LAT_LNG_DELTA);
- assertEquals("Moved camera position longitude should match", cameraPosition.target.getLongitude(),
- moveTarget.getLongitude(), TestConstants.LAT_LNG_DELTA);
- assertEquals("Moved zoom should match", cameraPosition.zoom, moveZoom, TestConstants.ZOOM_DELTA);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ final float moveZoom = 15.5f;
+ final LatLng moveTarget = new LatLng(1.0000000001, 1.0000000003);
+ mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(moveTarget, moveZoom));
+ uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
+ CameraPosition cameraPosition = mapboxMap.getCameraPosition();
+ assertEquals("Moved camera position latitude should match", cameraPosition.target.getLatitude(),
+ moveTarget.getLatitude(), TestConstants.LAT_LNG_DELTA);
+ assertEquals("Moved camera position longitude should match", cameraPosition.target.getLongitude(),
+ moveTarget.getLongitude(), TestConstants.LAT_LNG_DELTA);
+ assertEquals("Moved zoom should match", cameraPosition.zoom, moveZoom, TestConstants.ZOOM_DELTA);
});
}
@@ -75,32 +66,29 @@ public class CameraAnimateTest extends BaseActivityTest {
@Ignore
public void testAnimateToCameraPosition() {
validateTestSetup();
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- final LatLng moveTarget = new LatLng(1.0000000001, 1.0000000003);
- final float moveZoom = 15.5f;
- final float moveTilt = 45.5f;
- final float moveBearing = 12.5f;
-
- mapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(
- new CameraPosition.Builder()
- .target(moveTarget)
- .zoom(moveZoom)
- .tilt(moveTilt)
- .bearing(moveBearing)
- .build())
- );
- uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Moved camera position latitude should match", cameraPosition.target.getLatitude(),
- moveTarget.getLatitude(), TestConstants.LAT_LNG_DELTA);
- assertEquals("Moved camera position longitude should match", cameraPosition.target.getLongitude(),
- moveTarget.getLongitude(), TestConstants.LAT_LNG_DELTA);
- assertEquals("Moved zoom should match", cameraPosition.zoom, moveZoom, TestConstants.ZOOM_DELTA);
- assertEquals("Moved zoom should match", cameraPosition.tilt, moveTilt, TestConstants.TILT_DELTA);
- assertEquals("Moved bearing should match", cameraPosition.bearing, moveBearing, TestConstants.BEARING_DELTA);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ final LatLng moveTarget = new LatLng(1.0000000001, 1.0000000003);
+ final float moveZoom = 15.5f;
+ final float moveTilt = 45.5f;
+ final float moveBearing = 12.5f;
+
+ mapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(
+ new CameraPosition.Builder()
+ .target(moveTarget)
+ .zoom(moveZoom)
+ .tilt(moveTilt)
+ .bearing(moveBearing)
+ .build())
+ );
+ uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
+ CameraPosition cameraPosition = mapboxMap.getCameraPosition();
+ assertEquals("Moved camera position latitude should match", cameraPosition.target.getLatitude(),
+ moveTarget.getLatitude(), TestConstants.LAT_LNG_DELTA);
+ assertEquals("Moved camera position longitude should match", cameraPosition.target.getLongitude(),
+ moveTarget.getLongitude(), TestConstants.LAT_LNG_DELTA);
+ assertEquals("Moved zoom should match", cameraPosition.zoom, moveZoom, TestConstants.ZOOM_DELTA);
+ assertEquals("Moved zoom should match", cameraPosition.tilt, moveTilt, TestConstants.TILT_DELTA);
+ assertEquals("Moved bearing should match", cameraPosition.bearing, moveBearing, TestConstants.BEARING_DELTA);
});
}
@@ -108,27 +96,24 @@ public class CameraAnimateTest extends BaseActivityTest {
@Ignore
public void testAnimateToBounds() {
validateTestSetup();
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- final LatLng centerBounds = new LatLng(1, 1);
- LatLng cornerOne = new LatLng();
- LatLng cornerTwo = new LatLng(2, 2);
- final LatLngBounds.Builder builder = new LatLngBounds.Builder();
- builder.include(cornerOne);
- builder.include(cornerTwo);
- mapboxMap.animateCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), 0));
- uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Moved camera position latitude should match center bounds",
- cameraPosition.target.getLatitude(),
- centerBounds.getLatitude(),
- TestConstants.LAT_LNG_DELTA);
- assertEquals("Moved camera position longitude should match center bounds",
- cameraPosition.target.getLongitude(),
- centerBounds.getLongitude(),
- TestConstants.LAT_LNG_DELTA);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ final LatLng centerBounds = new LatLng(1, 1);
+ LatLng cornerOne = new LatLng();
+ LatLng cornerTwo = new LatLng(2, 2);
+ final LatLngBounds.Builder builder = new LatLngBounds.Builder();
+ builder.include(cornerOne);
+ builder.include(cornerTwo);
+ mapboxMap.animateCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), 0));
+ uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
+ CameraPosition cameraPosition = mapboxMap.getCameraPosition();
+ assertEquals("Moved camera position latitude should match center bounds",
+ cameraPosition.target.getLatitude(),
+ centerBounds.getLatitude(),
+ TestConstants.LAT_LNG_DELTA);
+ assertEquals("Moved camera position longitude should match center bounds",
+ cameraPosition.target.getLongitude(),
+ centerBounds.getLongitude(),
+ TestConstants.LAT_LNG_DELTA);
});
}
@@ -136,21 +121,18 @@ public class CameraAnimateTest extends BaseActivityTest {
@Ignore
public void testAnimateToMoveBy() {
validateTestSetup();
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- final PointF centerPoint = mapboxMap.getProjection().toScreenLocation(mapboxMap.getCameraPosition().target);
- final LatLng moveTarget = new LatLng(2, 2);
- final PointF moveTargetPoint = mapboxMap.getProjection().toScreenLocation(moveTarget);
- mapboxMap.animateCamera(CameraUpdateFactory.scrollBy(
- moveTargetPoint.x - centerPoint.x, moveTargetPoint.y - centerPoint.y));
- uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Moved camera position latitude should match", cameraPosition.target.getLatitude(),
- moveTarget.getLatitude(), TestConstants.LAT_LNG_DELTA_LARGE);
- assertEquals("Moved camera position longitude should match", cameraPosition.target.getLongitude(),
- moveTarget.getLongitude(), TestConstants.LAT_LNG_DELTA_LARGE);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ final PointF centerPoint = mapboxMap.getProjection().toScreenLocation(mapboxMap.getCameraPosition().target);
+ final LatLng moveTarget = new LatLng(2, 2);
+ final PointF moveTargetPoint = mapboxMap.getProjection().toScreenLocation(moveTarget);
+ mapboxMap.animateCamera(CameraUpdateFactory.scrollBy(
+ moveTargetPoint.x - centerPoint.x, moveTargetPoint.y - centerPoint.y));
+ uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
+ CameraPosition cameraPosition = mapboxMap.getCameraPosition();
+ assertEquals("Moved camera position latitude should match", cameraPosition.target.getLatitude(),
+ moveTarget.getLatitude(), TestConstants.LAT_LNG_DELTA_LARGE);
+ assertEquals("Moved camera position longitude should match", cameraPosition.target.getLongitude(),
+ moveTarget.getLongitude(), TestConstants.LAT_LNG_DELTA_LARGE);
});
}
@@ -158,16 +140,13 @@ public class CameraAnimateTest extends BaseActivityTest {
@Ignore
public void testAnimateToZoomIn() {
validateTestSetup();
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- float zoom = 1.0f;
- mapboxMap.animateCamera(CameraUpdateFactory.zoomIn());
- uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Moved camera zoom should match moved camera zoom", cameraPosition.zoom, zoom + 1,
- TestConstants.ZOOM_DELTA);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ float zoom = 1.0f;
+ mapboxMap.animateCamera(CameraUpdateFactory.zoomIn());
+ uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
+ CameraPosition cameraPosition = mapboxMap.getCameraPosition();
+ assertEquals("Moved camera zoom should match moved camera zoom", cameraPosition.zoom, zoom + 1,
+ TestConstants.ZOOM_DELTA);
});
}
@@ -175,18 +154,15 @@ public class CameraAnimateTest extends BaseActivityTest {
@Ignore
public void testAnimateToZoomOut() {
validateTestSetup();
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- float zoom = 10.0f;
- mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(), zoom));
- uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
- mapboxMap.animateCamera(CameraUpdateFactory.zoomOut());
- uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Moved camera zoom should match moved camera zoom", cameraPosition.zoom, zoom - 1,
- TestConstants.ZOOM_DELTA);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ float zoom = 10.0f;
+ mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(), zoom));
+ uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
+ mapboxMap.animateCamera(CameraUpdateFactory.zoomOut());
+ uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
+ CameraPosition cameraPosition = mapboxMap.getCameraPosition();
+ assertEquals("Moved camera zoom should match moved camera zoom", cameraPosition.zoom, zoom - 1,
+ TestConstants.ZOOM_DELTA);
});
}
@@ -194,17 +170,14 @@ public class CameraAnimateTest extends BaseActivityTest {
@Ignore
public void testAnimateToZoomBy() {
validateTestSetup();
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- float zoom = 1.0f;
- final float zoomBy = 2.45f;
- mapboxMap.animateCamera(CameraUpdateFactory.zoomBy(zoomBy));
- uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Moved camera zoom should match moved camera zoom", cameraPosition.zoom, zoom + zoomBy,
- TestConstants.ZOOM_DELTA);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ float zoom = 1.0f;
+ final float zoomBy = 2.45f;
+ mapboxMap.animateCamera(CameraUpdateFactory.zoomBy(zoomBy));
+ uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
+ CameraPosition cameraPosition = mapboxMap.getCameraPosition();
+ assertEquals("Moved camera zoom should match moved camera zoom", cameraPosition.zoom, zoom + zoomBy,
+ TestConstants.ZOOM_DELTA);
});
}
@@ -212,16 +185,13 @@ public class CameraAnimateTest extends BaseActivityTest {
@Ignore
public void testAnimateToZoomTo() {
validateTestSetup();
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- final float zoomTo = 2.45f;
- mapboxMap.animateCamera(CameraUpdateFactory.zoomTo(zoomTo));
- uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Moved camera zoom should match moved camera zoom", cameraPosition.zoom, zoomTo,
- TestConstants.ZOOM_DELTA);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ final float zoomTo = 2.45f;
+ mapboxMap.animateCamera(CameraUpdateFactory.zoomTo(zoomTo));
+ uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
+ CameraPosition cameraPosition = mapboxMap.getCameraPosition();
+ assertEquals("Moved camera zoom should match moved camera zoom", cameraPosition.zoom, zoomTo,
+ TestConstants.ZOOM_DELTA);
});
}
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraEaseTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraEaseTest.java
index 8c67af7da3..e7c8d6b7bb 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraEaseTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraEaseTest.java
@@ -1,14 +1,11 @@
package com.mapbox.mapboxsdk.testapp.camera;
import android.graphics.PointF;
-import android.support.test.espresso.UiController;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-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.TestConstants;
@@ -30,23 +27,20 @@ public class CameraEaseTest extends BaseActivityTest {
@Ignore
public void testEaseToCameraPositionTarget() {
validateTestSetup();
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- float zoom = 1.0f;
- LatLng moveTarget = new LatLng(1, 1);
- CameraPosition initialPosition = new CameraPosition.Builder().target(
- new LatLng()).zoom(zoom).bearing(0).tilt(0).build();
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Default camera position should match default", cameraPosition, initialPosition);
- mapboxMap.easeCamera(CameraUpdateFactory.newLatLng(moveTarget));
- uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
- cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Moved camera position latitude should match", cameraPosition.target.getLatitude(),
- moveTarget.getLatitude(), TestConstants.LAT_LNG_DELTA);
- assertEquals("Moved camera position longitude should match", cameraPosition.target.getLongitude(),
- moveTarget.getLongitude(), TestConstants.LAT_LNG_DELTA);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ float zoom = 1.0f;
+ LatLng moveTarget = new LatLng(1, 1);
+ CameraPosition initialPosition = new CameraPosition.Builder().target(
+ new LatLng()).zoom(zoom).bearing(0).tilt(0).build();
+ CameraPosition cameraPosition = mapboxMap.getCameraPosition();
+ assertEquals("Default camera position should match default", cameraPosition, initialPosition);
+ mapboxMap.easeCamera(CameraUpdateFactory.newLatLng(moveTarget));
+ uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
+ cameraPosition = mapboxMap.getCameraPosition();
+ assertEquals("Moved camera position latitude should match", cameraPosition.target.getLatitude(),
+ moveTarget.getLatitude(), TestConstants.LAT_LNG_DELTA);
+ assertEquals("Moved camera position longitude should match", cameraPosition.target.getLongitude(),
+ moveTarget.getLongitude(), TestConstants.LAT_LNG_DELTA);
});
}
@@ -54,20 +48,17 @@ public class CameraEaseTest extends BaseActivityTest {
@Ignore
public void testEaseToCameraPositionTargetZoom() {
validateTestSetup();
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- final float moveZoom = 15.5f;
- final LatLng moveTarget = new LatLng(1.0000000001, 1.0000000003);
- mapboxMap.easeCamera(CameraUpdateFactory.newLatLngZoom(moveTarget, moveZoom));
- uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Moved camera position latitude should match", cameraPosition.target.getLatitude(),
- moveTarget.getLatitude(), TestConstants.LAT_LNG_DELTA);
- assertEquals("Moved camera position longitude should match", cameraPosition.target.getLongitude(),
- moveTarget.getLongitude(), TestConstants.LAT_LNG_DELTA);
- assertEquals("Moved zoom should match", cameraPosition.zoom, moveZoom, TestConstants.ZOOM_DELTA);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ final float moveZoom = 15.5f;
+ final LatLng moveTarget = new LatLng(1.0000000001, 1.0000000003);
+ mapboxMap.easeCamera(CameraUpdateFactory.newLatLngZoom(moveTarget, moveZoom));
+ uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
+ CameraPosition cameraPosition = mapboxMap.getCameraPosition();
+ assertEquals("Moved camera position latitude should match", cameraPosition.target.getLatitude(),
+ moveTarget.getLatitude(), TestConstants.LAT_LNG_DELTA);
+ assertEquals("Moved camera position longitude should match", cameraPosition.target.getLongitude(),
+ moveTarget.getLongitude(), TestConstants.LAT_LNG_DELTA);
+ assertEquals("Moved zoom should match", cameraPosition.zoom, moveZoom, TestConstants.ZOOM_DELTA);
});
}
@@ -75,32 +66,29 @@ public class CameraEaseTest extends BaseActivityTest {
@Ignore
public void testEaseToCameraPosition() {
validateTestSetup();
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- final LatLng moveTarget = new LatLng(1.0000000001, 1.0000000003);
- final float moveZoom = 15.5f;
- final float moveTilt = 45.5f;
- final float moveBearing = 12.5f;
-
- mapboxMap.easeCamera(CameraUpdateFactory.newCameraPosition(
- new CameraPosition.Builder()
- .target(moveTarget)
- .zoom(moveZoom)
- .tilt(moveTilt)
- .bearing(moveBearing)
- .build())
- );
- uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Moved camera position latitude should match", cameraPosition.target.getLatitude(),
- moveTarget.getLatitude(), TestConstants.LAT_LNG_DELTA);
- assertEquals("Moved camera position longitude should match", cameraPosition.target.getLongitude(),
- moveTarget.getLongitude(), TestConstants.LAT_LNG_DELTA);
- assertEquals("Moved zoom should match", cameraPosition.zoom, moveZoom, TestConstants.ZOOM_DELTA);
- assertEquals("Moved zoom should match", cameraPosition.tilt, moveTilt, TestConstants.TILT_DELTA);
- assertEquals("Moved bearing should match", cameraPosition.bearing, moveBearing, TestConstants.BEARING_DELTA);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ final LatLng moveTarget = new LatLng(1.0000000001, 1.0000000003);
+ final float moveZoom = 15.5f;
+ final float moveTilt = 45.5f;
+ final float moveBearing = 12.5f;
+
+ mapboxMap.easeCamera(CameraUpdateFactory.newCameraPosition(
+ new CameraPosition.Builder()
+ .target(moveTarget)
+ .zoom(moveZoom)
+ .tilt(moveTilt)
+ .bearing(moveBearing)
+ .build())
+ );
+ uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
+ CameraPosition cameraPosition = mapboxMap.getCameraPosition();
+ assertEquals("Moved camera position latitude should match", cameraPosition.target.getLatitude(),
+ moveTarget.getLatitude(), TestConstants.LAT_LNG_DELTA);
+ assertEquals("Moved camera position longitude should match", cameraPosition.target.getLongitude(),
+ moveTarget.getLongitude(), TestConstants.LAT_LNG_DELTA);
+ assertEquals("Moved zoom should match", cameraPosition.zoom, moveZoom, TestConstants.ZOOM_DELTA);
+ assertEquals("Moved zoom should match", cameraPosition.tilt, moveTilt, TestConstants.TILT_DELTA);
+ assertEquals("Moved bearing should match", cameraPosition.bearing, moveBearing, TestConstants.BEARING_DELTA);
});
}
@@ -108,27 +96,24 @@ public class CameraEaseTest extends BaseActivityTest {
@Ignore
public void testEaseToBounds() {
validateTestSetup();
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- final LatLng centerBounds = new LatLng(1, 1);
- LatLng cornerOne = new LatLng();
- LatLng cornerTwo = new LatLng(2, 2);
- final LatLngBounds.Builder builder = new LatLngBounds.Builder();
- builder.include(cornerOne);
- builder.include(cornerTwo);
- mapboxMap.easeCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), 0));
- uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Moved camera position latitude should match center bounds",
- cameraPosition.target.getLatitude(),
- centerBounds.getLatitude(),
- TestConstants.LAT_LNG_DELTA);
- assertEquals("Moved camera position longitude should match center bounds",
- cameraPosition.target.getLongitude(),
- centerBounds.getLongitude(),
- TestConstants.LAT_LNG_DELTA);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ final LatLng centerBounds = new LatLng(1, 1);
+ LatLng cornerOne = new LatLng();
+ LatLng cornerTwo = new LatLng(2, 2);
+ final LatLngBounds.Builder builder = new LatLngBounds.Builder();
+ builder.include(cornerOne);
+ builder.include(cornerTwo);
+ mapboxMap.easeCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), 0));
+ uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
+ CameraPosition cameraPosition = mapboxMap.getCameraPosition();
+ assertEquals("Moved camera position latitude should match center bounds",
+ cameraPosition.target.getLatitude(),
+ centerBounds.getLatitude(),
+ TestConstants.LAT_LNG_DELTA);
+ assertEquals("Moved camera position longitude should match center bounds",
+ cameraPosition.target.getLongitude(),
+ centerBounds.getLongitude(),
+ TestConstants.LAT_LNG_DELTA);
});
}
@@ -136,21 +121,18 @@ public class CameraEaseTest extends BaseActivityTest {
@Ignore
public void testEaseToMoveBy() {
validateTestSetup();
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- final PointF centerPoint = mapboxMap.getProjection().toScreenLocation(mapboxMap.getCameraPosition().target);
- final LatLng moveTarget = new LatLng(2, 2);
- final PointF moveTargetPoint = mapboxMap.getProjection().toScreenLocation(moveTarget);
- mapboxMap.easeCamera(CameraUpdateFactory.scrollBy(
- moveTargetPoint.x - centerPoint.x, moveTargetPoint.y - centerPoint.y));
- uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Moved camera position latitude should match", cameraPosition.target.getLatitude(),
- moveTarget.getLatitude(), TestConstants.LAT_LNG_DELTA_LARGE);
- assertEquals("Moved camera position longitude should match", cameraPosition.target.getLongitude(),
- moveTarget.getLongitude(), TestConstants.LAT_LNG_DELTA_LARGE);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ final PointF centerPoint = mapboxMap.getProjection().toScreenLocation(mapboxMap.getCameraPosition().target);
+ final LatLng moveTarget = new LatLng(2, 2);
+ final PointF moveTargetPoint = mapboxMap.getProjection().toScreenLocation(moveTarget);
+ mapboxMap.easeCamera(CameraUpdateFactory.scrollBy(
+ moveTargetPoint.x - centerPoint.x, moveTargetPoint.y - centerPoint.y));
+ uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
+ CameraPosition cameraPosition = mapboxMap.getCameraPosition();
+ assertEquals("Moved camera position latitude should match", cameraPosition.target.getLatitude(),
+ moveTarget.getLatitude(), TestConstants.LAT_LNG_DELTA_LARGE);
+ assertEquals("Moved camera position longitude should match", cameraPosition.target.getLongitude(),
+ moveTarget.getLongitude(), TestConstants.LAT_LNG_DELTA_LARGE);
});
}
@@ -158,16 +140,13 @@ public class CameraEaseTest extends BaseActivityTest {
@Ignore
public void testEaseToZoomIn() {
validateTestSetup();
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- float zoom = 1.0f;
- mapboxMap.easeCamera(CameraUpdateFactory.zoomIn());
- uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Moved camera zoom should match moved camera zoom", cameraPosition.zoom, zoom + 1,
- TestConstants.ZOOM_DELTA);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ float zoom = 1.0f;
+ mapboxMap.easeCamera(CameraUpdateFactory.zoomIn());
+ uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
+ CameraPosition cameraPosition = mapboxMap.getCameraPosition();
+ assertEquals("Moved camera zoom should match moved camera zoom", cameraPosition.zoom, zoom + 1,
+ TestConstants.ZOOM_DELTA);
});
}
@@ -175,18 +154,15 @@ public class CameraEaseTest extends BaseActivityTest {
@Ignore
public void testEaseToZoomOut() {
validateTestSetup();
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- float zoom = 10.0f;
- mapboxMap.easeCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(), zoom));
- uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
- mapboxMap.easeCamera(CameraUpdateFactory.zoomOut());
- uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Moved camera zoom should match moved camera zoom", cameraPosition.zoom, zoom - 1,
- TestConstants.ZOOM_DELTA);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ float zoom = 10.0f;
+ mapboxMap.easeCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(), zoom));
+ uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
+ mapboxMap.easeCamera(CameraUpdateFactory.zoomOut());
+ uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
+ CameraPosition cameraPosition = mapboxMap.getCameraPosition();
+ assertEquals("Moved camera zoom should match moved camera zoom", cameraPosition.zoom, zoom - 1,
+ TestConstants.ZOOM_DELTA);
});
}
@@ -194,17 +170,14 @@ public class CameraEaseTest extends BaseActivityTest {
@Ignore
public void testEaseToZoomBy() {
validateTestSetup();
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- float zoom = 1.0f;
- final float zoomBy = 2.45f;
- mapboxMap.easeCamera(CameraUpdateFactory.zoomBy(zoomBy));
- uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Moved camera zoom should match moved camera zoom", cameraPosition.zoom, zoom + zoomBy,
- TestConstants.ZOOM_DELTA);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ float zoom = 1.0f;
+ final float zoomBy = 2.45f;
+ mapboxMap.easeCamera(CameraUpdateFactory.zoomBy(zoomBy));
+ uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
+ CameraPosition cameraPosition = mapboxMap.getCameraPosition();
+ assertEquals("Moved camera zoom should match moved camera zoom", cameraPosition.zoom, zoom + zoomBy,
+ TestConstants.ZOOM_DELTA);
});
}
@@ -212,16 +185,13 @@ public class CameraEaseTest extends BaseActivityTest {
@Ignore
public void testEaseToZoomTo() {
validateTestSetup();
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- final float zoomTo = 2.45f;
- mapboxMap.easeCamera(CameraUpdateFactory.zoomTo(zoomTo));
- uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Moved camera zoom should match moved camera zoom", cameraPosition.zoom, zoomTo,
- TestConstants.ZOOM_DELTA);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ final float zoomTo = 2.45f;
+ mapboxMap.easeCamera(CameraUpdateFactory.zoomTo(zoomTo));
+ uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
+ CameraPosition cameraPosition = mapboxMap.getCameraPosition();
+ assertEquals("Moved camera zoom should match moved camera zoom", cameraPosition.zoom, zoomTo,
+ TestConstants.ZOOM_DELTA);
});
}
} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraMoveTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraMoveTest.java
index 54590981fa..37b9171a95 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraMoveTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraMoveTest.java
@@ -1,14 +1,12 @@
+
package com.mapbox.mapboxsdk.testapp.camera;
import android.graphics.PointF;
-import android.support.test.espresso.UiController;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-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.TestConstants;
@@ -30,23 +28,20 @@ public class CameraMoveTest extends BaseActivityTest {
@Ignore
public void testMoveToCameraPositionTarget() {
validateTestSetup();
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- float zoom = 1.0f;
- LatLng moveTarget = new LatLng(1, 1);
- CameraPosition initialPosition = new CameraPosition.Builder().target(
- new LatLng()).zoom(zoom).bearing(0).tilt(0).build();
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Default camera position should match default", cameraPosition, initialPosition);
- mapboxMap.moveCamera(CameraUpdateFactory.newLatLng(moveTarget));
- uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
- cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Moved camera position latitude should match", cameraPosition.target.getLatitude(),
- moveTarget.getLatitude(), TestConstants.LAT_LNG_DELTA);
- assertEquals("Moved camera position longitude should match", cameraPosition.target.getLongitude(),
- moveTarget.getLongitude(), TestConstants.LAT_LNG_DELTA);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ float zoom = 1.0f;
+ LatLng moveTarget = new LatLng(1, 1);
+ CameraPosition initialPosition = new CameraPosition.Builder().target(
+ new LatLng()).zoom(zoom).bearing(0).tilt(0).build();
+ CameraPosition cameraPosition = mapboxMap.getCameraPosition();
+ assertEquals("Default camera position should match default", cameraPosition, initialPosition);
+ mapboxMap.moveCamera(CameraUpdateFactory.newLatLng(moveTarget));
+ uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
+ cameraPosition = mapboxMap.getCameraPosition();
+ assertEquals("Moved camera position latitude should match", cameraPosition.target.getLatitude(),
+ moveTarget.getLatitude(), TestConstants.LAT_LNG_DELTA);
+ assertEquals("Moved camera position longitude should match", cameraPosition.target.getLongitude(),
+ moveTarget.getLongitude(), TestConstants.LAT_LNG_DELTA);
});
}
@@ -54,20 +49,17 @@ public class CameraMoveTest extends BaseActivityTest {
@Ignore
public void testMoveToCameraPositionTargetZoom() {
validateTestSetup();
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- final float moveZoom = 15.5f;
- final LatLng moveTarget = new LatLng(1.0000000001, 1.0000000003);
- mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(moveTarget, moveZoom));
- uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Moved camera position latitude should match", cameraPosition.target.getLatitude(),
- moveTarget.getLatitude(), TestConstants.LAT_LNG_DELTA);
- assertEquals("Moved camera position longitude should match", cameraPosition.target.getLongitude(),
- moveTarget.getLongitude(), TestConstants.LAT_LNG_DELTA);
- assertEquals("Moved zoom should match", cameraPosition.zoom, moveZoom, TestConstants.ZOOM_DELTA);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ final float moveZoom = 15.5f;
+ final LatLng moveTarget = new LatLng(1.0000000001, 1.0000000003);
+ mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(moveTarget, moveZoom));
+ uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
+ CameraPosition cameraPosition = mapboxMap.getCameraPosition();
+ assertEquals("Moved camera position latitude should match", cameraPosition.target.getLatitude(),
+ moveTarget.getLatitude(), TestConstants.LAT_LNG_DELTA);
+ assertEquals("Moved camera position longitude should match", cameraPosition.target.getLongitude(),
+ moveTarget.getLongitude(), TestConstants.LAT_LNG_DELTA);
+ assertEquals("Moved zoom should match", cameraPosition.zoom, moveZoom, TestConstants.ZOOM_DELTA);
});
}
@@ -75,32 +67,29 @@ public class CameraMoveTest extends BaseActivityTest {
@Ignore
public void testMoveToCameraPosition() {
validateTestSetup();
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- final LatLng moveTarget = new LatLng(1.0000000001, 1.0000000003);
- final float moveZoom = 15.5f;
- final float moveTilt = 45.5f;
- final float moveBearing = 12.5f;
-
- mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(
- new CameraPosition.Builder()
- .target(moveTarget)
- .zoom(moveZoom)
- .tilt(moveTilt)
- .bearing(moveBearing)
- .build())
- );
- uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Moved camera position latitude should match", cameraPosition.target.getLatitude(),
- moveTarget.getLatitude(), TestConstants.LAT_LNG_DELTA);
- assertEquals("Moved camera position longitude should match", cameraPosition.target.getLongitude(),
- moveTarget.getLongitude(), TestConstants.LAT_LNG_DELTA);
- assertEquals("Moved zoom should match", cameraPosition.zoom, moveZoom, TestConstants.ZOOM_DELTA);
- assertEquals("Moved zoom should match", cameraPosition.tilt, moveTilt, TestConstants.TILT_DELTA);
- assertEquals("Moved bearing should match", cameraPosition.bearing, moveBearing, TestConstants.BEARING_DELTA);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ final LatLng moveTarget = new LatLng(1.0000000001, 1.0000000003);
+ final float moveZoom = 15.5f;
+ final float moveTilt = 45.5f;
+ final float moveBearing = 12.5f;
+
+ mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(
+ new CameraPosition.Builder()
+ .target(moveTarget)
+ .zoom(moveZoom)
+ .tilt(moveTilt)
+ .bearing(moveBearing)
+ .build())
+ );
+ uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
+ CameraPosition cameraPosition = mapboxMap.getCameraPosition();
+ assertEquals("Moved camera position latitude should match", cameraPosition.target.getLatitude(),
+ moveTarget.getLatitude(), TestConstants.LAT_LNG_DELTA);
+ assertEquals("Moved camera position longitude should match", cameraPosition.target.getLongitude(),
+ moveTarget.getLongitude(), TestConstants.LAT_LNG_DELTA);
+ assertEquals("Moved zoom should match", cameraPosition.zoom, moveZoom, TestConstants.ZOOM_DELTA);
+ assertEquals("Moved zoom should match", cameraPosition.tilt, moveTilt, TestConstants.TILT_DELTA);
+ assertEquals("Moved bearing should match", cameraPosition.bearing, moveBearing, TestConstants.BEARING_DELTA);
});
}
@@ -108,27 +97,24 @@ public class CameraMoveTest extends BaseActivityTest {
@Ignore
public void testMoveToBounds() {
validateTestSetup();
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- final LatLng centerBounds = new LatLng(1, 1);
- LatLng cornerOne = new LatLng();
- LatLng cornerTwo = new LatLng(2, 2);
- final LatLngBounds.Builder builder = new LatLngBounds.Builder();
- builder.include(cornerOne);
- builder.include(cornerTwo);
- mapboxMap.moveCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), 0));
- uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Moved camera position latitude should match center bounds",
- cameraPosition.target.getLatitude(),
- centerBounds.getLatitude(),
- TestConstants.LAT_LNG_DELTA);
- assertEquals("Moved camera position longitude should match center bounds",
- cameraPosition.target.getLongitude(),
- centerBounds.getLongitude(),
- TestConstants.LAT_LNG_DELTA);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ final LatLng centerBounds = new LatLng(1, 1);
+ LatLng cornerOne = new LatLng();
+ LatLng cornerTwo = new LatLng(2, 2);
+ final LatLngBounds.Builder builder = new LatLngBounds.Builder();
+ builder.include(cornerOne);
+ builder.include(cornerTwo);
+ mapboxMap.moveCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), 0));
+ uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
+ CameraPosition cameraPosition = mapboxMap.getCameraPosition();
+ assertEquals("Moved camera position latitude should match center bounds",
+ cameraPosition.target.getLatitude(),
+ centerBounds.getLatitude(),
+ TestConstants.LAT_LNG_DELTA);
+ assertEquals("Moved camera position longitude should match center bounds",
+ cameraPosition.target.getLongitude(),
+ centerBounds.getLongitude(),
+ TestConstants.LAT_LNG_DELTA);
});
}
@@ -136,21 +122,18 @@ public class CameraMoveTest extends BaseActivityTest {
@Ignore
public void testMoveToMoveBy() {
validateTestSetup();
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- final PointF centerPoint = mapboxMap.getProjection().toScreenLocation(mapboxMap.getCameraPosition().target);
- final LatLng moveTarget = new LatLng(2, 2);
- final PointF moveTargetPoint = mapboxMap.getProjection().toScreenLocation(moveTarget);
- mapboxMap.moveCamera(CameraUpdateFactory.scrollBy(
- moveTargetPoint.x - centerPoint.x, moveTargetPoint.y - centerPoint.y));
- uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Moved camera position latitude should match", cameraPosition.target.getLatitude(),
- moveTarget.getLatitude(), TestConstants.LAT_LNG_DELTA_LARGE);
- assertEquals("Moved camera position longitude should match", cameraPosition.target.getLongitude(),
- moveTarget.getLongitude(), TestConstants.LAT_LNG_DELTA_LARGE);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ final PointF centerPoint = mapboxMap.getProjection().toScreenLocation(mapboxMap.getCameraPosition().target);
+ final LatLng moveTarget = new LatLng(2, 2);
+ final PointF moveTargetPoint = mapboxMap.getProjection().toScreenLocation(moveTarget);
+ mapboxMap.moveCamera(CameraUpdateFactory.scrollBy(
+ moveTargetPoint.x - centerPoint.x, moveTargetPoint.y - centerPoint.y));
+ uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
+ CameraPosition cameraPosition = mapboxMap.getCameraPosition();
+ assertEquals("Moved camera position latitude should match", cameraPosition.target.getLatitude(),
+ moveTarget.getLatitude(), TestConstants.LAT_LNG_DELTA_LARGE);
+ assertEquals("Moved camera position longitude should match", cameraPosition.target.getLongitude(),
+ moveTarget.getLongitude(), TestConstants.LAT_LNG_DELTA_LARGE);
});
}
@@ -158,16 +141,13 @@ public class CameraMoveTest extends BaseActivityTest {
@Ignore
public void testMoveToZoomIn() {
validateTestSetup();
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- float zoom = 1.0f;
- mapboxMap.moveCamera(CameraUpdateFactory.zoomIn());
- uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Moved camera zoom should match moved camera zoom", cameraPosition.zoom, zoom + 1,
- TestConstants.ZOOM_DELTA);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ float zoom = 1.0f;
+ mapboxMap.moveCamera(CameraUpdateFactory.zoomIn());
+ uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
+ CameraPosition cameraPosition = mapboxMap.getCameraPosition();
+ assertEquals("Moved camera zoom should match moved camera zoom", cameraPosition.zoom, zoom + 1,
+ TestConstants.ZOOM_DELTA);
});
}
@@ -175,18 +155,15 @@ public class CameraMoveTest extends BaseActivityTest {
@Ignore
public void testMoveToZoomOut() {
validateTestSetup();
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- float zoom = 10.0f;
- mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(), zoom));
- uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
- mapboxMap.moveCamera(CameraUpdateFactory.zoomOut());
- uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Moved camera zoom should match moved camera zoom", cameraPosition.zoom, zoom - 1,
- TestConstants.ZOOM_DELTA);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ float zoom = 10.0f;
+ mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(), zoom));
+ uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
+ mapboxMap.moveCamera(CameraUpdateFactory.zoomOut());
+ uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
+ CameraPosition cameraPosition = mapboxMap.getCameraPosition();
+ assertEquals("Moved camera zoom should match moved camera zoom", cameraPosition.zoom, zoom - 1,
+ TestConstants.ZOOM_DELTA);
});
}
@@ -194,17 +171,14 @@ public class CameraMoveTest extends BaseActivityTest {
@Ignore
public void testMoveToZoomBy() {
validateTestSetup();
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- float zoom = 1.0f;
- final float zoomBy = 2.45f;
- mapboxMap.moveCamera(CameraUpdateFactory.zoomBy(zoomBy));
- uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Moved camera zoom should match moved camera zoom", cameraPosition.zoom, zoom + zoomBy,
- TestConstants.ZOOM_DELTA);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ float zoom = 1.0f;
+ final float zoomBy = 2.45f;
+ mapboxMap.moveCamera(CameraUpdateFactory.zoomBy(zoomBy));
+ uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
+ CameraPosition cameraPosition = mapboxMap.getCameraPosition();
+ assertEquals("Moved camera zoom should match moved camera zoom", cameraPosition.zoom, zoom + zoomBy,
+ TestConstants.ZOOM_DELTA);
});
}
@@ -212,16 +186,13 @@ public class CameraMoveTest extends BaseActivityTest {
@Ignore
public void testMoveToZoomTo() {
validateTestSetup();
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- final float zoomTo = 2.45f;
- mapboxMap.moveCamera(CameraUpdateFactory.zoomTo(zoomTo));
- uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Moved camera zoom should match moved camera zoom", cameraPosition.zoom, zoomTo,
- TestConstants.ZOOM_DELTA);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ final float zoomTo = 2.45f;
+ mapboxMap.moveCamera(CameraUpdateFactory.zoomTo(zoomTo));
+ uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
+ CameraPosition cameraPosition = mapboxMap.getCameraPosition();
+ assertEquals("Moved camera zoom should match moved camera zoom", cameraPosition.zoom, zoomTo,
+ TestConstants.ZOOM_DELTA);
});
}
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/feature/QueryRenderedFeaturesPropertiesTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/feature/QueryRenderedFeaturesPropertiesTest.java
index 8e6987b712..6e446ceda9 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/feature/QueryRenderedFeaturesPropertiesTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/feature/QueryRenderedFeaturesPropertiesTest.java
@@ -2,11 +2,9 @@ package com.mapbox.mapboxsdk.testapp.feature;
import android.graphics.PointF;
import android.support.test.espresso.ViewAction;
-import android.support.test.espresso.action.CoordinatesProvider;
import android.support.test.espresso.action.GeneralClickAction;
import android.support.test.espresso.action.Press;
import android.support.test.espresso.action.Tap;
-import android.view.View;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.testapp.R;
@@ -44,15 +42,12 @@ public class QueryRenderedFeaturesPropertiesTest extends BaseActivityTest {
private static ViewAction clickXY(final float x, final float y) {
return new GeneralClickAction(
Tap.SINGLE,
- new CoordinatesProvider() {
- @Override
- public float[] calculateCoordinates(View view) {
- final int[] screenPos = new int[2];
- view.getLocationOnScreen(screenPos);
- final float screenX = screenPos[0] + x;
- final float screenY = screenPos[1] + y;
- return new float[] {screenX, screenY};
- }
+ view -> {
+ final int[] screenPos = new int[2];
+ view.getLocationOnScreen(screenPos);
+ final float screenX = screenPos[0] + x;
+ final float screenY = screenPos[1] + y;
+ return new float[] {screenX, screenY};
},
Press.FINGER);
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/geometry/LatLngBoundsTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/geometry/LatLngBoundsTest.java
index f3ce9994a3..738f1e203f 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/geometry/LatLngBoundsTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/geometry/LatLngBoundsTest.java
@@ -1,11 +1,8 @@
package com.mapbox.mapboxsdk.testapp.geometry;
-import android.support.test.espresso.UiController;
-
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.testapp.action.MapboxMapAction;
import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest;
import com.mapbox.mapboxsdk.testapp.activity.feature.QueryRenderedFeaturesBoxHighlightActivity;
@@ -26,15 +23,12 @@ public class LatLngBoundsTest extends BaseActivityTest {
public void testLatLngBounds() {
// regression test for #9322
validateTestSetup();
- MapboxMapAction.invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- LatLngBounds bounds = new LatLngBounds.Builder()
- .include(new LatLng(48.8589506, 2.2773457))
- .include(new LatLng(47.2383171, -1.6309316))
- .build();
- mapboxMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0));
- }
+ MapboxMapAction.invoke(mapboxMap, (uiController, mapboxMap) -> {
+ LatLngBounds bounds = new LatLngBounds.Builder()
+ .include(new LatLng(48.8589506, 2.2773457))
+ .include(new LatLng(47.2383171, -1.6309316))
+ .build();
+ mapboxMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0));
});
}
} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/AttributionTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/AttributionTest.java
index a20426c29c..09fbcb868c 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/AttributionTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/AttributionTest.java
@@ -150,15 +150,12 @@ public class AttributionTest extends BaseActivityTest {
}
private void buildUrlSpans() {
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- for (Source source : mapboxMap.getSources()) {
- String attributionSource = source.getAttribution();
- if (!TextUtils.isEmpty(attributionSource)) {
- SpannableStringBuilder htmlBuilder = (SpannableStringBuilder) Html.fromHtml(attributionSource);
- urlSpans = htmlBuilder.getSpans(0, htmlBuilder.length(), URLSpan.class);
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ for (Source source : mapboxMap.getSources()) {
+ String attributionSource = source.getAttribution();
+ if (!TextUtils.isEmpty(attributionSource)) {
+ SpannableStringBuilder htmlBuilder = (SpannableStringBuilder) Html.fromHtml(attributionSource);
+ urlSpans = htmlBuilder.getSpans(0, htmlBuilder.length(), URLSpan.class);
}
}
}));
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/CompassViewTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/CompassViewTest.java
index 28389a3386..2a510b4dc5 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/CompassViewTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/CompassViewTest.java
@@ -1,13 +1,9 @@
package com.mapbox.mapboxsdk.testapp.maps.widgets;
-import android.support.test.espresso.UiController;
-
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.geometry.LatLng;
-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.TestConstants;
@@ -41,18 +37,15 @@ public class CompassViewTest extends BaseActivityTest {
@Ignore
public void testVisible() {
validateTestSetup();
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(
- new CameraPosition.Builder()
- .bearing(45)
- .zoom(1)
- .target(new LatLng())
- .build()
- ));
- uiController.loopMainThreadForAtLeast(500);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(
+ new CameraPosition.Builder()
+ .bearing(45)
+ .zoom(1)
+ .target(new LatLng())
+ .build()
+ ));
+ uiController.loopMainThreadForAtLeast(500);
});
onView(withId(R.id.compassView)).check(matches(isDisplayed()));
}
@@ -61,27 +54,19 @@ public class CompassViewTest extends BaseActivityTest {
@Ignore
public void testClick() {
validateTestSetup();
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(
- new CameraPosition.Builder()
- .bearing(45)
- .zoom(1)
- .target(new LatLng())
- .build()
- ));
- }
- });
+ invoke(mapboxMap, (uiController, mapboxMap) -> mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(
+ new CameraPosition.Builder()
+ .bearing(45)
+ .zoom(1)
+ .target(new LatLng())
+ .build()
+ )));
onView(withId(R.id.compassView)).perform(click());
waitLoop();
onView(withId(R.id.compassView)).check(matches(not(isDisplayed())));
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Camera bearing should face north, ", 0, cameraPosition.bearing, TestConstants.BEARING_DELTA);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ CameraPosition cameraPosition = mapboxMap.getCameraPosition();
+ assertEquals("Camera bearing should face north, ", 0, cameraPosition.bearing, TestConstants.BEARING_DELTA);
});
}
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/offline/OfflineUtilsTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/offline/OfflineUtilsTest.java
new file mode 100644
index 0000000000..84f84fdb90
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/offline/OfflineUtilsTest.java
@@ -0,0 +1,44 @@
+package com.mapbox.mapboxsdk.testapp.offline;
+
+import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest;
+import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity;
+import com.mapbox.mapboxsdk.testapp.utils.OfflineUtils;
+
+import org.junit.Test;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
+
+import static com.mapbox.mapboxsdk.testapp.activity.offline.OfflineActivity.JSON_CHARSET;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.TestCase.assertTrue;
+
+public class OfflineUtilsTest extends BaseActivityTest {
+
+ private static final String REGION_NAME = "hello world";
+ private static final String CONVERTED_REGION_NAME = "{\"FIELD_REGION_NAME\":\"hello world\"}";
+
+ @Override
+ protected Class getActivityClass() {
+ return EspressoTestActivity.class;
+ }
+
+ @Test
+ public void testOfflineUtilsConvertToBytes() throws UnsupportedEncodingException {
+ byte[] expected = CONVERTED_REGION_NAME.getBytes(JSON_CHARSET);
+ byte[] actual = OfflineUtils.convertRegionName(REGION_NAME);
+ assertTrue("Bytes arrays should match", Arrays.equals(expected, actual));
+ }
+
+ @Test
+ public void testOfflineUtilsConvertToString() throws UnsupportedEncodingException {
+ String actual = OfflineUtils.convertRegionName(CONVERTED_REGION_NAME.getBytes(JSON_CHARSET));
+ assertEquals("Strings should match", REGION_NAME, actual);
+ }
+
+ @Test
+ public void testOfflineUtilsConvertNoOp() {
+ String convertNoOp = OfflineUtils.convertRegionName(OfflineUtils.convertRegionName(REGION_NAME));
+ assertEquals("Strings should match", REGION_NAME, convertNoOp);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/GeoJsonSourceTests.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/GeoJsonSourceTests.java
index 5d10cfa38a..2156c96973 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/GeoJsonSourceTests.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/GeoJsonSourceTests.java
@@ -13,9 +13,9 @@ import com.mapbox.mapboxsdk.testapp.R;
import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest;
import com.mapbox.mapboxsdk.testapp.activity.style.RuntimeStyleTestActivity;
import com.mapbox.mapboxsdk.testapp.utils.ResourceUtils;
-import com.mapbox.services.commons.geojson.Feature;
-import com.mapbox.services.commons.geojson.FeatureCollection;
-import com.mapbox.services.commons.geojson.Point;
+import com.mapbox.geojson.Feature;
+import com.mapbox.geojson.FeatureCollection;
+import com.mapbox.geojson.Point;
import org.hamcrest.Matcher;
import org.junit.Test;
@@ -67,7 +67,7 @@ public class GeoJsonSourceTests extends BaseActivityTest {
@Override
public void perform(UiController uiController, View view) {
- GeoJsonSource source = new GeoJsonSource("source", Point.fromCoordinates(new double[] {0d, 0d}));
+ GeoJsonSource source = new GeoJsonSource("source", Point.fromLngLat(0d, 0d));
mapboxMap.addSource(source);
mapboxMap.addLayer(new CircleLayer("layer", source.getId()));
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/HeatmapLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/HeatmapLayerTest.java
new file mode 100644
index 0000000000..364c8d2679
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/HeatmapLayerTest.java
@@ -0,0 +1,626 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`.
+
+package com.mapbox.mapboxsdk.testapp.style;
+
+import android.graphics.Color;
+import android.support.test.espresso.UiController;
+import android.support.test.runner.AndroidJUnit4;
+
+import timber.log.Timber;
+
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.style.functions.CompositeFunction;
+import com.mapbox.mapboxsdk.style.functions.CameraFunction;
+import com.mapbox.mapboxsdk.style.functions.SourceFunction;
+import com.mapbox.mapboxsdk.style.functions.stops.CategoricalStops;
+import com.mapbox.mapboxsdk.style.functions.stops.ExponentialStops;
+import com.mapbox.mapboxsdk.style.functions.stops.IdentityStops;
+import com.mapbox.mapboxsdk.style.functions.stops.IntervalStops;
+import com.mapbox.mapboxsdk.style.functions.stops.Stop;
+import com.mapbox.mapboxsdk.style.functions.stops.Stops;
+import com.mapbox.mapboxsdk.style.layers.HeatmapLayer;
+import com.mapbox.mapboxsdk.testapp.action.MapboxMapAction;
+import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static com.mapbox.mapboxsdk.style.functions.Function.*;
+import static com.mapbox.mapboxsdk.style.functions.stops.Stop.stop;
+import static com.mapbox.mapboxsdk.style.functions.stops.Stops.*;
+import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke;
+import static org.junit.Assert.*;
+import static com.mapbox.mapboxsdk.style.layers.Property.*;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.*;
+
+import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
+import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity;
+
+/**
+ * Basic smoke tests for HeatmapLayer
+ */
+@RunWith(AndroidJUnit4.class)
+public class HeatmapLayerTest extends BaseActivityTest {
+
+ private HeatmapLayer layer;
+
+ @Override
+ protected Class getActivityClass() {
+ return EspressoTestActivity.class;
+ }
+
+ private void setupLayer() {
+ Timber.i("Retrieving layer");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Timber.i("Adding layer");
+ layer = new HeatmapLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ // Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ }
+ });
+ }
+
+ @Test
+ public void testSetVisibility() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("Visibility");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Get initial
+ assertEquals(layer.getVisibility().getValue(), VISIBLE);
+
+ // Set
+ layer.setProperties(visibility(NONE));
+ assertEquals(layer.getVisibility().getValue(), NONE);
+ }
+ });
+ }
+
+ @Test
+ public void testSourceLayer() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("SourceLayer");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Get initial
+ assertEquals(layer.getSourceLayer(), "composite");
+
+ // Set
+ final String sourceLayer = "test";
+ layer.setSourceLayer(sourceLayer);
+ assertEquals(layer.getSourceLayer(), sourceLayer);
+ }
+ });
+ }
+
+ @Test
+ public void testHeatmapRadiusTransition() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("heatmap-radiusTransitionOptions");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setHeatmapRadiusTransition(options);
+ assertEquals(layer.getHeatmapRadiusTransition(), options);
+ }
+ });
+ }
+
+ @Test
+ public void testHeatmapRadiusAsConstant() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("heatmap-radius");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(heatmapRadius(0.3f));
+ assertEquals((Float) layer.getHeatmapRadius().getValue(), (Float) 0.3f);
+ }
+ });
+ }
+
+ @Test
+ public void testHeatmapRadiusAsCameraFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("heatmap-radius");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ heatmapRadius(
+ zoom(
+ exponential(
+ stop(2, heatmapRadius(0.3f))
+ ).withBase(0.5f)
+ )
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getHeatmapRadius());
+ assertNotNull(layer.getHeatmapRadius().getFunction());
+ assertEquals(CameraFunction.class, layer.getHeatmapRadius().getFunction().getClass());
+ assertEquals(ExponentialStops.class, layer.getHeatmapRadius().getFunction().getStops().getClass());
+ assertEquals(0.5f, ((ExponentialStops) layer.getHeatmapRadius().getFunction().getStops()).getBase(), 0.001);
+ assertEquals(1, ((ExponentialStops) layer.getHeatmapRadius().getFunction().getStops()).size());
+ }
+ });
+ }
+
+ @Test
+ public void testHeatmapRadiusAsIdentitySourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("heatmap-radius");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ heatmapRadius(property("FeaturePropertyA", Stops.<Float>identity()))
+ );
+
+ // Verify
+ assertNotNull(layer.getHeatmapRadius());
+ assertNotNull(layer.getHeatmapRadius().getFunction());
+ assertEquals(SourceFunction.class, layer.getHeatmapRadius().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getHeatmapRadius().getFunction()).getProperty());
+ assertEquals(IdentityStops.class, layer.getHeatmapRadius().getFunction().getStops().getClass());
+ }
+ });
+ }
+
+ @Test
+ public void testHeatmapRadiusAsExponentialSourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("heatmap-radius");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ heatmapRadius(
+ property(
+ "FeaturePropertyA",
+ exponential(
+ stop(0.3f, heatmapRadius(0.3f))
+ ).withBase(0.5f)
+ )
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getHeatmapRadius());
+ assertNotNull(layer.getHeatmapRadius().getFunction());
+ assertEquals(SourceFunction.class, layer.getHeatmapRadius().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getHeatmapRadius().getFunction()).getProperty());
+ assertEquals(ExponentialStops.class, layer.getHeatmapRadius().getFunction().getStops().getClass());
+ }
+ });
+ }
+
+ @Test
+ public void testHeatmapRadiusAsCategoricalSourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("heatmap-radius");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ heatmapRadius(
+ property(
+ "FeaturePropertyA",
+ categorical(
+ stop(1.0f, heatmapRadius(0.3f))
+ )
+ ).withDefaultValue(heatmapRadius(0.3f))
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getHeatmapRadius());
+ assertNotNull(layer.getHeatmapRadius().getFunction());
+ assertEquals(SourceFunction.class, layer.getHeatmapRadius().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getHeatmapRadius().getFunction()).getProperty());
+ assertEquals(CategoricalStops.class, layer.getHeatmapRadius().getFunction().getStops().getClass());
+ assertNotNull(((SourceFunction) layer.getHeatmapRadius().getFunction()).getDefaultValue());
+ assertNotNull(((SourceFunction) layer.getHeatmapRadius().getFunction()).getDefaultValue().getValue());
+ assertEquals(0.3f, ((SourceFunction) layer.getHeatmapRadius().getFunction()).getDefaultValue().getValue());
+ }
+ });
+
+ }
+
+ @Test
+ public void testHeatmapRadiusAsCompositeFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("heatmap-radius");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ heatmapRadius(
+ composite(
+ "FeaturePropertyA",
+ exponential(
+ stop(0, 0.3f, heatmapRadius(0.9f))
+ ).withBase(0.5f)
+ ).withDefaultValue(heatmapRadius(0.3f))
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getHeatmapRadius());
+ assertNotNull(layer.getHeatmapRadius().getFunction());
+ assertEquals(CompositeFunction.class, layer.getHeatmapRadius().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getHeatmapRadius().getFunction()).getProperty());
+ assertEquals(ExponentialStops.class, layer.getHeatmapRadius().getFunction().getStops().getClass());
+ assertEquals(1, ((ExponentialStops) layer.getHeatmapRadius().getFunction().getStops()).size());
+
+ ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops =
+ (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getHeatmapRadius().getFunction().getStops();
+ Stop<Stop.CompositeValue<Float, Float>, Float> stop = stops.iterator().next();
+ assertEquals(0f, stop.in.zoom, 0.001);
+ assertEquals(0.3f, stop.in.value, 0.001f);
+ assertEquals(0.9f, stop.out, 0.001f);
+ }
+ });
+ }
+
+ @Test
+ public void testHeatmapWeightAsConstant() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("heatmap-weight");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(heatmapWeight(0.3f));
+ assertEquals((Float) layer.getHeatmapWeight().getValue(), (Float) 0.3f);
+ }
+ });
+ }
+
+ @Test
+ public void testHeatmapWeightAsCameraFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("heatmap-weight");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ heatmapWeight(
+ zoom(
+ exponential(
+ stop(2, heatmapWeight(0.3f))
+ ).withBase(0.5f)
+ )
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getHeatmapWeight());
+ assertNotNull(layer.getHeatmapWeight().getFunction());
+ assertEquals(CameraFunction.class, layer.getHeatmapWeight().getFunction().getClass());
+ assertEquals(ExponentialStops.class, layer.getHeatmapWeight().getFunction().getStops().getClass());
+ assertEquals(0.5f, ((ExponentialStops) layer.getHeatmapWeight().getFunction().getStops()).getBase(), 0.001);
+ assertEquals(1, ((ExponentialStops) layer.getHeatmapWeight().getFunction().getStops()).size());
+ }
+ });
+ }
+
+ @Test
+ public void testHeatmapWeightAsIdentitySourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("heatmap-weight");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ heatmapWeight(property("FeaturePropertyA", Stops.<Float>identity()))
+ );
+
+ // Verify
+ assertNotNull(layer.getHeatmapWeight());
+ assertNotNull(layer.getHeatmapWeight().getFunction());
+ assertEquals(SourceFunction.class, layer.getHeatmapWeight().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getHeatmapWeight().getFunction()).getProperty());
+ assertEquals(IdentityStops.class, layer.getHeatmapWeight().getFunction().getStops().getClass());
+ }
+ });
+ }
+
+ @Test
+ public void testHeatmapWeightAsExponentialSourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("heatmap-weight");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ heatmapWeight(
+ property(
+ "FeaturePropertyA",
+ exponential(
+ stop(0.3f, heatmapWeight(0.3f))
+ ).withBase(0.5f)
+ )
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getHeatmapWeight());
+ assertNotNull(layer.getHeatmapWeight().getFunction());
+ assertEquals(SourceFunction.class, layer.getHeatmapWeight().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getHeatmapWeight().getFunction()).getProperty());
+ assertEquals(ExponentialStops.class, layer.getHeatmapWeight().getFunction().getStops().getClass());
+ }
+ });
+ }
+
+ @Test
+ public void testHeatmapWeightAsCategoricalSourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("heatmap-weight");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ heatmapWeight(
+ property(
+ "FeaturePropertyA",
+ categorical(
+ stop(1.0f, heatmapWeight(0.3f))
+ )
+ ).withDefaultValue(heatmapWeight(0.3f))
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getHeatmapWeight());
+ assertNotNull(layer.getHeatmapWeight().getFunction());
+ assertEquals(SourceFunction.class, layer.getHeatmapWeight().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getHeatmapWeight().getFunction()).getProperty());
+ assertEquals(CategoricalStops.class, layer.getHeatmapWeight().getFunction().getStops().getClass());
+ assertNotNull(((SourceFunction) layer.getHeatmapWeight().getFunction()).getDefaultValue());
+ assertNotNull(((SourceFunction) layer.getHeatmapWeight().getFunction()).getDefaultValue().getValue());
+ assertEquals(0.3f, ((SourceFunction) layer.getHeatmapWeight().getFunction()).getDefaultValue().getValue());
+ }
+ });
+
+ }
+
+ @Test
+ public void testHeatmapWeightAsCompositeFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("heatmap-weight");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ heatmapWeight(
+ composite(
+ "FeaturePropertyA",
+ exponential(
+ stop(0, 0.3f, heatmapWeight(0.9f))
+ ).withBase(0.5f)
+ ).withDefaultValue(heatmapWeight(0.3f))
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getHeatmapWeight());
+ assertNotNull(layer.getHeatmapWeight().getFunction());
+ assertEquals(CompositeFunction.class, layer.getHeatmapWeight().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getHeatmapWeight().getFunction()).getProperty());
+ assertEquals(ExponentialStops.class, layer.getHeatmapWeight().getFunction().getStops().getClass());
+ assertEquals(1, ((ExponentialStops) layer.getHeatmapWeight().getFunction().getStops()).size());
+
+ ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops =
+ (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getHeatmapWeight().getFunction().getStops();
+ Stop<Stop.CompositeValue<Float, Float>, Float> stop = stops.iterator().next();
+ assertEquals(0f, stop.in.zoom, 0.001);
+ assertEquals(0.3f, stop.in.value, 0.001f);
+ assertEquals(0.9f, stop.out, 0.001f);
+ }
+ });
+ }
+
+ @Test
+ public void testHeatmapIntensityTransition() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("heatmap-intensityTransitionOptions");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setHeatmapIntensityTransition(options);
+ assertEquals(layer.getHeatmapIntensityTransition(), options);
+ }
+ });
+ }
+
+ @Test
+ public void testHeatmapIntensityAsConstant() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("heatmap-intensity");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(heatmapIntensity(0.3f));
+ assertEquals((Float) layer.getHeatmapIntensity().getValue(), (Float) 0.3f);
+ }
+ });
+ }
+
+ @Test
+ public void testHeatmapIntensityAsCameraFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("heatmap-intensity");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ heatmapIntensity(
+ zoom(
+ exponential(
+ stop(2, heatmapIntensity(0.3f))
+ ).withBase(0.5f)
+ )
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getHeatmapIntensity());
+ assertNotNull(layer.getHeatmapIntensity().getFunction());
+ assertEquals(CameraFunction.class, layer.getHeatmapIntensity().getFunction().getClass());
+ assertEquals(ExponentialStops.class, layer.getHeatmapIntensity().getFunction().getStops().getClass());
+ assertEquals(0.5f, ((ExponentialStops) layer.getHeatmapIntensity().getFunction().getStops()).getBase(), 0.001);
+ assertEquals(1, ((ExponentialStops) layer.getHeatmapIntensity().getFunction().getStops()).size());
+ }
+ });
+ }
+
+ @Test
+ public void testHeatmapOpacityTransition() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("heatmap-opacityTransitionOptions");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setHeatmapOpacityTransition(options);
+ assertEquals(layer.getHeatmapOpacityTransition(), options);
+ }
+ });
+ }
+
+ @Test
+ public void testHeatmapOpacityAsConstant() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("heatmap-opacity");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(heatmapOpacity(0.3f));
+ assertEquals((Float) layer.getHeatmapOpacity().getValue(), (Float) 0.3f);
+ }
+ });
+ }
+
+ @Test
+ public void testHeatmapOpacityAsCameraFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("heatmap-opacity");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ heatmapOpacity(
+ zoom(
+ exponential(
+ stop(2, heatmapOpacity(0.3f))
+ ).withBase(0.5f)
+ )
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getHeatmapOpacity());
+ assertNotNull(layer.getHeatmapOpacity().getFunction());
+ assertEquals(CameraFunction.class, layer.getHeatmapOpacity().getFunction().getClass());
+ assertEquals(ExponentialStops.class, layer.getHeatmapOpacity().getFunction().getStops().getClass());
+ assertEquals(0.5f, ((ExponentialStops) layer.getHeatmapOpacity().getFunction().getStops()).getBase(), 0.001);
+ assertEquals(1, ((ExponentialStops) layer.getHeatmapOpacity().getFunction().getStops()).size());
+ }
+ });
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/HillshadeLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/HillshadeLayerTest.java
new file mode 100644
index 0000000000..1cb7b575be
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/HillshadeLayerTest.java
@@ -0,0 +1,505 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`.
+
+package com.mapbox.mapboxsdk.testapp.style;
+
+import android.graphics.Color;
+import android.support.test.espresso.UiController;
+import android.support.test.runner.AndroidJUnit4;
+
+import timber.log.Timber;
+
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.style.functions.CompositeFunction;
+import com.mapbox.mapboxsdk.style.functions.CameraFunction;
+import com.mapbox.mapboxsdk.style.functions.SourceFunction;
+import com.mapbox.mapboxsdk.style.functions.stops.CategoricalStops;
+import com.mapbox.mapboxsdk.style.functions.stops.ExponentialStops;
+import com.mapbox.mapboxsdk.style.functions.stops.IdentityStops;
+import com.mapbox.mapboxsdk.style.functions.stops.IntervalStops;
+import com.mapbox.mapboxsdk.style.functions.stops.Stop;
+import com.mapbox.mapboxsdk.style.functions.stops.Stops;
+import com.mapbox.mapboxsdk.style.layers.HillshadeLayer;
+import com.mapbox.mapboxsdk.testapp.action.MapboxMapAction;
+import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static com.mapbox.mapboxsdk.style.functions.Function.*;
+import static com.mapbox.mapboxsdk.style.functions.stops.Stop.stop;
+import static com.mapbox.mapboxsdk.style.functions.stops.Stops.*;
+import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke;
+import static org.junit.Assert.*;
+import static com.mapbox.mapboxsdk.style.layers.Property.*;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.*;
+
+import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
+import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity;
+
+/**
+ * Basic smoke tests for HillshadeLayer
+ */
+@RunWith(AndroidJUnit4.class)
+public class HillshadeLayerTest extends BaseActivityTest {
+
+ private HillshadeLayer layer;
+
+ @Override
+ protected Class getActivityClass() {
+ return EspressoTestActivity.class;
+ }
+
+ private void setupLayer() {
+ Timber.i("Retrieving layer");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Timber.i("Adding layer");
+ layer = new HillshadeLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ // Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ }
+ });
+ }
+
+ @Test
+ public void testSetVisibility() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("Visibility");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Get initial
+ assertEquals(layer.getVisibility().getValue(), VISIBLE);
+
+ // Set
+ layer.setProperties(visibility(NONE));
+ assertEquals(layer.getVisibility().getValue(), NONE);
+ }
+ });
+ }
+
+ @Test
+ public void testHillshadeIlluminationDirectionAsConstant() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("hillshade-illumination-direction");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(hillshadeIlluminationDirection(0.3f));
+ assertEquals((Float) layer.getHillshadeIlluminationDirection().getValue(), (Float) 0.3f);
+ }
+ });
+ }
+
+ @Test
+ public void testHillshadeIlluminationDirectionAsCameraFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("hillshade-illumination-direction");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ hillshadeIlluminationDirection(
+ zoom(
+ exponential(
+ stop(2, hillshadeIlluminationDirection(0.3f))
+ ).withBase(0.5f)
+ )
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getHillshadeIlluminationDirection());
+ assertNotNull(layer.getHillshadeIlluminationDirection().getFunction());
+ assertEquals(CameraFunction.class, layer.getHillshadeIlluminationDirection().getFunction().getClass());
+ assertEquals(ExponentialStops.class, layer.getHillshadeIlluminationDirection().getFunction().getStops().getClass());
+ assertEquals(0.5f, ((ExponentialStops) layer.getHillshadeIlluminationDirection().getFunction().getStops()).getBase(), 0.001);
+ assertEquals(1, ((ExponentialStops) layer.getHillshadeIlluminationDirection().getFunction().getStops()).size());
+ }
+ });
+ }
+
+ @Test
+ public void testHillshadeIlluminationAnchorAsConstant() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("hillshade-illumination-anchor");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(hillshadeIlluminationAnchor(HILLSHADE_ILLUMINATION_ANCHOR_MAP));
+ assertEquals((String) layer.getHillshadeIlluminationAnchor().getValue(), (String) HILLSHADE_ILLUMINATION_ANCHOR_MAP);
+ }
+ });
+ }
+
+ @Test
+ public void testHillshadeIlluminationAnchorAsCameraFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("hillshade-illumination-anchor");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ hillshadeIlluminationAnchor(
+ zoom(
+ interval(
+ stop(2, hillshadeIlluminationAnchor(HILLSHADE_ILLUMINATION_ANCHOR_MAP))
+ )
+ )
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getHillshadeIlluminationAnchor());
+ assertNotNull(layer.getHillshadeIlluminationAnchor().getFunction());
+ assertEquals(CameraFunction.class, layer.getHillshadeIlluminationAnchor().getFunction().getClass());
+ assertEquals(IntervalStops.class, layer.getHillshadeIlluminationAnchor().getFunction().getStops().getClass());
+ assertEquals(1, ((IntervalStops) layer.getHillshadeIlluminationAnchor().getFunction().getStops()).size());
+ }
+ });
+ }
+
+ @Test
+ public void testHillshadeExaggerationTransition() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("hillshade-exaggerationTransitionOptions");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setHillshadeExaggerationTransition(options);
+ assertEquals(layer.getHillshadeExaggerationTransition(), options);
+ }
+ });
+ }
+
+ @Test
+ public void testHillshadeExaggerationAsConstant() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("hillshade-exaggeration");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(hillshadeExaggeration(0.3f));
+ assertEquals((Float) layer.getHillshadeExaggeration().getValue(), (Float) 0.3f);
+ }
+ });
+ }
+
+ @Test
+ public void testHillshadeExaggerationAsCameraFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("hillshade-exaggeration");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ hillshadeExaggeration(
+ zoom(
+ exponential(
+ stop(2, hillshadeExaggeration(0.3f))
+ ).withBase(0.5f)
+ )
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getHillshadeExaggeration());
+ assertNotNull(layer.getHillshadeExaggeration().getFunction());
+ assertEquals(CameraFunction.class, layer.getHillshadeExaggeration().getFunction().getClass());
+ assertEquals(ExponentialStops.class, layer.getHillshadeExaggeration().getFunction().getStops().getClass());
+ assertEquals(0.5f, ((ExponentialStops) layer.getHillshadeExaggeration().getFunction().getStops()).getBase(), 0.001);
+ assertEquals(1, ((ExponentialStops) layer.getHillshadeExaggeration().getFunction().getStops()).size());
+ }
+ });
+ }
+
+ @Test
+ public void testHillshadeShadowColorTransition() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("hillshade-shadow-colorTransitionOptions");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setHillshadeShadowColorTransition(options);
+ assertEquals(layer.getHillshadeShadowColorTransition(), options);
+ }
+ });
+ }
+
+ @Test
+ public void testHillshadeShadowColorAsConstant() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("hillshade-shadow-color");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(hillshadeShadowColor("rgba(0, 0, 0, 1)"));
+ assertEquals((String) layer.getHillshadeShadowColor().getValue(), (String) "rgba(0, 0, 0, 1)");
+ }
+ });
+ }
+
+ @Test
+ public void testHillshadeShadowColorAsCameraFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("hillshade-shadow-color");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ hillshadeShadowColor(
+ zoom(
+ exponential(
+ stop(2, hillshadeShadowColor("rgba(0, 0, 0, 1)"))
+ ).withBase(0.5f)
+ )
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getHillshadeShadowColor());
+ assertNotNull(layer.getHillshadeShadowColor().getFunction());
+ assertEquals(CameraFunction.class, layer.getHillshadeShadowColor().getFunction().getClass());
+ assertEquals(ExponentialStops.class, layer.getHillshadeShadowColor().getFunction().getStops().getClass());
+ assertEquals(0.5f, ((ExponentialStops) layer.getHillshadeShadowColor().getFunction().getStops()).getBase(), 0.001);
+ assertEquals(1, ((ExponentialStops) layer.getHillshadeShadowColor().getFunction().getStops()).size());
+ }
+ });
+ }
+
+ @Test
+ public void testHillshadeShadowColorAsIntConstant() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("hillshade-shadow-color");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(hillshadeShadowColor(Color.RED));
+ assertEquals(layer.getHillshadeShadowColorAsInt(), Color.RED);
+ }
+ });
+ }
+
+ @Test
+ public void testHillshadeHighlightColorTransition() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("hillshade-highlight-colorTransitionOptions");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setHillshadeHighlightColorTransition(options);
+ assertEquals(layer.getHillshadeHighlightColorTransition(), options);
+ }
+ });
+ }
+
+ @Test
+ public void testHillshadeHighlightColorAsConstant() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("hillshade-highlight-color");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(hillshadeHighlightColor("rgba(0, 0, 0, 1)"));
+ assertEquals((String) layer.getHillshadeHighlightColor().getValue(), (String) "rgba(0, 0, 0, 1)");
+ }
+ });
+ }
+
+ @Test
+ public void testHillshadeHighlightColorAsCameraFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("hillshade-highlight-color");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ hillshadeHighlightColor(
+ zoom(
+ exponential(
+ stop(2, hillshadeHighlightColor("rgba(0, 0, 0, 1)"))
+ ).withBase(0.5f)
+ )
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getHillshadeHighlightColor());
+ assertNotNull(layer.getHillshadeHighlightColor().getFunction());
+ assertEquals(CameraFunction.class, layer.getHillshadeHighlightColor().getFunction().getClass());
+ assertEquals(ExponentialStops.class, layer.getHillshadeHighlightColor().getFunction().getStops().getClass());
+ assertEquals(0.5f, ((ExponentialStops) layer.getHillshadeHighlightColor().getFunction().getStops()).getBase(), 0.001);
+ assertEquals(1, ((ExponentialStops) layer.getHillshadeHighlightColor().getFunction().getStops()).size());
+ }
+ });
+ }
+
+ @Test
+ public void testHillshadeHighlightColorAsIntConstant() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("hillshade-highlight-color");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(hillshadeHighlightColor(Color.RED));
+ assertEquals(layer.getHillshadeHighlightColorAsInt(), Color.RED);
+ }
+ });
+ }
+
+ @Test
+ public void testHillshadeAccentColorTransition() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("hillshade-accent-colorTransitionOptions");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setHillshadeAccentColorTransition(options);
+ assertEquals(layer.getHillshadeAccentColorTransition(), options);
+ }
+ });
+ }
+
+ @Test
+ public void testHillshadeAccentColorAsConstant() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("hillshade-accent-color");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(hillshadeAccentColor("rgba(0, 0, 0, 1)"));
+ assertEquals((String) layer.getHillshadeAccentColor().getValue(), (String) "rgba(0, 0, 0, 1)");
+ }
+ });
+ }
+
+ @Test
+ public void testHillshadeAccentColorAsCameraFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("hillshade-accent-color");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ hillshadeAccentColor(
+ zoom(
+ exponential(
+ stop(2, hillshadeAccentColor("rgba(0, 0, 0, 1)"))
+ ).withBase(0.5f)
+ )
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getHillshadeAccentColor());
+ assertNotNull(layer.getHillshadeAccentColor().getFunction());
+ assertEquals(CameraFunction.class, layer.getHillshadeAccentColor().getFunction().getClass());
+ assertEquals(ExponentialStops.class, layer.getHillshadeAccentColor().getFunction().getStops().getClass());
+ assertEquals(0.5f, ((ExponentialStops) layer.getHillshadeAccentColor().getFunction().getStops()).getBase(), 0.001);
+ assertEquals(1, ((ExponentialStops) layer.getHillshadeAccentColor().getFunction().getStops()).size());
+ }
+ });
+ }
+
+ @Test
+ public void testHillshadeAccentColorAsIntConstant() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("hillshade-accent-color");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(hillshadeAccentColor(Color.RED));
+ assertEquals(layer.getHillshadeAccentColorAsInt(), Color.RED);
+ }
+ });
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/ImageTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/ImageTest.java
index 60cf4ced3d..c049fabb52 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/ImageTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/ImageTest.java
@@ -3,10 +3,8 @@ package com.mapbox.mapboxsdk.testapp.style;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
-import android.support.test.espresso.UiController;
import android.support.test.runner.AndroidJUnit4;
-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;
@@ -34,21 +32,18 @@ public class ImageTest extends BaseActivityTest {
@Test
public void testAddGetImage() {
validateTestSetup();
- MapboxMapAction.invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- Drawable drawable = rule.getActivity().getResources().getDrawable(R.drawable.ic_launcher_round);
- assertTrue(drawable instanceof BitmapDrawable);
+ MapboxMapAction.invoke(mapboxMap, (uiController, mapboxMap) -> {
+ Drawable drawable = rule.getActivity().getResources().getDrawable(R.drawable.ic_launcher_round);
+ assertTrue(drawable instanceof BitmapDrawable);
- Bitmap bitmapSet = ((BitmapDrawable) drawable).getBitmap();
- mapboxMap.addImage(IMAGE_ID, bitmapSet);
+ Bitmap bitmapSet = ((BitmapDrawable) drawable).getBitmap();
+ mapboxMap.addImage(IMAGE_ID, bitmapSet);
- Bitmap bitmapGet = mapboxMap.getImage(IMAGE_ID);
- assertTrue(bitmapGet.sameAs(bitmapSet));
+ Bitmap bitmapGet = mapboxMap.getImage(IMAGE_ID);
+ assertTrue(bitmapGet.sameAs(bitmapSet));
- mapboxMap.removeImage(IMAGE_ID);
- assertNull(mapboxMap.getImage(IMAGE_ID));
- }
+ mapboxMap.removeImage(IMAGE_ID);
+ assertNull(mapboxMap.getImage(IMAGE_ID));
});
}
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RasterLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RasterLayerTest.java
index 020effe331..df560e2632 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RasterLayerTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RasterLayerTest.java
@@ -489,24 +489,6 @@ public class RasterLayerTest extends BaseActivityTest {
}
@Test
- public void testRasterFadeDurationTransition() {
- validateTestSetup();
- setupLayer();
- Timber.i("raster-fade-durationTransitionOptions");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setRasterFadeDurationTransition(options);
- assertEquals(layer.getRasterFadeDurationTransition(), options);
- }
- });
- }
-
- @Test
public void testRasterFadeDurationAsConstant() {
validateTestSetup();
setupLayer();
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RuntimeStyleTests.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RuntimeStyleTests.java
index bf8bcb9f66..fc526176d4 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RuntimeStyleTests.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RuntimeStyleTests.java
@@ -8,7 +8,6 @@ import android.support.test.espresso.ViewAction;
import android.support.test.runner.AndroidJUnit4;
import android.view.View;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.style.layers.CannotAddLayerException;
import com.mapbox.mapboxsdk.style.layers.CircleLayer;
import com.mapbox.mapboxsdk.style.layers.FillLayer;
@@ -22,7 +21,6 @@ import com.mapbox.mapboxsdk.style.sources.RasterSource;
import com.mapbox.mapboxsdk.style.sources.Source;
import com.mapbox.mapboxsdk.style.sources.VectorSource;
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.style.RuntimeStyleTestActivity;
@@ -192,41 +190,38 @@ public class RuntimeStyleTests extends BaseActivityTest {
@Test
public void testAddRemoveSource() {
validateTestSetup();
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- mapboxMap.addSource(new VectorSource("my-source", "mapbox://mapbox.mapbox-terrain-v2"));
- mapboxMap.removeSource("my-source");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ mapboxMap.addSource(new VectorSource("my-source", "mapbox://mapbox.mapbox-terrain-v2"));
+ mapboxMap.removeSource("my-source");
- // Add initial source
- mapboxMap.addSource(new VectorSource("my-source", "mapbox://mapbox.mapbox-terrain-v2"));
+ // Add initial source
+ mapboxMap.addSource(new VectorSource("my-source", "mapbox://mapbox.mapbox-terrain-v2"));
- // Remove
- Source mySource = mapboxMap.removeSource("my-source");
- assertNotNull(mySource);
- assertNull(mapboxMap.getLayer("my-source"));
+ // Remove
+ Source mySource = mapboxMap.removeSource("my-source");
+ assertNotNull(mySource);
+ assertNull(mapboxMap.getLayer("my-source"));
- // Add
- Source source = new VectorSource("my-source", "mapbox://mapbox.mapbox-terrain-v2");
- mapboxMap.addSource(source);
+ // Add
+ Source source = new VectorSource("my-source", "mapbox://mapbox.mapbox-terrain-v2");
+ mapboxMap.addSource(source);
- // Remove, preserving the reference
- mapboxMap.removeSource(source);
+ // Remove, preserving the reference
+ mapboxMap.removeSource(source);
- // Re-add the reference...
- mapboxMap.addSource(source);
+ // Re-add the reference...
+ mapboxMap.addSource(source);
- // Ensure it's there
- Assert.assertNotNull(mapboxMap.getSource(source.getId()));
+ // Ensure it's there
+ Assert.assertNotNull(mapboxMap.getSource(source.getId()));
- // Test adding a duplicate source
- try {
- Source source2 = new VectorSource("my-source", "mapbox://mapbox.mapbox-terrain-v2");
- mapboxMap.addSource(source2);
- fail("Should not have been allowed to add a source with a duplicate id");
- } catch (CannotAddSourceException cannotAddSourceException) {
- // OK
- }
+ // Test adding a duplicate source
+ try {
+ Source source2 = new VectorSource("my-source", "mapbox://mapbox.mapbox-terrain-v2");
+ mapboxMap.addSource(source2);
+ fail("Should not have been allowed to add a source with a duplicate id");
+ } catch (CannotAddSourceException cannotAddSourceException) {
+ // OK
}
});
@@ -235,45 +230,36 @@ public class RuntimeStyleTests extends BaseActivityTest {
@Test
public void testVectorSourceUrlGetter() {
validateTestSetup();
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- VectorSource source = new VectorSource("my-source", "mapbox://mapbox.mapbox-terrain-v2");
- mapboxMap.addSource(source);
- assertEquals("mapbox://mapbox.mapbox-terrain-v2", source.getUrl());
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ VectorSource source = new VectorSource("my-source", "mapbox://mapbox.mapbox-terrain-v2");
+ mapboxMap.addSource(source);
+ assertEquals("mapbox://mapbox.mapbox-terrain-v2", source.getUrl());
});
}
@Test
public void testRasterSourceUrlGetter() {
validateTestSetup();
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- RasterSource source = new RasterSource("my-source", "mapbox://mapbox.mapbox-terrain-v2");
- mapboxMap.addSource(source);
- assertEquals("mapbox://mapbox.mapbox-terrain-v2", source.getUrl());
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ RasterSource source = new RasterSource("my-source", "mapbox://mapbox.mapbox-terrain-v2");
+ mapboxMap.addSource(source);
+ assertEquals("mapbox://mapbox.mapbox-terrain-v2", source.getUrl());
});
}
@Test
public void testGeoJsonSourceUrlGetter() throws MalformedURLException {
validateTestSetup();
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- GeoJsonSource source = new GeoJsonSource("my-source");
- mapboxMap.addSource(source);
- assertNull(source.getUrl());
- try {
- source.setUrl(new URL("http://mapbox.com/my-file.json"));
- } catch (MalformedURLException exception) {
- fail();
- }
- assertEquals("http://mapbox.com/my-file.json", source.getUrl());
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ GeoJsonSource source = new GeoJsonSource("my-source");
+ mapboxMap.addSource(source);
+ assertNull(source.getUrl());
+ try {
+ source.setUrl(new URL("http://mapbox.com/my-file.json"));
+ } catch (MalformedURLException exception) {
+ fail();
}
+ assertEquals("http://mapbox.com/my-file.json", source.getUrl());
});
}
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 f8248ae4a7..781307862d 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
@@ -1616,6 +1616,63 @@ public class SymbolLayerTest extends BaseActivityTest {
}
@Test
+ public void testTextFontAsIdentitySourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("text-font");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ textFont(property("FeaturePropertyA", Stops.<String[]>identity()))
+ );
+
+ // Verify
+ assertNotNull(layer.getTextFont());
+ assertNotNull(layer.getTextFont().getFunction());
+ assertEquals(SourceFunction.class, layer.getTextFont().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextFont().getFunction()).getProperty());
+ assertEquals(IdentityStops.class, layer.getTextFont().getFunction().getStops().getClass());
+ }
+ });
+ }
+
+ @Test
+ public void testTextFontAsIntervalSourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("text-font");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ textFont(
+ property(
+ "FeaturePropertyA",
+ interval(
+ stop(1, textFont(new String[]{"Open Sans Regular", "Arial Unicode MS Regular"}))
+ )
+ )
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getTextFont());
+ assertNotNull(layer.getTextFont().getFunction());
+ assertEquals(SourceFunction.class, layer.getTextFont().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextFont().getFunction()).getProperty());
+ assertEquals(IntervalStops.class, layer.getTextFont().getFunction().getStops().getClass());
+ }
+ });
+ }
+
+ @Test
public void testTextSizeAsConstant() {
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 192740f708..206497b860 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
@@ -99,7 +99,7 @@ public class <%- camelize(type) %>LayerTest extends BaseActivityTest {
}
});
}
-<% if (!(type === 'background' || type === 'raster')) { -%>
+<% if (!(type === 'background' || type === 'raster' || type === 'hillshade')) { -%>
@Test
public void testSourceLayer() {
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
index 6f311788ba..5a0493e5bd 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
@@ -598,7 +598,17 @@
android:name="android.support.PARENT_ACTIVITY"
android:value=".activity.FeatureOverviewActivity"/>
</activity>
-
+ <activity
+ android:name=".activity.style.GridSourceActivity"
+ android:description="@string/description_grid_source"
+ android:label="@string/activity_grid_source">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_style"/>
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value=".activity.FeatureOverviewActivity"/>
+ </activity>
<!-- Features -->
<activity
android:name=".activity.feature.QueryRenderedFeaturesPropertiesActivity"
@@ -768,6 +778,28 @@
android:name="android.support.PARENT_ACTIVITY"
android:value=".activity.FeatureOverviewActivity"/>
</activity>
+ <activity
+ android:name=".activity.style.HillshadeLayerActivity"
+ android:description="@string/description_hillshade"
+ android:label="@string/activity_hillshade">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_style"/>
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value=".activity.FeatureOverviewActivity"/>
+ </activity>
+ <activity
+ android:name=".activity.style.HeatmapLayerActivity"
+ android:description="@string/description_heatmaplayer"
+ android:label="@string/activity_heatmaplayer">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_style"/>
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value=".activity.FeatureOverviewActivity"/>
+ </activity>
<!-- For Instrumentation tests -->
<activity
@@ -785,7 +817,7 @@
<!-- Configuration Settings -->
<meta-data
android:name="com.mapbox.TestEventsServer"
- android:value="https://cloudfront-staging.tilestream.net"/>
+ android:value="api-events-staging.tilestream.net"/>
<meta-data
android:name="com.mapbox.TestEventsAccessToken"
android:value="pk.eyJ1IjoiYmxzdGFnaW5nIiwiYSI6ImNpdDF3OHpoaTAwMDcyeXA5Y3Z0Nmk2dzEifQ.0IfB7v5Qbm2MGVYt8Kb8fg"/>
@@ -796,4 +828,4 @@
<!-- android:value="true" /> -->
</application>
-</manifest> \ No newline at end of file
+</manifest>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java
index 3f20f19f5d..0ee1f78e0e 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java
@@ -16,15 +16,14 @@ import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
-import android.view.View;
import com.mapbox.mapboxsdk.testapp.R;
import com.mapbox.mapboxsdk.testapp.adapter.FeatureAdapter;
import com.mapbox.mapboxsdk.testapp.adapter.FeatureSectionAdapter;
import com.mapbox.mapboxsdk.testapp.model.activity.Feature;
import com.mapbox.mapboxsdk.testapp.utils.ItemClickSupport;
-import com.mapbox.services.android.telemetry.permissions.PermissionsListener;
-import com.mapbox.services.android.telemetry.permissions.PermissionsManager;
+import com.mapbox.android.core.permissions.PermissionsListener;
+import com.mapbox.android.core.permissions.PermissionsManager;
import java.util.ArrayList;
import java.util.Collections;
@@ -62,19 +61,16 @@ public class FeatureOverviewActivity extends AppCompatActivity implements Permis
recyclerView.addOnItemTouchListener(new RecyclerView.SimpleOnItemTouchListener());
recyclerView.setHasFixedSize(true);
- ItemClickSupport.addTo(recyclerView).setOnItemClickListener(new ItemClickSupport.OnItemClickListener() {
- @Override
- public void onItemClicked(RecyclerView recyclerView, int position, View view) {
- if (!sectionAdapter.isSectionHeaderPosition(position)) {
- int itemPosition = sectionAdapter.getConvertedPosition(position);
- Feature feature = features.get(itemPosition);
- if (feature.isRequiresLocationPermission()) {
- if (requestLocationPermission(itemPosition)) {
- return;
- }
+ ItemClickSupport.addTo(recyclerView).setOnItemClickListener((recyclerView, position, view) -> {
+ if (!sectionAdapter.isSectionHeaderPosition(position)) {
+ int itemPosition = sectionAdapter.getConvertedPosition(position);
+ Feature feature = features.get(itemPosition);
+ if (feature.isRequiresLocationPermission()) {
+ if (requestLocationPermission(itemPosition)) {
+ return;
}
- startFeature(feature);
}
+ startFeature(feature);
}
});
@@ -188,15 +184,12 @@ public class FeatureOverviewActivity extends AppCompatActivity implements Permis
}
if (!features.isEmpty()) {
- Comparator<Feature> comparator = new Comparator<Feature>() {
- @Override
- public int compare(Feature lhs, Feature rhs) {
- int result = lhs.getCategory().compareToIgnoreCase(rhs.getCategory());
- if (result == 0) {
- result = lhs.getLabel().compareToIgnoreCase(rhs.getLabel());
- }
- return result;
+ Comparator<Feature> comparator = (lhs, rhs) -> {
+ int result = lhs.getCategory().compareToIgnoreCase(rhs.getCategory());
+ if (result == 0) {
+ result = lhs.getLabel().compareToIgnoreCase(rhs.getLabel());
}
+ return result;
};
Collections.sort(features, comparator);
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java
index d8752bbea2..e6db071141 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java
@@ -7,12 +7,12 @@ import android.animation.TypeEvaluator;
import android.animation.ValueAnimator;
import android.os.Bundle;
import android.support.annotation.DrawableRes;
-import android.support.annotation.NonNull;
import android.support.v4.content.res.ResourcesCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
+import com.mapbox.geojson.Point;
import com.mapbox.mapboxsdk.annotations.Icon;
import com.mapbox.mapboxsdk.annotations.IconFactory;
import com.mapbox.mapboxsdk.annotations.Marker;
@@ -24,11 +24,9 @@ import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
import com.mapbox.mapboxsdk.testapp.utils.IconUtils;
-import com.mapbox.services.api.utils.turf.TurfMeasurement;
-import com.mapbox.services.commons.models.Position;
+import com.mapbox.turf.TurfMeasurement;
import java.util.ArrayList;
import java.util.List;
@@ -59,25 +57,18 @@ public class AnimatedMarkerActivity extends AppCompatActivity {
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
+ mapView.getMapAsync(mapboxMap -> {
+ AnimatedMarkerActivity.this.mapboxMap = mapboxMap;
+ setupMap();
- @Override
- public void onMapReady(@NonNull final MapboxMap mapboxMap) {
- AnimatedMarkerActivity.this.mapboxMap = mapboxMap;
- setupMap();
-
- animationRunnable = new Runnable() {
- @Override
- public void run() {
- for (int i = 0; i < 10; i++) {
- addRandomCar();
- }
- addPassenger();
- addMainCar();
- }
- };
- mapView.post(animationRunnable);
- }
+ animationRunnable = () -> {
+ for (int i = 0; i < 10; i++) {
+ addRandomCar();
+ }
+ addPassenger();
+ addMainCar();
+ };
+ mapView.post(animationRunnable);
});
}
@@ -116,13 +107,10 @@ public class AnimatedMarkerActivity extends AppCompatActivity {
if (carMarker == null) {
carMarker = createCarMarker(randomLatLng, R.drawable.ic_taxi_top,
- new MarkerViewManager.OnMarkerViewAddedListener() {
- @Override
- public void onViewAdded(@NonNull MarkerView markerView) {
- // Make sure the car marker is selected so that it's always brought to the front (#5285)
- mapboxMap.selectMarker(carMarker);
- animateMoveToPassenger(carMarker);
- }
+ markerView -> {
+ // Make sure the car marker is selected so that it's always brought to the front (#5285)
+ mapboxMap.selectMarker(carMarker);
+ animateMoveToPassenger(carMarker);
});
markerViews.add(carMarker);
} else {
@@ -147,12 +135,7 @@ public class AnimatedMarkerActivity extends AppCompatActivity {
protected void addRandomCar() {
markerViews.add(createCarMarker(getLatLngInBounds(), R.drawable.ic_car_top,
- new MarkerViewManager.OnMarkerViewAddedListener() {
- @Override
- public void onViewAdded(@NonNull MarkerView markerView) {
- randomlyMoveMarker(markerView);
- }
- }));
+ markerView -> randomlyMoveMarker(markerView)));
}
private void randomlyMoveMarker(final MarkerView marker) {
@@ -289,8 +272,8 @@ public class AnimatedMarkerActivity extends AppCompatActivity {
private double getBearing(LatLng from, LatLng to) {
return TurfMeasurement.bearing(
- Position.fromCoordinates(from.getLongitude(), from.getLatitude()),
- Position.fromCoordinates(to.getLongitude(), to.getLatitude())
+ Point.fromLngLat(from.getLongitude(), from.getLatitude()),
+ Point.fromLngLat(to.getLongitude(), to.getLatitude())
);
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/BulkMarkerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/BulkMarkerActivity.java
index 666fd2eee6..afeb3b8979 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/BulkMarkerActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/BulkMarkerActivity.java
@@ -5,7 +5,6 @@ import android.animation.AnimatorListenerAdapter;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
-import android.support.annotation.NonNull;
import android.support.v4.content.res.ResourcesCompat;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.AppCompatActivity;
@@ -17,30 +16,25 @@ import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
-
import com.mapbox.mapboxsdk.annotations.Icon;
-import com.mapbox.mapboxsdk.annotations.Marker;
import com.mapbox.mapboxsdk.annotations.MarkerOptions;
import com.mapbox.mapboxsdk.annotations.MarkerViewOptions;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
import com.mapbox.mapboxsdk.testapp.utils.GeoParseUtil;
import com.mapbox.mapboxsdk.testapp.utils.IconUtils;
-
-import org.json.JSONException;
+import timber.log.Timber;
import java.io.IOException;
+import java.lang.ref.WeakReference;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Random;
-import timber.log.Timber;
-
/**
* Test activity showcasing adding a large amount of Markers or MarkerViews.
*/
@@ -50,6 +44,7 @@ public class BulkMarkerActivity extends AppCompatActivity implements AdapterView
private MapView mapView;
private boolean customMarkerView;
private List<LatLng> locations;
+ private ProgressDialog progressDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -58,12 +53,7 @@ public class BulkMarkerActivity extends AppCompatActivity implements AdapterView
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull MapboxMap mapboxMap) {
- BulkMarkerActivity.this.mapboxMap = mapboxMap;
- }
- });
+ mapView.getMapAsync(mapboxMap -> BulkMarkerActivity.this.mapboxMap = mapboxMap);
final View fab = findViewById(R.id.fab);
if (fab != null) {
@@ -88,6 +78,7 @@ public class BulkMarkerActivity extends AppCompatActivity implements AdapterView
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
int amount = Integer.valueOf(getResources().getStringArray(R.array.bulk_marker_list)[position]);
if (locations == null) {
+ progressDialog = ProgressDialog.show(this, "Loading", "Fetching markers", false);
new LoadLocationTask(this, amount).execute();
} else {
showMarkers(amount);
@@ -95,6 +86,7 @@ public class BulkMarkerActivity extends AppCompatActivity implements AdapterView
}
private void onLatLngListLoaded(List<LatLng> latLngs, int amount) {
+ progressDialog.hide();
locations = latLngs;
showMarkers(amount);
}
@@ -232,29 +224,22 @@ public class BulkMarkerActivity extends AppCompatActivity implements AdapterView
viewCountView = (TextView) findViewById(R.id.countView);
- mapView.addOnMapChangedListener(new MapView.OnMapChangedListener() {
- @Override
- public void onMapChanged(@MapView.MapChange int change) {
- if (change == MapView.REGION_IS_CHANGING || change == MapView.REGION_DID_CHANGE) {
- if (!mapboxMap.getMarkerViewManager().getMarkerViewAdapters().isEmpty()) {
- viewCountView.setText(String.format(Locale.getDefault(), "ViewCache size %d",
- mapboxMap.getMarkerViewManager().getMarkerViewContainer().getChildCount()));
- }
+ mapView.addOnMapChangedListener(change -> {
+ if (change == MapView.REGION_IS_CHANGING || change == MapView.REGION_DID_CHANGE) {
+ if (!mapboxMap.getMarkerViewManager().getMarkerViewAdapters().isEmpty()) {
+ viewCountView.setText(String.format(Locale.getDefault(), "ViewCache size %d",
+ mapboxMap.getMarkerViewManager().getMarkerViewContainer().getChildCount()));
}
}
});
mapboxMap.getMarkerViewManager().setOnMarkerViewClickListener(
- new MapboxMap.OnMarkerViewClickListener() {
- @Override
- public boolean onMarkerClick(
- @NonNull Marker marker, @NonNull View view, @NonNull MapboxMap.MarkerViewAdapter adapter) {
- Toast.makeText(
- BulkMarkerActivity.this,
- "Hello " + marker.getId(),
- Toast.LENGTH_SHORT).show();
- return false;
- }
+ (marker, view1, adapter) -> {
+ Toast.makeText(
+ BulkMarkerActivity.this,
+ "Hello " + marker.getId(),
+ Toast.LENGTH_SHORT).show();
+ return false;
});
}
}
@@ -262,32 +247,39 @@ public class BulkMarkerActivity extends AppCompatActivity implements AdapterView
private static class LoadLocationTask extends AsyncTask<Void, Integer, List<LatLng>> {
- private BulkMarkerActivity activity;
- private ProgressDialog progressDialog;
+ private WeakReference<BulkMarkerActivity> activity;
private int amount;
private LoadLocationTask(BulkMarkerActivity activity, int amount) {
this.amount = amount;
- this.activity = activity;
- progressDialog = ProgressDialog.show(activity, "Loading", "Fetching markers", false);
+ this.activity = new WeakReference<>(activity);
}
@Override
protected List<LatLng> doInBackground(Void... params) {
- try {
- String json = GeoParseUtil.loadStringFromAssets(activity.getApplicationContext(), "points.geojson");
- return GeoParseUtil.parseGeoJsonCoordinates(json);
- } catch (IOException | JSONException exception) {
- Timber.e(exception, "Could not add markers");
- return null;
+ BulkMarkerActivity activity = this.activity.get();
+ if (activity != null) {
+ String json = null;
+ try {
+ json = GeoParseUtil.loadStringFromAssets(activity.getApplicationContext(), "points.geojson");
+ } catch (IOException exception) {
+ Timber.e(exception, "Could not add markers");
+ }
+
+ if (json != null) {
+ return GeoParseUtil.parseGeoJsonCoordinates(json);
+ }
}
+ return null;
}
@Override
protected void onPostExecute(List<LatLng> locations) {
super.onPostExecute(locations);
- activity.onLatLngListLoaded(locations, amount);
- progressDialog.hide();
+ BulkMarkerActivity activity = this.activity.get();
+ if (activity != null) {
+ activity.onLatLngListLoaded(locations, amount);
+ }
}
}
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/DynamicMarkerChangeActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/DynamicMarkerChangeActivity.java
index f7ffc61a7d..1fe0340a55 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/DynamicMarkerChangeActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/DynamicMarkerChangeActivity.java
@@ -1,19 +1,16 @@
package com.mapbox.mapboxsdk.testapp.activity.annotation;
import android.os.Bundle;
-import android.support.annotation.NonNull;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.res.ResourcesCompat;
import android.support.v7.app.AppCompatActivity;
-import android.view.View;
import com.mapbox.mapboxsdk.annotations.Marker;
import com.mapbox.mapboxsdk.annotations.MarkerOptions;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
import com.mapbox.mapboxsdk.testapp.utils.IconUtils;
@@ -37,29 +34,23 @@ public class DynamicMarkerChangeActivity extends AppCompatActivity {
mapView = (MapView) findViewById(R.id.mapView);
mapView.setTag(false);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull MapboxMap mapboxMap) {
- DynamicMarkerChangeActivity.this.mapboxMap = mapboxMap;
- // Create marker
- MarkerOptions markerOptions = new MarkerOptions()
- .position(LAT_LNG_CHELSEA)
- .icon(IconUtils.drawableToIcon(DynamicMarkerChangeActivity.this, R.drawable.ic_stars,
- ResourcesCompat.getColor(getResources(), R.color.blueAccent, getTheme())))
- .title(getString(R.string.dynamic_marker_chelsea_title))
- .snippet(getString(R.string.dynamic_marker_chelsea_snippet));
- marker = mapboxMap.addMarker(markerOptions);
- }
+ mapView.getMapAsync(mapboxMap -> {
+ DynamicMarkerChangeActivity.this.mapboxMap = mapboxMap;
+ // Create marker
+ MarkerOptions markerOptions = new MarkerOptions()
+ .position(LAT_LNG_CHELSEA)
+ .icon(IconUtils.drawableToIcon(DynamicMarkerChangeActivity.this, R.drawable.ic_stars,
+ ResourcesCompat.getColor(getResources(), R.color.blueAccent, getTheme())))
+ .title(getString(R.string.dynamic_marker_chelsea_title))
+ .snippet(getString(R.string.dynamic_marker_chelsea_snippet));
+ marker = mapboxMap.addMarker(markerOptions);
});
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setColorFilter(ContextCompat.getColor(this, R.color.primary));
- fab.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (mapboxMap != null) {
- updateMarker();
- }
+ fab.setOnClickListener(view -> {
+ if (mapboxMap != null) {
+ updateMarker();
}
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewActivity.java
index 61ece0a94f..8c0dd69a0c 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewActivity.java
@@ -29,7 +29,6 @@ import com.mapbox.mapboxsdk.annotations.MarkerViewOptions;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
import com.mapbox.mapboxsdk.testapp.model.annotations.CountryMarkerView;
import com.mapbox.mapboxsdk.testapp.model.annotations.CountryMarkerViewOptions;
@@ -81,127 +80,117 @@ public class MarkerViewActivity extends AppCompatActivity {
final TextView viewCountView = (TextView) findViewById(R.id.countView);
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull MapboxMap mapboxMap) {
- MarkerViewActivity.this.mapboxMap = mapboxMap;
-
- final MarkerViewManager markerViewManager = mapboxMap.getMarkerViewManager();
-
- Icon usFlag = IconFactory.getInstance(MarkerViewActivity.this)
- .fromResource(R.drawable.ic_us);
-
- // add default ViewMarker markers
- for (int i = 0; i < LAT_LNGS.length; i++) {
- MarkerViewActivity.this.mapboxMap.addMarker(new MarkerViewOptions()
- .position(LAT_LNGS[i])
- .title(String.valueOf(i))
- .alpha(0.5f)
- .icon(usFlag)
- );
- }
-
- // add custom ViewMarker
- CountryMarkerViewOptions options = new CountryMarkerViewOptions();
- options.flagRes(R.drawable.icon_burned);
- options.abbrevName("Mapbox");
- options.title("Hello");
- options.position(new LatLng(38.899774, -77.023237));
- options.flat(true);
- MarkerView markerView = mapboxMap.addMarker(options);
-
- // Use object animator to rotate MarkerView
- ValueAnimator markerAnimator = ObjectAnimator.ofObject(markerView, "rotation", new FloatEvaluator(), -90, 90);
- markerAnimator.setDuration(5000);
- markerAnimator.start();
-
- MarkerViewActivity.this.mapboxMap.addMarker(new MarkerOptions()
- .title("United States")
- .position(new LatLng(38.902580, -77.050102))
- );
-
- rotateMarker = MarkerViewActivity.this.mapboxMap.addMarker(new TextMarkerViewOptions()
- .text("A")
- .rotation(rotation = 270)
- .position(new LatLng(38.889876, -77.008849))
- );
- loopMarkerRotate();
+ mapView.getMapAsync(mapboxMap -> {
+ MarkerViewActivity.this.mapboxMap = mapboxMap;
+ final MarkerViewManager markerViewManager = mapboxMap.getMarkerViewManager();
- MarkerViewActivity.this.mapboxMap.addMarker(new TextMarkerViewOptions()
- .text("B")
- .position(new LatLng(38.907327, -77.041293))
- );
+ Icon usFlag = IconFactory.getInstance(MarkerViewActivity.this)
+ .fromResource(R.drawable.ic_us);
- MarkerViewActivity.this.mapboxMap.addMarker(new TextMarkerViewOptions()
- .text("C")
- .position(new LatLng(38.897642, -77.041980))
- );
-
- // if you want to customise a ViewMarker you need to extend ViewMarker and provide an adapter implementation
- // set adapters for child classes of ViewMarker
- markerViewManager.addMarkerViewAdapter(new CountryAdapter(MarkerViewActivity.this, mapboxMap));
- markerViewManager.addMarkerViewAdapter(new TextAdapter(MarkerViewActivity.this, mapboxMap));
-
- final ViewGroup markerViewContainer = markerViewManager.getMarkerViewContainer();
-
- // add a change listener to validate the size of amount of child views
- mapView.addOnMapChangedListener(new MapView.OnMapChangedListener() {
- @Override
- public void onMapChanged(@MapView.MapChange int change) {
- if (change == MapView.REGION_IS_CHANGING || change == MapView.REGION_DID_CHANGE) {
- if (!markerViewManager.getMarkerViewAdapters().isEmpty() && viewCountView != null) {
- viewCountView.setText(String.format(
- Locale.getDefault(),
- getString(R.string.viewcache_size),
- markerViewContainer.getChildCount())
- );
- }
- }
- }
- });
-
- // add a OnMarkerView click listener
- MarkerViewActivity.this.mapboxMap.getMarkerViewManager().setOnMarkerViewClickListener(
- new MapboxMap.OnMarkerViewClickListener() {
- @Override
- public boolean onMarkerClick(@NonNull Marker marker, @NonNull View view,
- @NonNull MapboxMap.MarkerViewAdapter adapter) {
- Toast.makeText(MarkerViewActivity.this, "Hello " + marker.getId(), Toast.LENGTH_SHORT).show();
- return false;
- }
- });
-
- movingMarkerOne = MarkerViewActivity.this.mapboxMap.addMarker(new MarkerViewOptions()
- .position(CarLocation.CAR_0_LNGS[0])
- .icon(IconFactory.getInstance(mapView.getContext())
- .fromResource(R.drawable.ic_android))
+ // add default ViewMarker markers
+ for (int i = 0; i < LAT_LNGS.length; i++) {
+ MarkerViewActivity.this.mapboxMap.addMarker(new MarkerViewOptions()
+ .position(LAT_LNGS[i])
+ .title(String.valueOf(i))
+ .alpha(0.5f)
+ .icon(usFlag)
);
+ }
- movingMarkerTwo = mapboxMap.addMarker(new MarkerViewOptions()
- .position(CarLocation.CAR_1_LNGS[0])
- .icon(IconFactory.getInstance(mapView.getContext())
- .fromResource(R.drawable.ic_android_2))
- );
+ // add custom ViewMarker
+ CountryMarkerViewOptions options = new CountryMarkerViewOptions();
+ options.flagRes(R.drawable.icon_burned);
+ options.abbrevName("Mapbox");
+ options.title("Hello");
+ options.position(new LatLng(38.899774, -77.023237));
+ options.flat(true);
+ MarkerView markerView = mapboxMap.addMarker(options);
+
+ // Use object animator to rotate MarkerView
+ ValueAnimator markerAnimator = ObjectAnimator.ofObject(markerView, "rotation", new FloatEvaluator(), -90, 90);
+ markerAnimator.setDuration(5000);
+ markerAnimator.start();
+
+ MarkerViewActivity.this.mapboxMap.addMarker(new MarkerOptions()
+ .title("United States")
+ .position(new LatLng(38.902580, -77.050102))
+ );
+
+ rotateMarker = MarkerViewActivity.this.mapboxMap.addMarker(new TextMarkerViewOptions()
+ .text("A")
+ .rotation(rotation = 270)
+ .position(new LatLng(38.889876, -77.008849))
+ );
+ loopMarkerRotate();
- // allow more open infowindows at the same time
- mapboxMap.setAllowConcurrentMultipleOpenInfoWindows(true);
- // add offscreen markers
- Marker markerRightOffScreen = mapboxMap.addMarker(new MarkerOptions()
- .setPosition(new LatLng(38.892846, -76.909399))
- .title("InfoWindow")
- .snippet("Offscreen, to the right of the Map."));
+ MarkerViewActivity.this.mapboxMap.addMarker(new TextMarkerViewOptions()
+ .text("B")
+ .position(new LatLng(38.907327, -77.041293))
+ );
+
+ MarkerViewActivity.this.mapboxMap.addMarker(new TextMarkerViewOptions()
+ .text("C")
+ .position(new LatLng(38.897642, -77.041980))
+ );
+
+ // if you want to customise a ViewMarker you need to extend ViewMarker and provide an adapter implementation
+ // set adapters for child classes of ViewMarker
+ markerViewManager.addMarkerViewAdapter(new CountryAdapter(MarkerViewActivity.this, mapboxMap));
+ markerViewManager.addMarkerViewAdapter(new TextAdapter(MarkerViewActivity.this, mapboxMap));
+
+ final ViewGroup markerViewContainer = markerViewManager.getMarkerViewContainer();
+
+ // add a change listener to validate the size of amount of child views
+ mapView.addOnMapChangedListener(change -> {
+ if (change == MapView.REGION_IS_CHANGING || change == MapView.REGION_DID_CHANGE) {
+ if (!markerViewManager.getMarkerViewAdapters().isEmpty() && viewCountView != null) {
+ viewCountView.setText(String.format(
+ Locale.getDefault(),
+ getString(R.string.viewcache_size),
+ markerViewContainer.getChildCount())
+ );
+ }
+ }
+ });
- Marker markerRightBottomOffScreen = mapboxMap.addMarker(new MarkerOptions()
- .setPosition(new LatLng(38.791645, -77.039006))
- .title("InfoWindow")
- .snippet("Offscreen, to the bottom of the Map"));
+ // add a OnMarkerView click listener
+ MarkerViewActivity.this.mapboxMap.getMarkerViewManager().setOnMarkerViewClickListener(
+ (marker, view, adapter) -> {
+ Toast.makeText(MarkerViewActivity.this, "Hello " + marker.getId(), Toast.LENGTH_SHORT).show();
+ return false;
+ });
- // open infowindow offscreen markers
- mapboxMap.selectMarker(markerRightOffScreen);
- mapboxMap.selectMarker(markerRightBottomOffScreen);
- }
+ movingMarkerOne = MarkerViewActivity.this.mapboxMap.addMarker(new MarkerViewOptions()
+ .position(CarLocation.CAR_0_LNGS[0])
+ .icon(IconFactory.getInstance(mapView.getContext())
+ .fromResource(R.drawable.ic_android))
+ );
+
+ movingMarkerTwo = mapboxMap.addMarker(new MarkerViewOptions()
+ .position(CarLocation.CAR_1_LNGS[0])
+ .icon(IconFactory.getInstance(mapView.getContext())
+ .fromResource(R.drawable.ic_android_2))
+ );
+
+ // allow more open infowindows at the same time
+ mapboxMap.setAllowConcurrentMultipleOpenInfoWindows(true);
+
+ // add offscreen markers
+ Marker markerRightOffScreen = mapboxMap.addMarker(new MarkerOptions()
+ .setPosition(new LatLng(38.892846, -76.909399))
+ .title("InfoWindow")
+ .snippet("Offscreen, to the right of the Map."));
+
+ Marker markerRightBottomOffScreen = mapboxMap.addMarker(new MarkerOptions()
+ .setPosition(new LatLng(38.791645, -77.039006))
+ .title("InfoWindow")
+ .snippet("Offscreen, to the bottom of the Map"));
+
+ // open infowindow offscreen markers
+ mapboxMap.selectMarker(markerRightOffScreen);
+ mapboxMap.selectMarker(markerRightBottomOffScreen);
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolygonActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolygonActivity.java
index 49c9663672..93f2e9c98f 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolygonActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolygonActivity.java
@@ -2,7 +2,6 @@ package com.mapbox.mapboxsdk.testapp.activity.annotation;
import android.graphics.Color;
import android.os.Bundle;
-import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
@@ -78,16 +77,11 @@ public class PolygonActivity extends AppCompatActivity implements OnMapReadyCall
public void onMapReady(MapboxMap map) {
mapboxMap = map;
- map.setOnPolygonClickListener(new MapboxMap.OnPolygonClickListener() {
- @Override
- public void onPolygonClick(@NonNull Polygon polygon) {
- Toast.makeText(
- PolygonActivity.this,
- "You clicked on polygon with id = " + polygon.getId(),
- Toast.LENGTH_SHORT
- ).show();
- }
- });
+ map.setOnPolygonClickListener(polygon -> Toast.makeText(
+ PolygonActivity.this,
+ "You clicked on polygon with id = " + polygon.getId(),
+ Toast.LENGTH_SHORT
+ ).show());
polygon = mapboxMap.addPolygon(new PolygonOptions()
.addAll(STAR_SHAPE_POINTS)
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolylineActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolylineActivity.java
index b9dc39e5d4..fbf439448f 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolylineActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolylineActivity.java
@@ -2,7 +2,6 @@ package com.mapbox.mapboxsdk.testapp.activity.annotation;
import android.graphics.Color;
import android.os.Bundle;
-import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
@@ -14,7 +13,6 @@ import com.mapbox.mapboxsdk.annotations.PolylineOptions;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
import java.util.ArrayList;
@@ -65,46 +63,35 @@ public class PolylineActivity extends AppCompatActivity {
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull final MapboxMap mapboxMap) {
- PolylineActivity.this.mapboxMap = mapboxMap;
-
- mapboxMap.setOnPolylineClickListener(new MapboxMap.OnPolylineClickListener() {
- @Override
- public void onPolylineClick(@NonNull Polyline polyline) {
- Toast.makeText(
- PolylineActivity.this,
- "You clicked on polygon with id = " + polyline.getId(),
- Toast.LENGTH_SHORT
- ).show();
- }
- });
+ mapView.getMapAsync(mapboxMap -> {
+ PolylineActivity.this.mapboxMap = mapboxMap;
- polylines = mapboxMap.addPolylines(polylineOptions);
- }
+ mapboxMap.setOnPolylineClickListener(polyline -> Toast.makeText(
+ PolylineActivity.this,
+ "You clicked on polygon with id = " + polyline.getId(),
+ Toast.LENGTH_SHORT
+ ).show());
+
+ polylines = mapboxMap.addPolylines(polylineOptions);
});
View fab = findViewById(R.id.fab);
if (fab != null) {
- fab.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (mapboxMap != null) {
- if (polylines != null && polylines.size() > 0) {
- if (polylines.size() == 1) {
- // test for removing annotation
- mapboxMap.removeAnnotation(polylines.get(0));
- } else {
- // test for removing annotations
- mapboxMap.removeAnnotations(polylines);
- }
+ fab.setOnClickListener(view -> {
+ if (mapboxMap != null) {
+ if (polylines != null && polylines.size() > 0) {
+ if (polylines.size() == 1) {
+ // test for removing annotation
+ mapboxMap.removeAnnotation(polylines.get(0));
+ } else {
+ // test for removing annotations
+ mapboxMap.removeAnnotations(polylines);
}
- polylineOptions.clear();
- polylineOptions.addAll(getRandomLine());
- polylines = mapboxMap.addPolylines(polylineOptions);
-
}
+ polylineOptions.clear();
+ polylineOptions.addAll(getRandomLine());
+ polylines = mapboxMap.addPolylines(polylineOptions);
+
}
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PressForMarkerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PressForMarkerActivity.java
index 7cfe35f160..29c0ae0fca 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PressForMarkerActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PressForMarkerActivity.java
@@ -2,7 +2,6 @@ package com.mapbox.mapboxsdk.testapp.activity.annotation;
import android.graphics.PointF;
import android.os.Bundle;
-import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
@@ -12,7 +11,6 @@ import com.mapbox.mapboxsdk.annotations.MarkerOptions;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
import java.text.DecimalFormat;
@@ -41,30 +39,17 @@ public class PressForMarkerActivity extends AppCompatActivity {
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(final MapboxMap map) {
- mapboxMap = map;
- resetMap();
+ mapView.getMapAsync(map -> {
+ mapboxMap = map;
+ resetMap();
+
+ mapboxMap.setOnMapLongClickListener(point -> addMarker(point));
+
+ mapboxMap.setOnMapClickListener(point -> addMarker(point));
- mapboxMap.setOnMapLongClickListener(new MapboxMap.OnMapLongClickListener() {
- @Override
- public void onMapLongClick(@NonNull LatLng point) {
- addMarker(point);
- }
- });
-
- mapboxMap.setOnMapClickListener(new MapboxMap.OnMapClickListener() {
- @Override
- public void onMapClick(@NonNull LatLng point) {
- addMarker(point);
- }
- });
-
- if (savedInstanceState != null) {
- markerList = savedInstanceState.getParcelableArrayList(STATE_MARKER_LIST);
- mapboxMap.addMarkers(markerList);
- }
+ if (savedInstanceState != null) {
+ markerList = savedInstanceState.getParcelableArrayList(STATE_MARKER_LIST);
+ mapboxMap.addMarkers(markerList);
}
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimationTypeActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimationTypeActivity.java
index 030785565e..14997b768b 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimationTypeActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimationTypeActivity.java
@@ -47,102 +47,88 @@ public class CameraAnimationTypeActivity extends AppCompatActivity implements On
mapboxMap = map;
mapboxMap.getUiSettings().setAttributionEnabled(false);
mapboxMap.getUiSettings().setLogoEnabled(false);
- mapboxMap.setOnCameraChangeListener(new MapboxMap.OnCameraChangeListener() {
- @Override
- public void onCameraChange(CameraPosition position) {
- Timber.w(position.toString());
- }
- });
+ mapboxMap.setOnCameraChangeListener(position -> Timber.w(position.toString()));
// handle move button clicks
View moveButton = findViewById(R.id.cameraMoveButton);
if (moveButton != null) {
- moveButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- CameraPosition cameraPosition = new CameraPosition.Builder()
- .target(getNextLatLng())
- .zoom(14)
- .tilt(30)
- .tilt(0)
- .build();
- mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
- }
+ moveButton.setOnClickListener(view -> {
+ CameraPosition cameraPosition = new CameraPosition.Builder()
+ .target(getNextLatLng())
+ .zoom(14)
+ .tilt(30)
+ .tilt(0)
+ .build();
+ mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
});
}
// handle ease button clicks
View easeButton = findViewById(R.id.cameraEaseButton);
if (easeButton != null) {
- easeButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- CameraPosition cameraPosition = new CameraPosition.Builder()
- .target(getNextLatLng())
- .zoom(15)
- .bearing(180)
- .tilt(30)
- .build();
-
- MapboxMap.CancelableCallback callback = new MapboxMap.CancelableCallback() {
- @Override
- public void onCancel() {
- Timber.i("Duration onCancel Callback called.");
- Toast.makeText(
- CameraAnimationTypeActivity.this,
- "Ease onCancel Callback called.",
- Toast.LENGTH_LONG).show();
- }
-
- @Override
- public void onFinish() {
- Timber.i("Duration onFinish Callback called.");
- Toast.makeText(
- CameraAnimationTypeActivity.this,
- "Ease onFinish Callback called.",
- Toast.LENGTH_LONG).show();
- }
- };
-
- mapboxMap.easeCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), 7500, callback);
- }
+ easeButton.setOnClickListener(view -> {
+ CameraPosition cameraPosition = new CameraPosition.Builder()
+ .target(getNextLatLng())
+ .zoom(15)
+ .bearing(180)
+ .tilt(30)
+ .build();
+
+ MapboxMap.CancelableCallback callback = new MapboxMap.CancelableCallback() {
+ @Override
+ public void onCancel() {
+ Timber.i("Duration onCancel Callback called.");
+ Toast.makeText(
+ CameraAnimationTypeActivity.this,
+ "Ease onCancel Callback called.",
+ Toast.LENGTH_LONG).show();
+ }
+
+ @Override
+ public void onFinish() {
+ Timber.i("Duration onFinish Callback called.");
+ Toast.makeText(
+ CameraAnimationTypeActivity.this,
+ "Ease onFinish Callback called.",
+ Toast.LENGTH_LONG).show();
+ }
+ };
+
+ mapboxMap.easeCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), 7500, callback);
});
}
// handle animate button clicks
View animateButton = findViewById(R.id.cameraAnimateButton);
if (animateButton != null) {
- animateButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- CameraPosition cameraPosition = new CameraPosition.Builder()
- .target(getNextLatLng())
- .bearing(270)
- .tilt(20)
- .build();
-
- MapboxMap.CancelableCallback callback = new MapboxMap.CancelableCallback() {
- @Override
- public void onCancel() {
- Timber.i("Duration onCancel Callback called.");
- Toast.makeText(
- CameraAnimationTypeActivity.this,
- "Duration onCancel Callback called.",
- Toast.LENGTH_LONG).show();
- }
-
- @Override
- public void onFinish() {
- Timber.i("Duration onFinish Callback called.");
- Toast.makeText(
- CameraAnimationTypeActivity.this,
- "Duration onFinish Callback called.",
- Toast.LENGTH_LONG).show();
- }
- };
-
- mapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), 7500, callback);
- }
+ animateButton.setOnClickListener(view -> {
+ CameraPosition cameraPosition = new CameraPosition.Builder()
+ .target(getNextLatLng())
+ .bearing(270)
+ .tilt(20)
+ .build();
+
+ MapboxMap.CancelableCallback callback = new MapboxMap.CancelableCallback() {
+ @Override
+ public void onCancel() {
+ Timber.i("Duration onCancel Callback called.");
+ Toast.makeText(
+ CameraAnimationTypeActivity.this,
+ "Duration onCancel Callback called.",
+ Toast.LENGTH_LONG).show();
+ }
+
+ @Override
+ public void onFinish() {
+ Timber.i("Duration onFinish Callback called.");
+ Toast.makeText(
+ CameraAnimationTypeActivity.this,
+ "Duration onFinish Callback called.",
+ Toast.LENGTH_LONG).show();
+ }
+ };
+
+ mapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), 7500, callback);
});
}
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimatorActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimatorActivity.java
index c8c5c6bd37..f25fd6ab27 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimatorActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimatorActivity.java
@@ -35,44 +35,30 @@ public class CameraAnimatorActivity extends AppCompatActivity implements OnMapRe
private final LongSparseArray<AnimatorBuilder> animators = new LongSparseArray<AnimatorBuilder>() {
{
- put(R.id.menu_action_accelerate_decelerate_interpolator, new AnimatorBuilder() {
- @Override
- public Animator build() {
- AnimatorSet animatorSet = new AnimatorSet();
- animatorSet.playTogether(
- createLatLngAnimator(START_LAT_LNG, new LatLng(37.826715, -122.422795)),
- obtainExampleInterpolator(new FastOutSlowInInterpolator(), 2500)
- );
- return animatorSet;
- }
+ put(R.id.menu_action_accelerate_decelerate_interpolator, () -> {
+ AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet.playTogether(
+ createLatLngAnimator(START_LAT_LNG, new LatLng(37.826715, -122.422795)),
+ obtainExampleInterpolator(new FastOutSlowInInterpolator(), 2500)
+ );
+ return animatorSet;
});
- put(R.id.menu_action_bounce_interpolator, new AnimatorBuilder() {
- @Override
- public Animator build() {
- AnimatorSet animatorSet = new AnimatorSet();
- animatorSet.playTogether(
- createLatLngAnimator(START_LAT_LNG, new LatLng(37.787947, -122.407432)),
- obtainExampleInterpolator(new BounceInterpolator(), 3750)
- );
- return animatorSet;
- }
+ put(R.id.menu_action_bounce_interpolator, () -> {
+ AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet.playTogether(
+ createLatLngAnimator(START_LAT_LNG, new LatLng(37.787947, -122.407432)),
+ obtainExampleInterpolator(new BounceInterpolator(), 3750)
+ );
+ return animatorSet;
});
- put(R.id.menu_action_anticipate_overshoot_interpolator, new AnimatorBuilder() {
- @Override
- public Animator build() {
- return obtainExampleInterpolator(new AnticipateOvershootInterpolator(), 2500);
- }
- });
+ put(R.id.menu_action_anticipate_overshoot_interpolator, () ->
+ obtainExampleInterpolator(new AnticipateOvershootInterpolator(), 2500)
+ );
- put(R.id.menu_action_path_interpolator, new AnimatorBuilder() {
- @Override
- public Animator build() {
- return obtainExampleInterpolator(
- PathInterpolatorCompat.create(.22f, .68f, 0, 1.71f), 2500);
- }
- });
+ put(R.id.menu_action_path_interpolator, () -> obtainExampleInterpolator(
+ PathInterpolatorCompat.create(.22f, .68f, 0, 1.71f), 2500));
}
};
@@ -98,20 +84,17 @@ public class CameraAnimatorActivity extends AppCompatActivity implements OnMapRe
}
private void initFab() {
- findViewById(R.id.fab).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- view.setVisibility(View.GONE);
-
- CameraPosition animatedPosition = new CameraPosition.Builder()
- .target(new LatLng(37.789992, -122.402214))
- .tilt(60)
- .zoom(14.5f)
- .bearing(135)
- .build();
-
- createExampleAnimator(mapboxMap.getCameraPosition(), animatedPosition).start();
- }
+ findViewById(R.id.fab).setOnClickListener(view -> {
+ view.setVisibility(View.GONE);
+
+ CameraPosition animatedPosition = new CameraPosition.Builder()
+ .target(new LatLng(37.789992, -122.402214))
+ .tilt(60)
+ .zoom(14.5f)
+ .bearing(135)
+ .build();
+
+ createExampleAnimator(mapboxMap.getCameraPosition(), animatedPosition).start();
});
}
@@ -132,12 +115,7 @@ public class CameraAnimatorActivity extends AppCompatActivity implements OnMapRe
ValueAnimator latLngAnimator = ValueAnimator.ofObject(new LatLngEvaluator(), currentPosition, targetPosition);
latLngAnimator.setDuration((long) (1000 * ANIMATION_DELAY_FACTOR));
latLngAnimator.setInterpolator(new FastOutSlowInInterpolator());
- latLngAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- mapboxMap.setLatLng((LatLng) animation.getAnimatedValue());
- }
- });
+ latLngAnimator.addUpdateListener(animation -> mapboxMap.setLatLng((LatLng) animation.getAnimatedValue()));
return latLngAnimator;
}
@@ -146,12 +124,7 @@ public class CameraAnimatorActivity extends AppCompatActivity implements OnMapRe
zoomAnimator.setDuration((long) (2200 * ANIMATION_DELAY_FACTOR));
zoomAnimator.setStartDelay((long) (600 * ANIMATION_DELAY_FACTOR));
zoomAnimator.setInterpolator(new AnticipateOvershootInterpolator());
- zoomAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- mapboxMap.setZoom((Float) animation.getAnimatedValue());
- }
- });
+ zoomAnimator.addUpdateListener(animation -> mapboxMap.setZoom((Float) animation.getAnimatedValue()));
return zoomAnimator;
}
@@ -160,12 +133,7 @@ public class CameraAnimatorActivity extends AppCompatActivity implements OnMapRe
bearingAnimator.setDuration((long) (1000 * ANIMATION_DELAY_FACTOR));
bearingAnimator.setStartDelay((long) (1000 * ANIMATION_DELAY_FACTOR));
bearingAnimator.setInterpolator(new FastOutLinearInInterpolator());
- bearingAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- mapboxMap.setBearing((Float) animation.getAnimatedValue());
- }
- });
+ bearingAnimator.addUpdateListener(animation -> mapboxMap.setBearing((Float) animation.getAnimatedValue()));
return bearingAnimator;
}
@@ -173,12 +141,7 @@ public class CameraAnimatorActivity extends AppCompatActivity implements OnMapRe
ValueAnimator tiltAnimator = ValueAnimator.ofFloat((float) currentTilt, (float) targetTilt);
tiltAnimator.setDuration((long) (1000 * ANIMATION_DELAY_FACTOR));
tiltAnimator.setStartDelay((long) (1500 * ANIMATION_DELAY_FACTOR));
- tiltAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- mapboxMap.setTilt((Float) animation.getAnimatedValue());
- }
- });
+ tiltAnimator.addUpdateListener(animation -> mapboxMap.setTilt((Float) animation.getAnimatedValue()));
return tiltAnimator;
}
@@ -229,12 +192,7 @@ public class CameraAnimatorActivity extends AppCompatActivity implements OnMapRe
ValueAnimator zoomAnimator = ValueAnimator.ofFloat(11.0f, 16.0f);
zoomAnimator.setDuration((long) (duration * ANIMATION_DELAY_FACTOR));
zoomAnimator.setInterpolator(interpolator);
- zoomAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- mapboxMap.setZoom((Float) animation.getAnimatedValue());
- }
- });
+ zoomAnimator.addUpdateListener(animation -> mapboxMap.setZoom((Float) animation.getAnimatedValue()));
return zoomAnimator;
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraPositionActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraPositionActivity.java
index e01c7a10cc..89c8a68abd 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraPositionActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraPositionActivity.java
@@ -160,13 +160,7 @@ public class CameraPositionActivity extends AppCompatActivity implements OnMapRe
}
};
- private MapboxMap.OnCameraMoveCanceledListener moveCanceledListener = new MapboxMap.OnCameraMoveCanceledListener() {
- @Override
- public void onCameraMoveCanceled() {
- Timber.e("OnCameraMoveCanceled");
-
- }
- };
+ private MapboxMap.OnCameraMoveCanceledListener moveCanceledListener = () -> Timber.e("OnCameraMoveCanceled");
private MapboxMap.OnCameraMoveStartedListener moveStartedListener = new MapboxMap.OnCameraMoveStartedListener() {
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/LatLngBoundsActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/LatLngBoundsActivity.java
index a0a8de69a4..ac4c6ff9df 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/LatLngBoundsActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/LatLngBoundsActivity.java
@@ -13,7 +13,6 @@ import com.mapbox.mapboxsdk.geometry.LatLngBounds;
import com.mapbox.mapboxsdk.http.HttpRequestUtil;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
import com.mapbox.mapboxsdk.testapp.view.LockableBottomSheetBehavior;
@@ -57,12 +56,9 @@ public class LatLngBoundsActivity extends AppCompatActivity implements View.OnCl
setContentView(R.layout.activity_latlngbounds);
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(final MapboxMap map) {
- mapboxMap = map;
- initMap();
- }
+ mapView.getMapAsync(map -> {
+ mapboxMap = map;
+ initMap();
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/ManualZoomActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/ManualZoomActivity.java
index b999572436..716d0463a5 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/ManualZoomActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/ManualZoomActivity.java
@@ -2,7 +2,6 @@ package com.mapbox.mapboxsdk.testapp.activity.camera;
import android.graphics.Point;
import android.os.Bundle;
-import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
@@ -12,7 +11,6 @@ import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.constants.Style;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.maps.UiSettings;
import com.mapbox.mapboxsdk.testapp.R;
@@ -35,14 +33,11 @@ public class ManualZoomActivity extends AppCompatActivity {
mapView = (MapView) findViewById(R.id.mapView);
mapView.setStyleUrl(Style.SATELLITE);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull final MapboxMap mapboxMap) {
- ManualZoomActivity.this.mapboxMap = mapboxMap;
-
- UiSettings uiSettings = ManualZoomActivity.this.mapboxMap.getUiSettings();
- uiSettings.setAllGesturesEnabled(false);
- }
+ mapView.getMapAsync(mapboxMap -> {
+ ManualZoomActivity.this.mapboxMap = mapboxMap;
+
+ UiSettings uiSettings = ManualZoomActivity.this.mapboxMap.getUiSettings();
+ uiSettings.setAllGesturesEnabled(false);
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/MaxMinZoomActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/MaxMinZoomActivity.java
index 53a5800f26..277eadb1b5 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/MaxMinZoomActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/MaxMinZoomActivity.java
@@ -1,11 +1,9 @@
package com.mapbox.mapboxsdk.testapp.activity.camera;
import android.os.Bundle;
-import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
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.maps.OnMapReadyCallback;
@@ -37,17 +35,7 @@ public class MaxMinZoomActivity extends AppCompatActivity implements OnMapReadyC
mapboxMap = map;
mapboxMap.setMinZoomPreference(3);
mapboxMap.setMaxZoomPreference(5);
- mapboxMap.setOnMapClickListener(new MapboxMap.OnMapClickListener() {
- @Override
- public void onMapClick(@NonNull LatLng point) {
- map.setStyle(Style.OUTDOORS, new MapboxMap.OnStyleLoadedListener() {
- @Override
- public void onStyleLoaded(String style) {
- Timber.d("Style Loaded %s", style);
- }
- });
- }
- });
+ mapboxMap.setOnMapClickListener(point -> map.setStyle(Style.OUTDOORS, style -> Timber.d("Style Loaded %s", style)));
}
@Override
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/ScrollByActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/ScrollByActivity.java
index 2e889f6e11..4906559935 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/ScrollByActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/ScrollByActivity.java
@@ -9,7 +9,6 @@ import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.MenuItem;
-import android.view.View;
import android.widget.SeekBar;
import android.widget.TextView;
@@ -69,15 +68,10 @@ public class ScrollByActivity extends AppCompatActivity implements OnMapReadyCal
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setColorFilter(ContextCompat.getColor(ScrollByActivity.this, R.color.primary));
- fab.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- mapboxMap.easeCamera(CameraUpdateFactory.scrollBy(
- seekBarX.getProgress() * MULTIPLIER_PER_PIXEL,
- seekBarY.getProgress() * MULTIPLIER_PER_PIXEL)
- );
- }
- });
+ fab.setOnClickListener(view -> mapboxMap.easeCamera(CameraUpdateFactory.scrollBy(
+ seekBarX.getProgress() * MULTIPLIER_PER_PIXEL,
+ seekBarY.getProgress() * MULTIPLIER_PER_PIXEL)
+ ));
}
@Override
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/customlayer/CustomLayerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/customlayer/CustomLayerActivity.java
index 1b49e9e3d6..4cad7593ef 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/customlayer/CustomLayerActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/customlayer/CustomLayerActivity.java
@@ -6,13 +6,11 @@ import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
-import android.view.View;
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.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.style.layers.CustomLayer;
import com.mapbox.mapboxsdk.testapp.R;
import com.mapbox.mapboxsdk.testapp.model.customlayer.ExampleCustomLayer;
@@ -38,23 +36,17 @@ public class CustomLayerActivity extends AppCompatActivity {
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(MapboxMap map) {
- mapboxMap = map;
- mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(39.91448, -243.60947), 10));
+ mapView.getMapAsync(map -> {
+ mapboxMap = map;
+ mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(39.91448, -243.60947), 10));
- }
});
fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setColorFilter(ContextCompat.getColor(this, R.color.primary));
- fab.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (mapboxMap != null) {
- swapCustomLayer();
- }
+ fab.setOnClickListener(view -> {
+ if (mapboxMap != null) {
+ swapCustomLayer();
}
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxCountActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxCountActivity.java
index 20174daeaa..c4ea81263b 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxCountActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxCountActivity.java
@@ -7,11 +7,10 @@ import android.view.View;
import android.widget.Toast;
import com.google.gson.JsonElement;
+import com.mapbox.geojson.Feature;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.services.commons.geojson.Feature;
import java.util.List;
import java.util.Map;
@@ -36,32 +35,25 @@ public class QueryRenderedFeaturesBoxCountActivity extends AppCompatActivity {
// Initialize map as normal
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @SuppressWarnings("ConstantConditions")
- @Override
- public void onMapReady(final MapboxMap mapboxMap) {
- QueryRenderedFeaturesBoxCountActivity.this.mapboxMap = mapboxMap;
- selectionBox.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- // Query
- int top = selectionBox.getTop() - mapView.getTop();
- int left = selectionBox.getLeft() - mapView.getLeft();
- RectF box = new RectF(left, top, left + selectionBox.getWidth(), top + selectionBox.getHeight());
- Timber.i("Querying box %s", box);
- List<Feature> features = mapboxMap.queryRenderedFeatures(box);
-
- // Show count
- Toast.makeText(
- QueryRenderedFeaturesBoxCountActivity.this,
- String.format("%s features in box", features.size()),
- Toast.LENGTH_SHORT).show();
-
- // Debug output
- debugOutput(features);
- }
- });
- }
+ mapView.getMapAsync(mapboxMap -> {
+ QueryRenderedFeaturesBoxCountActivity.this.mapboxMap = mapboxMap;
+ selectionBox.setOnClickListener(view -> {
+ // Query
+ int top = selectionBox.getTop() - mapView.getTop();
+ int left = selectionBox.getLeft() - mapView.getLeft();
+ RectF box = new RectF(left, top, left + selectionBox.getWidth(), top + selectionBox.getHeight());
+ Timber.i("Querying box %s", box);
+ List<Feature> features = mapboxMap.queryRenderedFeatures(box);
+
+ // Show count
+ Toast.makeText(
+ QueryRenderedFeaturesBoxCountActivity.this,
+ String.format("%s features in box", features.size()),
+ Toast.LENGTH_SHORT).show();
+
+ // Debug output
+ debugOutput(features);
+ });
});
}
@@ -70,12 +62,12 @@ public class QueryRenderedFeaturesBoxCountActivity extends AppCompatActivity {
for (Feature feature : features) {
if (feature != null) {
Timber.i("Got feature %s with %s properties and Geometry %s",
- feature.getId(),
- feature.getProperties() != null ? feature.getProperties().entrySet().size() : "<null>",
- feature.getGeometry() != null ? feature.getGeometry().getClass().getSimpleName() : "<null>"
+ feature.id(),
+ feature.properties() != null ? feature.properties().entrySet().size() : "<null>",
+ feature.geometry() != null ? feature.geometry().getClass().getSimpleName() : "<null>"
);
- if (feature.getProperties() != null) {
- for (Map.Entry<String, JsonElement> entry : feature.getProperties().entrySet()) {
+ if (feature.properties() != null) {
+ for (Map.Entry<String, JsonElement> entry : feature.properties().entrySet()) {
Timber.i("Prop %s - %s", entry.getKey(), entry.getValue());
}
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxHighlightActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxHighlightActivity.java
index 480e48437a..df608360ad 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxHighlightActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxHighlightActivity.java
@@ -7,15 +7,15 @@ import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;
+import com.mapbox.geojson.Feature;
+import com.mapbox.geojson.FeatureCollection;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.style.layers.FillLayer;
import com.mapbox.mapboxsdk.style.layers.Filter;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.services.commons.geojson.Feature;
-import com.mapbox.services.commons.geojson.FeatureCollection;
+
import java.util.List;
@@ -41,41 +41,34 @@ public class QueryRenderedFeaturesBoxHighlightActivity extends AppCompatActivity
// Initialize map as normal
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @SuppressWarnings("ConstantConditions")
- @Override
- public void onMapReady(final MapboxMap mapboxMap) {
- QueryRenderedFeaturesBoxHighlightActivity.this.mapboxMap = mapboxMap;
-
- // Add layer / source
- final GeoJsonSource source = new GeoJsonSource("highlighted-shapes-source");
- mapboxMap.addSource(source);
- mapboxMap.addLayer(
- new FillLayer("highlighted-shapes-layer", "highlighted-shapes-source")
- .withProperties(fillColor(Color.RED))
- );
-
- selectionBox.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- // Query
- int top = selectionBox.getTop() - mapView.getTop();
- 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");
-
- // Show count
- Toast.makeText(
- QueryRenderedFeaturesBoxHighlightActivity.this,
- String.format("%s features in box", features.size()),
- Toast.LENGTH_SHORT).show();
-
- // Update source data
- source.setGeoJson(FeatureCollection.fromFeatures(features));
- }
- });
- }
+ mapView.getMapAsync(mapboxMap -> {
+ QueryRenderedFeaturesBoxHighlightActivity.this.mapboxMap = mapboxMap;
+
+ // Add layer / source
+ final GeoJsonSource source = new GeoJsonSource("highlighted-shapes-source");
+ mapboxMap.addSource(source);
+ mapboxMap.addLayer(
+ new FillLayer("highlighted-shapes-layer", "highlighted-shapes-source")
+ .withProperties(fillColor(Color.RED))
+ );
+
+ selectionBox.setOnClickListener(view -> {
+ // Query
+ int top = selectionBox.getTop() - mapView.getTop();
+ 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");
+
+ // Show count
+ Toast.makeText(
+ QueryRenderedFeaturesBoxHighlightActivity.this,
+ String.format("%s features in box", features.size()),
+ Toast.LENGTH_SHORT).show();
+
+ // Update source data
+ source.setGeoJson(FeatureCollection.fromFeatures(features));
+ });
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxSymbolCountActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxSymbolCountActivity.java
index 220137d07d..46409d1893 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxSymbolCountActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxSymbolCountActivity.java
@@ -7,14 +7,13 @@ import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;
+import com.mapbox.geojson.Feature;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.style.layers.SymbolLayer;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
import com.mapbox.mapboxsdk.testapp.R;
import com.mapbox.mapboxsdk.testapp.utils.ResourceUtils;
-import com.mapbox.services.commons.geojson.Feature;
import java.io.IOException;
import java.util.List;
@@ -43,49 +42,42 @@ public class QueryRenderedFeaturesBoxSymbolCountActivity extends AppCompatActivi
// Initialize map as normal
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @SuppressWarnings("ConstantConditions")
- @Override
- public void onMapReady(final MapboxMap mapboxMap) {
- QueryRenderedFeaturesBoxSymbolCountActivity.this.mapboxMap = mapboxMap;
-
- // Add a symbol layer (also works with annotations)
- try {
- mapboxMap.addSource(new GeoJsonSource("symbols-source", ResourceUtils.readRawResource(
- QueryRenderedFeaturesBoxSymbolCountActivity.this, R.raw.test_points_utrecht)));
- } catch (IOException ioException) {
- Timber.e(ioException, "Could not load geojson");
- return;
- }
- mapboxMap.addImage(
- "test-icon",
- BitmapFactory.decodeResource(getResources(),
- R.drawable.mapbox_marker_icon_default)
- );
- mapboxMap.addLayer(new SymbolLayer("symbols-layer", "symbols-source").withProperties(iconImage("test-icon")));
-
- selectionBox.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- // Query
- int top = selectionBox.getTop() - mapView.getTop();
- int left = selectionBox.getLeft() - mapView.getLeft();
- RectF box = new RectF(left, top, left + selectionBox.getWidth(), top + selectionBox.getHeight());
- Timber.i("Querying box %s", box);
- List<Feature> features = mapboxMap.queryRenderedFeatures(box, "symbols-layer");
-
- // Show count
- if (toast != null) {
- toast.cancel();
- }
- toast = Toast.makeText(
- QueryRenderedFeaturesBoxSymbolCountActivity.this,
- String.format("%s features in box", features.size()),
- Toast.LENGTH_SHORT);
- toast.show();
- }
- });
+ mapView.getMapAsync(mapboxMap -> {
+ QueryRenderedFeaturesBoxSymbolCountActivity.this.mapboxMap = mapboxMap;
+
+ // Add a symbol layer (also works with annotations)
+ try {
+ mapboxMap.addSource(new GeoJsonSource("symbols-source", ResourceUtils.readRawResource(
+ QueryRenderedFeaturesBoxSymbolCountActivity.this, R.raw.test_points_utrecht)));
+ } catch (IOException ioException) {
+ Timber.e(ioException, "Could not load geojson");
+ return;
}
+ mapboxMap.addImage(
+ "test-icon",
+ BitmapFactory.decodeResource(getResources(),
+ R.drawable.mapbox_marker_icon_default)
+ );
+ mapboxMap.addLayer(new SymbolLayer("symbols-layer", "symbols-source").withProperties(iconImage("test-icon")));
+
+ selectionBox.setOnClickListener(view -> {
+ // Query
+ int top = selectionBox.getTop() - mapView.getTop();
+ int left = selectionBox.getLeft() - mapView.getLeft();
+ RectF box = new RectF(left, top, left + selectionBox.getWidth(), top + selectionBox.getHeight());
+ Timber.i("Querying box %s", box);
+ List<Feature> features = mapboxMap.queryRenderedFeatures(box, "symbols-layer");
+
+ // Show count
+ if (toast != null) {
+ toast.cancel();
+ }
+ toast = Toast.makeText(
+ QueryRenderedFeaturesBoxSymbolCountActivity.this,
+ String.format("%s features in box", features.size()),
+ Toast.LENGTH_SHORT);
+ toast.show();
+ });
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesPropertiesActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesPropertiesActivity.java
index 1bd6a34b7c..be32718dc3 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesPropertiesActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesPropertiesActivity.java
@@ -12,14 +12,13 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import com.google.gson.JsonElement;
+import com.mapbox.geojson.Feature;
import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions;
import com.mapbox.mapboxsdk.annotations.Marker;
-import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.services.commons.geojson.Feature;
+
import java.util.List;
import java.util.Map;
@@ -45,40 +44,34 @@ public class QueryRenderedFeaturesPropertiesActivity extends AppCompatActivity {
// Initialize map as normal
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(final MapboxMap mapboxMap) {
- QueryRenderedFeaturesPropertiesActivity.this.mapboxMap = mapboxMap;
-
- // Add custom window adapter
- addCustomInfoWindowAdapter(mapboxMap);
-
- // Add a click listener
- mapboxMap.setOnMapClickListener(new MapboxMap.OnMapClickListener() {
- @Override
- public void onMapClick(@NonNull LatLng point) {
- // Query
- final PointF pixel = mapboxMap.getProjection().toScreenLocation(point);
- Timber.i(
- "Requesting features for %sx%s (%sx%s adjusted for density)",
- pixel.x, pixel.y, pixel.x / density, pixel.y / density
- );
- List<Feature> features = mapboxMap.queryRenderedFeatures(pixel);
-
- // Debug output
- debugOutput(features);
-
- // Remove any previous markers
- if (marker != null) {
- mapboxMap.removeMarker(marker);
- }
-
- // Add a marker on the clicked point
- marker = mapboxMap.addMarker(new CustomMarkerOptions().position(point).features(features));
- mapboxMap.selectMarker(marker);
- }
- });
- }
+ mapView.getMapAsync(mapboxMap -> {
+ QueryRenderedFeaturesPropertiesActivity.this.mapboxMap = mapboxMap;
+
+ // Add custom window adapter
+ addCustomInfoWindowAdapter(mapboxMap);
+
+ // Add a click listener
+ mapboxMap.setOnMapClickListener(point -> {
+ // Query
+ final PointF pixel = mapboxMap.getProjection().toScreenLocation(point);
+ Timber.i(
+ "Requesting features for %sx%s (%sx%s adjusted for density)",
+ pixel.x, pixel.y, pixel.x / density, pixel.y / density
+ );
+ List<Feature> features = mapboxMap.queryRenderedFeatures(pixel);
+
+ // Debug output
+ debugOutput(features);
+
+ // Remove any previous markers
+ if (marker != null) {
+ mapboxMap.removeMarker(marker);
+ }
+
+ // Add a marker on the clicked point
+ marker = mapboxMap.addMarker(new CustomMarkerOptions().position(point).features(features));
+ mapboxMap.selectMarker(marker);
+ });
});
}
@@ -88,12 +81,12 @@ public class QueryRenderedFeaturesPropertiesActivity extends AppCompatActivity {
for (Feature feature : features) {
if (feature != null) {
Timber.i("Got feature %s with %s properties and Geometry %s",
- feature.getId(),
- feature.getProperties() != null ? feature.getProperties().entrySet().size() : "<null>",
- feature.getGeometry() != null ? feature.getGeometry().getClass().getSimpleName() : "<null>"
+ feature.id(),
+ feature.properties() != null ? feature.properties().entrySet().size() : "<null>",
+ feature.geometry() != null ? feature.geometry().getClass().getSimpleName() : "<null>"
);
- if (feature.getProperties() != null) {
- for (Map.Entry<String, JsonElement> entry : feature.getProperties().entrySet()) {
+ if (feature.properties() != null) {
+ for (Map.Entry<String, JsonElement> entry : feature.properties().entrySet()) {
Timber.i("Prop %s - %s", entry.getKey(), entry.getValue());
}
}
@@ -122,7 +115,7 @@ public class QueryRenderedFeaturesPropertiesActivity extends AppCompatActivity {
if (customMarker.features.size() > 0) {
view.addView(row(String.format("Found %s features", customMarker.features.size())));
Feature feature = customMarker.features.get(0);
- for (Map.Entry<String, JsonElement> prop : feature.getProperties().entrySet()) {
+ for (Map.Entry<String, JsonElement> prop : feature.properties().entrySet()) {
view.addView(row(String.format("%s: %s", prop.getKey(), prop.getValue())));
}
} else {
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QuerySourceFeaturesActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QuerySourceFeaturesActivity.java
index 861f2fef34..14de81ab30 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QuerySourceFeaturesActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QuerySourceFeaturesActivity.java
@@ -1,22 +1,20 @@
package com.mapbox.mapboxsdk.testapp.activity.feature;
import android.os.Bundle;
-import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;
import com.google.gson.JsonObject;
-import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.geojson.Feature;
+import com.mapbox.geojson.FeatureCollection;
+import com.mapbox.geojson.Point;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.style.layers.CircleLayer;
import com.mapbox.mapboxsdk.style.layers.Filter;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.services.commons.geojson.Feature;
-import com.mapbox.services.commons.geojson.FeatureCollection;
-import com.mapbox.services.commons.geojson.Point;
+
import java.util.List;
@@ -38,32 +36,26 @@ public class QuerySourceFeaturesActivity extends AppCompatActivity {
// Initialize map as normal
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(final MapboxMap mapboxMap) {
- QuerySourceFeaturesActivity.this.mapboxMap = mapboxMap;
-
- JsonObject properties = new JsonObject();
- properties.addProperty("key1", "value1");
- final GeoJsonSource source = new GeoJsonSource("test-source",
- FeatureCollection.fromFeatures(new Feature[] {
- Feature.fromGeometry(Point.fromCoordinates(new double[] {0, 0}), properties)
- }));
- mapboxMap.addSource(source);
-
- mapboxMap.addLayer(new CircleLayer("test-layer", source.getId()).withFilter(Filter.neq("key1", "value1")));
-
- // Add a click listener
- mapboxMap.setOnMapClickListener(new MapboxMap.OnMapClickListener() {
- @Override
- public void onMapClick(@NonNull LatLng point) {
- // Query
- List<Feature> features = source.querySourceFeatures(Filter.eq("key1", "value1"));
- Toast.makeText(QuerySourceFeaturesActivity.this, String.format("Found %s features",
- features.size()), Toast.LENGTH_SHORT).show();
- }
- });
- }
+ mapView.getMapAsync(mapboxMap -> {
+ QuerySourceFeaturesActivity.this.mapboxMap = mapboxMap;
+
+ JsonObject properties = new JsonObject();
+ properties.addProperty("key1", "value1");
+ final GeoJsonSource source = new GeoJsonSource("test-source",
+ FeatureCollection.fromFeatures(new Feature[] {
+ Feature.fromGeometry(Point.fromLngLat(0, 0), properties)
+ }));
+ mapboxMap.addSource(source);
+
+ mapboxMap.addLayer(new CircleLayer("test-layer", source.getId()).withFilter(Filter.neq("key1", "value1")));
+
+ // Add a click listener
+ mapboxMap.setOnMapClickListener(point -> {
+ // Query
+ List<Feature> features = source.querySourceFeatures(Filter.eq("key1", "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/fragment/MultiMapActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/MultiMapActivity.java
index eec440afb0..2305fdf16c 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/MultiMapActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/MultiMapActivity.java
@@ -1,7 +1,7 @@
package com.mapbox.mapboxsdk.testapp.activity.fragment;
-import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
import com.mapbox.mapboxsdk.testapp.R;
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/ViewPagerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/ViewPagerActivity.java
index bbdf450505..26cf777ba9 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/ViewPagerActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/ViewPagerActivity.java
@@ -48,6 +48,7 @@ public class ViewPagerActivity extends AppCompatActivity {
public Fragment getItem(int position) {
SupportMapFragment fragment = null;
MapboxMapOptions options = new MapboxMapOptions();
+ options.textureMode(true);
switch (position) {
case 0:
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/imagegenerator/PrintActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/imagegenerator/PrintActivity.java
index 18d80586c9..d0d4368d36 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/imagegenerator/PrintActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/imagegenerator/PrintActivity.java
@@ -2,14 +2,12 @@ package com.mapbox.mapboxsdk.testapp.activity.imagegenerator;
import android.graphics.Bitmap;
import android.os.Bundle;
-import android.support.annotation.NonNull;
import android.support.v4.print.PrintHelper;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
/**
@@ -27,21 +25,13 @@ public class PrintActivity extends AppCompatActivity implements MapboxMap.Snapsh
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull MapboxMap mapboxMap) {
- PrintActivity.this.mapboxMap = mapboxMap;
- }
- });
+ mapView.getMapAsync(mapboxMap -> PrintActivity.this.mapboxMap = mapboxMap);
final View fab = findViewById(R.id.fab);
if (fab != null) {
- fab.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (mapboxMap != null) {
- mapboxMap.snapshot(PrintActivity.this);
- }
+ fab.setOnClickListener(view -> {
+ if (mapboxMap != null) {
+ mapboxMap.snapshot(PrintActivity.this);
}
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/imagegenerator/SnapshotActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/imagegenerator/SnapshotActivity.java
index 655012f799..62d3ad34df 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/imagegenerator/SnapshotActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/imagegenerator/SnapshotActivity.java
@@ -1,6 +1,5 @@
package com.mapbox.mapboxsdk.testapp.activity.imagegenerator;
-import android.graphics.Bitmap;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.content.ContextCompat;
@@ -49,18 +48,15 @@ public class SnapshotActivity extends AppCompatActivity implements OnMapReadyCal
@Override
public void onClick(View view) {
final long startTime = System.nanoTime();
- mapboxMap.snapshot(new MapboxMap.SnapshotReadyCallback() {
- @Override
- public void onSnapshotReady(Bitmap snapshot) {
- long endTime = System.nanoTime();
- long duration = (long) ((endTime - startTime) / 1e6);
- ImageView snapshotView = (ImageView) findViewById(R.id.imageView);
- snapshotView.setImageBitmap(snapshot);
- Toast.makeText(
- SnapshotActivity.this,
- String.format(Locale.getDefault(), "Snapshot taken in %d ms", duration),
- Toast.LENGTH_LONG).show();
- }
+ mapboxMap.snapshot(snapshot -> {
+ long endTime = System.nanoTime();
+ long duration = (long) ((endTime - startTime) / 1e6);
+ ImageView snapshotView = (ImageView) findViewById(R.id.imageView);
+ snapshotView.setImageBitmap(snapshot);
+ Toast.makeText(
+ SnapshotActivity.this,
+ String.format(Locale.getDefault(), "Snapshot taken in %d ms", duration),
+ Toast.LENGTH_LONG).show();
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/DynamicInfoWindowAdapterActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/DynamicInfoWindowAdapterActivity.java
index 7fa4792a38..9dea4f77de 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/DynamicInfoWindowAdapterActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/DynamicInfoWindowAdapterActivity.java
@@ -2,14 +2,11 @@ package com.mapbox.mapboxsdk.testapp.activity.infowindow;
import android.graphics.Color;
import android.os.Bundle;
-import android.support.annotation.NonNull;
import android.support.v4.content.res.ResourcesCompat;
import android.support.v7.app.AppCompatActivity;
-import android.view.View;
import android.widget.TextView;
import com.mapbox.mapboxsdk.annotations.InfoWindow;
-import com.mapbox.mapboxsdk.annotations.Marker;
import com.mapbox.mapboxsdk.annotations.MarkerView;
import com.mapbox.mapboxsdk.annotations.MarkerViewOptions;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
@@ -57,28 +54,22 @@ public class DynamicInfoWindowAdapterActivity extends AppCompatActivity implemen
mapboxMap.selectMarker(marker);
// On map click, change the info window contents
- mapboxMap.setOnMapClickListener(new MapboxMap.OnMapClickListener() {
- @Override
- public void onMapClick(@NonNull LatLng point) {
- // Distance from click to marker
- double distanceKm = marker.getPosition().distanceTo(point) / 1000;
-
- // Get the info window
- final InfoWindow infoWindow = marker.getInfoWindow();
-
- // Get the view from the info window
- if (infoWindow != null && infoWindow.getView() != null) {
- // Set the new text on the text view in the info window
- TextView textView = (TextView) infoWindow.getView();
- textView.setText(String.format(Locale.getDefault(), "%.2fkm", distanceKm));
- textView.post(new Runnable() {
- @Override
- public void run() {
- // Update the info window position (as the text length changes)
- infoWindow.update();
- }
- });
- }
+ mapboxMap.setOnMapClickListener(point -> {
+ // Distance from click to marker
+ double distanceKm = marker.getPosition().distanceTo(point) / 1000;
+
+ // Get the info window
+ final InfoWindow infoWindow = marker.getInfoWindow();
+
+ // Get the view from the info window
+ if (infoWindow != null && infoWindow.getView() != null) {
+ // Set the new text on the text view in the info window
+ TextView textView = (TextView) infoWindow.getView();
+ textView.setText(String.format(Locale.getDefault(), "%.2fkm", distanceKm));
+ textView.post(() -> {
+ // Update the info window position (as the text length changes)
+ infoWindow.update();
+ });
}
});
@@ -97,17 +88,13 @@ public class DynamicInfoWindowAdapterActivity extends AppCompatActivity implemen
private void addCustomInfoWindowAdapter(final MapboxMap mapboxMap) {
final int padding = (int) getResources().getDimension(R.dimen.attr_margin);
- mapboxMap.setInfoWindowAdapter(new MapboxMap.InfoWindowAdapter() {
-
- @Override
- public View getInfoWindow(@NonNull Marker marker) {
- TextView textView = new TextView(DynamicInfoWindowAdapterActivity.this);
- textView.setText(marker.getTitle());
- textView.setBackgroundColor(Color.WHITE);
- textView.setText(R.string.action_calculate_distance);
- textView.setPadding(padding, padding, padding, padding);
- return textView;
- }
+ mapboxMap.setInfoWindowAdapter(marker -> {
+ TextView textView = new TextView(DynamicInfoWindowAdapterActivity.this);
+ textView.setText(marker.getTitle());
+ textView.setBackgroundColor(Color.WHITE);
+ textView.setText(R.string.action_calculate_distance);
+ textView.setPadding(padding, padding, padding, padding);
+ return textView;
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/InfoWindowAdapterActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/InfoWindowAdapterActivity.java
index 22347f8a92..d8dea0e3b5 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/InfoWindowAdapterActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/InfoWindowAdapterActivity.java
@@ -12,7 +12,6 @@ import com.mapbox.mapboxsdk.annotations.Marker;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
import com.mapbox.mapboxsdk.testapp.model.annotations.CityStateMarker;
import com.mapbox.mapboxsdk.testapp.model.annotations.CityStateMarkerOptions;
@@ -33,13 +32,10 @@ public class InfoWindowAdapterActivity extends AppCompatActivity {
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull MapboxMap map) {
- mapboxMap = map;
- addMarkers();
- addCustomInfoWindowAdapter();
- }
+ mapView.getMapAsync(map -> {
+ mapboxMap = map;
+ addMarkers();
+ addCustomInfoWindowAdapter();
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/BottomSheetActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/BottomSheetActivity.java
index 2c83f6d908..3b58843e13 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/BottomSheetActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/BottomSheetActivity.java
@@ -7,6 +7,7 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.BottomSheetBehavior;
import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
@@ -35,7 +36,6 @@ public class BottomSheetActivity extends AppCompatActivity {
private static final String TAG_BOTTOM_FRAGMENT = "com.mapbox.mapboxsdk.fragment.tag.bottom";
private boolean bottomSheetFragmentAdded;
- private int mapViewCounter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -45,22 +45,11 @@ public class BottomSheetActivity extends AppCompatActivity {
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setDisplayHomeAsUpEnabled(true);
}
- findViewById(R.id.fabFragment).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- addMapFragment();
- }
- });
+ findViewById(R.id.fabFragment).setOnClickListener(v -> addMapFragment());
- findViewById(R.id.fabBottomSheet).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- toggleBottomSheetMapFragment();
- }
- });
+ findViewById(R.id.fabBottomSheet).setOnClickListener(v -> toggleBottomSheetMapFragment());
BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(findViewById(R.id.bottom_sheet));
bottomSheetBehavior.setPeekHeight((int) (64 * getResources().getDisplayMetrics().density));
@@ -78,25 +67,29 @@ public class BottomSheetActivity extends AppCompatActivity {
@Override
public void onBackPressed() {
- super.onBackPressed();
- if (mapViewCounter > 0) {
- mapViewCounter--;
- Toast.makeText(this, "Amount of main map fragments: " + mapViewCounter, Toast.LENGTH_SHORT).show();
+ FragmentManager fragmentManager = getSupportFragmentManager();
+
+ if (fragmentManager.getBackStackEntryCount() > 0) {
+ fragmentManager.popBackStack();
+ } else {
+ super.onBackPressed();
}
}
private void addMapFragment() {
- FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
- MainMapFragment mainMapFragment = MainMapFragment.newInstance(mapViewCounter);
- if (mapViewCounter == 0) {
+ FragmentManager fragmentManager = getSupportFragmentManager();
+ int fragmentCount = fragmentManager.getBackStackEntryCount();
+
+ FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
+ MainMapFragment mainMapFragment = MainMapFragment.newInstance(fragmentCount);
+ if (fragmentCount == 0) {
fragmentTransaction.add(R.id.fragment_container, mainMapFragment, TAG_MAIN_FRAGMENT);
} else {
fragmentTransaction.replace(R.id.fragment_container, mainMapFragment, TAG_MAIN_FRAGMENT);
}
- fragmentTransaction.addToBackStack(null);
+ fragmentTransaction.addToBackStack(String.valueOf(mainMapFragment.hashCode()));
fragmentTransaction.commit();
- mapViewCounter++;
- Toast.makeText(this, "Amount of main map fragments: " + mapViewCounter, Toast.LENGTH_SHORT).show();
+ Toast.makeText(this, "Amount of main map fragments: " + (fragmentCount + 1), Toast.LENGTH_SHORT).show();
}
private void toggleBottomSheetMapFragment() {
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DebugModeActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DebugModeActivity.java
index f597f67555..44d27a41ab 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DebugModeActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DebugModeActivity.java
@@ -11,12 +11,10 @@ import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;
-import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.constants.Style;
import com.mapbox.mapboxsdk.http.HttpRequestUtil;
import com.mapbox.mapboxsdk.maps.MapView;
@@ -84,13 +82,10 @@ public class DebugModeActivity extends AppCompatActivity implements OnMapReadyCa
private void setupMapView(Bundle savedInstanceState) {
mapView = (MapView) findViewById(R.id.mapView);
- mapView.addOnMapChangedListener(new MapView.OnMapChangedListener() {
- @Override
- public void onMapChanged(int change) {
- if (change == MapView.DID_FINISH_LOADING_STYLE && mapboxMap != null) {
- Timber.v("New style loaded with JSON: %s", mapboxMap.getStyleJson());
- setupNavigationView(mapboxMap.getLayers());
- }
+ mapView.addOnMapChangedListener(change -> {
+ if (change == MapView.DID_FINISH_LOADING_STYLE && mapboxMap != null) {
+ Timber.v("New style loaded with JSON: %s", mapboxMap.getStyleJson());
+ setupNavigationView(mapboxMap.getLayers());
}
});
@@ -112,25 +107,19 @@ public class DebugModeActivity extends AppCompatActivity implements OnMapReadyCa
private void setFpsView() {
final TextView fpsView = (TextView) findViewById(R.id.fpsView);
- mapboxMap.setOnFpsChangedListener(new MapboxMap.OnFpsChangedListener() {
- @Override
- public void onFpsChanged(double fps) {
- fpsView.setText(String.format(Locale.US, "FPS: %4.2f", fps));
- }
- });
+ mapboxMap.setOnFpsChangedListener(fps ->
+ fpsView.setText(String.format(Locale.US, "FPS: %4.2f", fps))
+ );
}
private void setupNavigationView(List<Layer> layerList) {
final LayerListAdapter adapter = new LayerListAdapter(this, layerList);
ListView listView = (ListView) findViewById(R.id.listView);
listView.setAdapter(adapter);
- listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- Layer clickedLayer = adapter.getItem(position);
- toggleLayerVisibility(clickedLayer);
- closeNavigationView();
- }
+ listView.setOnItemClickListener((parent, view, position, id) -> {
+ Layer clickedLayer = adapter.getItem(position);
+ toggleLayerVisibility(clickedLayer);
+ closeNavigationView();
});
}
@@ -150,44 +139,30 @@ public class DebugModeActivity extends AppCompatActivity implements OnMapReadyCa
private void setupZoomView() {
final TextView textView = (TextView) findViewById(R.id.textZoom);
- mapboxMap.setOnCameraChangeListener(new MapboxMap.OnCameraChangeListener() {
- @Override
- public void onCameraChange(CameraPosition position) {
- textView.setText(String.format(getString(R.string.debug_zoom), position.zoom));
- }
- });
+ mapboxMap.setOnCameraChangeListener(position ->
+ textView.setText(String.format(getString(R.string.debug_zoom), position.zoom))
+ );
}
private void setupDebugChangeView() {
FloatingActionButton fabDebug = (FloatingActionButton) findViewById(R.id.fabDebug);
- fabDebug.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (mapboxMap != null) {
- Timber.d("Debug FAB: isDebug Active? %s", mapboxMap.isDebugActive());
- mapboxMap.cycleDebugOptions();
- }
+ fabDebug.setOnClickListener(view -> {
+ if (mapboxMap != null) {
+ Timber.d("Debug FAB: isDebug Active? %s", mapboxMap.isDebugActive());
+ mapboxMap.cycleDebugOptions();
}
});
}
private void setupStyleChangeView() {
FloatingActionButton fabStyles = (FloatingActionButton) findViewById(R.id.fabStyles);
- fabStyles.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (mapboxMap != null) {
- currentStyleIndex++;
- if (currentStyleIndex == STYLES.length) {
- currentStyleIndex = 0;
- }
- mapboxMap.setStyleUrl(STYLES[currentStyleIndex], new MapboxMap.OnStyleLoadedListener() {
- @Override
- public void onStyleLoaded(String style) {
- Timber.d("Style loaded %s", style);
- }
- });
+ fabStyles.setOnClickListener(view -> {
+ if (mapboxMap != null) {
+ currentStyleIndex++;
+ if (currentStyleIndex == STYLES.length) {
+ currentStyleIndex = 0;
}
+ mapboxMap.setStyleUrl(STYLES[currentStyleIndex], style -> Timber.d("Style loaded %s", style));
}
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DoubleMapActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DoubleMapActivity.java
index 462c0c8025..75b2378ef7 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DoubleMapActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DoubleMapActivity.java
@@ -3,7 +3,6 @@ package com.mapbox.mapboxsdk.testapp.activity.maplayout;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
-import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
@@ -14,10 +13,8 @@ import android.view.ViewGroup;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.constants.MyLocationTracking;
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.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.maps.TrackingSettings;
import com.mapbox.mapboxsdk.maps.UiSettings;
import com.mapbox.mapboxsdk.testapp.R;
@@ -90,47 +87,38 @@ public class DoubleMapActivity extends AppCompatActivity {
// MapView large
mapView = (MapView) view.findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull MapboxMap mapboxMap) {
- if (activity != null) {
- activity.setMapboxMap(mapboxMap);
- }
+ mapView.getMapAsync(mapboxMap -> {
+ if (activity != null) {
+ activity.setMapboxMap(mapboxMap);
}
});
// MapView mini
mapViewMini = (MapView) view.findViewById(R.id.mini_map);
mapViewMini.onCreate(savedInstanceState);
- mapViewMini.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull MapboxMap mapboxMap) {
- mapboxMap.setStyleUrl(Style.LIGHT);
- mapboxMap.moveCamera(CameraUpdateFactory.zoomTo(4));
-
- UiSettings uiSettings = mapboxMap.getUiSettings();
- uiSettings.setAllGesturesEnabled(false);
- uiSettings.setCompassEnabled(false);
- uiSettings.setAttributionEnabled(false);
- uiSettings.setLogoEnabled(false);
-
- try {
- mapboxMap.setMyLocationEnabled(true);
- TrackingSettings settings = mapboxMap.getTrackingSettings();
- settings.setMyLocationTrackingMode(MyLocationTracking.TRACKING_FOLLOW);
- } catch (SecurityException securityException) {
- // permission is handled in MainActivity
- getActivity().finish();
- }
-
- mapboxMap.setOnMapClickListener(new MapboxMap.OnMapClickListener() {
- @Override
- public void onMapClick(@NonNull LatLng point) {
- // test if we can open 2 activities after each other
- startActivity(new Intent(mapViewMini.getContext(), DoubleMapActivity.class));
- }
- });
+ mapViewMini.getMapAsync(mapboxMap -> {
+ mapboxMap.setStyleUrl(Style.LIGHT);
+ mapboxMap.moveCamera(CameraUpdateFactory.zoomTo(4));
+
+ UiSettings uiSettings = mapboxMap.getUiSettings();
+ uiSettings.setAllGesturesEnabled(false);
+ uiSettings.setCompassEnabled(false);
+ uiSettings.setAttributionEnabled(false);
+ uiSettings.setLogoEnabled(false);
+
+ try {
+ mapboxMap.setMyLocationEnabled(true);
+ TrackingSettings settings = mapboxMap.getTrackingSettings();
+ settings.setMyLocationTrackingMode(MyLocationTracking.TRACKING_FOLLOW);
+ } catch (SecurityException securityException) {
+ // permission is handled in MainActivity
+ getActivity().finish();
}
+
+ mapboxMap.setOnMapClickListener(point -> {
+ // test if we can open 2 activities after each other
+ startActivity(new Intent(mapViewMini.getContext(), DoubleMapActivity.class));
+ });
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/LocalGlyphActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/LocalGlyphActivity.java
index bc07765bf7..18d675a289 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/LocalGlyphActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/LocalGlyphActivity.java
@@ -12,6 +12,10 @@ import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
+/**
+ * Test activity that displays the city of Suzhou with a mixture of server-generated
+ * latin glyphs and CJK glyphs generated locally using "Droid Sans" as a font family.
+ */
public class LocalGlyphActivity extends AppCompatActivity {
private MapView mapView;
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapChangeActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapChangeActivity.java
index 32344248bc..160e69ed6e 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapChangeActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapChangeActivity.java
@@ -8,7 +8,6 @@ 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.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
import timber.log.Timber;
@@ -28,21 +27,13 @@ public class MapChangeActivity extends AppCompatActivity {
final LongSparseArray<String> mapChangeMap = buildMapChangeStringValueSparseArray();
mapView = (MapView) findViewById(R.id.mapView);
- mapView.addOnMapChangedListener(new MapView.OnMapChangedListener() {
- @Override
- public void onMapChanged(int change) {
- Timber.e("OnMapChange: %s, %s", change, mapChangeMap.get(change));
- }
- });
+ mapView.addOnMapChangedListener(change -> Timber.e("OnMapChange: %s, %s", change, mapChangeMap.get(change)));
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(MapboxMap map) {
- mapboxMap = map;
- mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(
- new LatLng(55.754020, 37.620948), 12), 9000);
- }
+ mapView.getMapAsync(map -> {
+ mapboxMap = map;
+ mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(
+ new LatLng(55.754020, 37.620948), 12), 9000);
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapInDialogActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapInDialogActivity.java
index f0827c65cc..ce00c9d18f 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapInDialogActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapInDialogActivity.java
@@ -29,13 +29,10 @@ public class MapInDialogActivity extends AppCompatActivity {
setContentView(R.layout.activity_map_in_dialog);
Button button = (Button) findViewById(R.id.button_open_dialog);
- button.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- FragmentManager fm = getSupportFragmentManager();
- MapDialogFragment editNameDialogFragment = MapDialogFragment.newInstance("Map Dialog");
- editNameDialogFragment.show(fm, "fragment_dialog_map");
- }
+ button.setOnClickListener(view -> {
+ FragmentManager fm = getSupportFragmentManager();
+ MapDialogFragment editNameDialogFragment = MapDialogFragment.newInstance("Map Dialog");
+ editNameDialogFragment.show(fm, "fragment_dialog_map");
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapPaddingActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapPaddingActivity.java
index 0cf0191cea..9a6079b157 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapPaddingActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapPaddingActivity.java
@@ -1,7 +1,6 @@
package com.mapbox.mapboxsdk.testapp.activity.maplayout;
import android.os.Bundle;
-import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
@@ -13,7 +12,6 @@ import com.mapbox.mapboxsdk.constants.MyLocationTracking;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.maps.TrackingSettings;
import com.mapbox.mapboxsdk.testapp.R;
@@ -37,19 +35,16 @@ public class MapPaddingActivity extends AppCompatActivity {
mapView.setTag(true);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull MapboxMap mapboxMap) {
- MapPaddingActivity.this.mapboxMap = mapboxMap;
+ mapView.getMapAsync(mapboxMap -> {
+ MapPaddingActivity.this.mapboxMap = mapboxMap;
- int paddingLeft = (int) getResources().getDimension(R.dimen.map_padding_left);
- int paddingBottom = (int) getResources().getDimension(R.dimen.map_padding_bottom);
- int paddingRight = (int) getResources().getDimension(R.dimen.map_padding_right);
- int paddingTop = (int) getResources().getDimension(R.dimen.map_padding_top);
- mapboxMap.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
+ int paddingLeft = (int) getResources().getDimension(R.dimen.map_padding_left);
+ int paddingBottom = (int) getResources().getDimension(R.dimen.map_padding_bottom);
+ int paddingRight = (int) getResources().getDimension(R.dimen.map_padding_right);
+ int paddingTop = (int) getResources().getDimension(R.dimen.map_padding_top);
+ mapboxMap.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
- moveToBangalore();
- }
+ moveToBangalore();
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/VisibilityChangeActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/VisibilityChangeActivity.java
index 393abb7cd4..c5d7dfbef7 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/VisibilityChangeActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/VisibilityChangeActivity.java
@@ -9,7 +9,6 @@ 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.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
/**
@@ -29,13 +28,10 @@ public class VisibilityChangeActivity extends AppCompatActivity {
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(MapboxMap map) {
- mapboxMap = map;
- mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(
- new LatLng(55.754020, 37.620948), 12), 9000);
- }
+ mapView.getMapAsync(map -> {
+ mapboxMap = map;
+ mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(
+ new LatLng(55.754020, 37.620948), 12), 9000);
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/DeleteRegionActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/DeleteRegionActivity.java
index 220f46f4e8..037c51f723 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/DeleteRegionActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/DeleteRegionActivity.java
@@ -1,7 +1,6 @@
package com.mapbox.mapboxsdk.testapp.activity.offline;
import android.content.Context;
-import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
@@ -53,18 +52,8 @@ public class DeleteRegionActivity extends AppCompatActivity implements AdapterVi
input.setText(metadata);
builder.setView(input);
- builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- delete(region);
- }
- });
- builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.cancel();
- }
- });
+ builder.setPositiveButton("OK", (dialog, which) -> delete(region));
+ builder.setNegativeButton("Cancel", (dialog, which) -> dialog.cancel());
builder.show();
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/OfflineActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/OfflineActivity.java
index 3a59e0628d..79e76168d5 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/OfflineActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/OfflineActivity.java
@@ -1,7 +1,6 @@
package com.mapbox.mapboxsdk.testapp.activity.offline;
import android.os.Bundle;
-import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.View;
@@ -17,7 +16,6 @@ import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.offline.OfflineManager;
import com.mapbox.mapboxsdk.offline.OfflineRegion;
import com.mapbox.mapboxsdk.offline.OfflineRegionError;
@@ -80,21 +78,18 @@ public class OfflineActivity extends AppCompatActivity
mapView = (MapView) findViewById(R.id.mapView);
mapView.setStyleUrl(STYLE_URL);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull MapboxMap mapboxMap) {
- Timber.d("Map is ready");
- OfflineActivity.this.mapboxMap = mapboxMap;
-
- // Set initial position to UNHQ in NYC
- mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(
- new CameraPosition.Builder()
- .target(new LatLng(40.749851, -73.967966))
- .zoom(14)
- .bearing(0)
- .tilt(0)
- .build()));
- }
+ mapView.getMapAsync(mapboxMap -> {
+ Timber.d("Map is ready");
+ OfflineActivity.this.mapboxMap = mapboxMap;
+
+ // Set initial position to UNHQ in NYC
+ mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(
+ new CameraPosition.Builder()
+ .target(new LatLng(40.749851, -73.967966))
+ .zoom(14)
+ .bearing(0)
+ .tilt(0)
+ .build()));
});
// The progress bar
@@ -102,20 +97,10 @@ public class OfflineActivity extends AppCompatActivity
// Set up button listeners
downloadRegion = (Button) findViewById(R.id.button_download_region);
- downloadRegion.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- handleDownloadRegion();
- }
- });
+ downloadRegion.setOnClickListener(view -> handleDownloadRegion());
listRegions = (Button) findViewById(R.id.button_list_regions);
- listRegions.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- handleListRegions();
- }
- });
+ listRegions.setOnClickListener(view -> handleListRegions());
// Set up the OfflineManager
offlineManager = OfflineManager.getInstance(this);
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/UpdateMetadataActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/UpdateMetadataActivity.java
index 285e896766..c5ad467673 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/UpdateMetadataActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/UpdateMetadataActivity.java
@@ -1,7 +1,6 @@
package com.mapbox.mapboxsdk.testapp.activity.offline;
import android.content.Context;
-import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
@@ -57,18 +56,10 @@ public class UpdateMetadataActivity extends AppCompatActivity implements Adapter
input.setSelection(metadata.length());
builder.setView(input);
- builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- updateMetadata(region, OfflineUtils.convertRegionName(input.getText().toString()));
- }
- });
- builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.cancel();
- }
- });
+ builder.setPositiveButton("OK", (dialog, which) ->
+ updateMetadata(region, OfflineUtils.convertRegionName(input.getText().toString()))
+ );
+ builder.setNegativeButton("Cancel", (dialog, which) -> dialog.cancel());
builder.show();
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterActivity.java
index c4fe93d200..aadf021a89 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterActivity.java
@@ -10,7 +10,6 @@ import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.constants.Style;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
-import com.mapbox.mapboxsdk.snapshotter.MapSnapshot;
import com.mapbox.mapboxsdk.snapshotter.MapSnapshotter;
import com.mapbox.mapboxsdk.testapp.R;
@@ -95,17 +94,14 @@ public class MapSnapshotterActivity extends AppCompatActivity {
MapSnapshotter snapshotter = new MapSnapshotter(MapSnapshotterActivity.this, options);
- snapshotter.start(new MapSnapshotter.SnapshotReadyCallback() {
- @Override
- public void onSnapshotReady(MapSnapshot snapshot) {
- Timber.i("Got the snapshot");
- ImageView imageView = new ImageView(MapSnapshotterActivity.this);
- imageView.setImageBitmap(snapshot.getBitmap());
- grid.addView(
- imageView,
- new GridLayout.LayoutParams(GridLayout.spec(row), GridLayout.spec(column))
- );
- }
+ snapshotter.start(snapshot -> {
+ Timber.i("Got the snapshot");
+ ImageView imageView = new ImageView(MapSnapshotterActivity.this);
+ imageView.setImageBitmap(snapshot.getBitmap());
+ grid.addView(
+ imageView,
+ new GridLayout.LayoutParams(GridLayout.spec(row), GridLayout.spec(column))
+ );
});
snapshotters.add(snapshotter);
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterMarkerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterMarkerActivity.java
index b690f18b6a..11d1df008a 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterMarkerActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterMarkerActivity.java
@@ -9,12 +9,14 @@ import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.ImageView;
+
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.constants.Style;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.snapshotter.MapSnapshot;
import com.mapbox.mapboxsdk.snapshotter.MapSnapshotter;
import com.mapbox.mapboxsdk.testapp.R;
+
import timber.log.Timber;
/**
@@ -72,9 +74,10 @@ public class MapSnapshotterMarkerActivity extends AppCompatActivity implements M
// Dom toren
PointF markerLocation = snapshot.pixelForLatLng(new LatLng(52.090649433011315, 5.121310651302338));
canvas.drawBitmap(marker,
- markerLocation.x,
- /* Subtract height (in dp) so the bottom of the marker aligns correctly */
- markerLocation.y - (marker.getHeight() / getResources().getDisplayMetrics().density),
+ /* Subtract half of the width so we center the bitmap correctly */
+ markerLocation.x - marker.getWidth() / 2,
+ /* Subtract half of the height so we align the bitmap bottom correctly */
+ markerLocation.y - marker.getHeight() / 2,
null
);
return snapshot.getBitmap();
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterReuseActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterReuseActivity.java
index 50f9be77fa..ef5913beb0 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterReuseActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterReuseActivity.java
@@ -31,24 +31,21 @@ public class MapSnapshotterReuseActivity extends AppCompatActivity implements Ma
fab = findViewById(R.id.fab);
fab.setVisibility(View.INVISIBLE);
- fab.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- fab.setVisibility(View.INVISIBLE);
-
- mapSnapshotter.setStyleUrl(getRandomStyle());
- if (random.nextInt(2) == 0) {
- mapSnapshotter.setCameraPosition(getRandomCameraPosition());
- } else {
- mapSnapshotter.setRegion(getRandomBounds());
- }
- if (random.nextInt(2) == 0) {
- mapSnapshotter.setSize(512, 512);
- } else {
- mapSnapshotter.setSize(256, 256);
- }
- mapSnapshotter.start(MapSnapshotterReuseActivity.this);
+ fab.setOnClickListener(v -> {
+ fab.setVisibility(View.INVISIBLE);
+
+ mapSnapshotter.setStyleUrl(getRandomStyle());
+ if (random.nextInt(2) == 0) {
+ mapSnapshotter.setCameraPosition(getRandomCameraPosition());
+ } else {
+ mapSnapshotter.setRegion(getRandomBounds());
}
+ if (random.nextInt(2) == 0) {
+ mapSnapshotter.setSize(512, 512);
+ } else {
+ mapSnapshotter.setSize(256, 256);
+ }
+ mapSnapshotter.start(MapSnapshotterReuseActivity.this);
});
mapSnapshotter = new MapSnapshotter(
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/storage/UrlTransformActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/storage/UrlTransformActivity.java
index 74b43e0257..6a1fd8e4e0 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/storage/UrlTransformActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/storage/UrlTransformActivity.java
@@ -6,7 +6,6 @@ import android.support.v7.app.AppCompatActivity;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.storage.FileSource;
import com.mapbox.mapboxsdk.storage.Resource;
import com.mapbox.mapboxsdk.testapp.R;
@@ -47,12 +46,9 @@ public class UrlTransformActivity extends AppCompatActivity {
// Get a handle to the file source and set the resource transform
FileSource.getInstance(UrlTransformActivity.this).setResourceTransform(new Transform());
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(MapboxMap map) {
- Timber.i("Map loaded");
- mapboxMap = map;
- }
+ mapView.getMapAsync(map -> {
+ Timber.i("Map loaded");
+ mapboxMap = map;
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/AnimatedImageSourceActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/AnimatedImageSourceActivity.java
index 1beba632b0..1014af25db 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/AnimatedImageSourceActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/AnimatedImageSourceActivity.java
@@ -18,7 +18,6 @@ import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.style.layers.RasterLayer;
import com.mapbox.mapboxsdk.style.sources.ImageSource;
-
import com.mapbox.mapboxsdk.testapp.R;
/**
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 def7d1678a..609910892b 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
@@ -2,15 +2,12 @@ package com.mapbox.mapboxsdk.testapp.activity.style;
import android.graphics.Color;
import android.os.Bundle;
-import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
-import android.view.View;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.style.functions.Function;
import com.mapbox.mapboxsdk.style.functions.stops.IdentityStops;
import com.mapbox.mapboxsdk.style.layers.FillExtrusionLayer;
@@ -46,13 +43,10 @@ public class BuildingFillExtrusionActivity extends AppCompatActivity {
setContentView(R.layout.activity_building_layer);
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull final MapboxMap map) {
- mapboxMap = map;
- setupBuildings();
- setupLight();
- }
+ mapView.getMapAsync(map -> {
+ mapboxMap = map;
+ setupBuildings();
+ setupLight();
});
}
@@ -73,24 +67,18 @@ public class BuildingFillExtrusionActivity extends AppCompatActivity {
private void setupLight() {
light = mapboxMap.getLight();
- findViewById(R.id.fabLightPosition).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- isInitPosition = !isInitPosition;
- if (isInitPosition) {
- light.setPosition(new Position(1.5f, 90, 80));
- } else {
- light.setPosition(new Position(1.15f, 210, 30));
- }
+ findViewById(R.id.fabLightPosition).setOnClickListener(v -> {
+ isInitPosition = !isInitPosition;
+ if (isInitPosition) {
+ light.setPosition(new Position(1.5f, 90, 80));
+ } else {
+ light.setPosition(new Position(1.15f, 210, 30));
}
});
- findViewById(R.id.fabLightColor).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- isRedColor = !isRedColor;
- light.setColor(PropertyFactory.colorToRgbaString(isRedColor ? Color.RED : Color.BLUE));
- }
+ findViewById(R.id.fabLightColor).setOnClickListener(v -> {
+ isRedColor = !isRedColor;
+ light.setColor(PropertyFactory.colorToRgbaString(isRedColor ? Color.RED : Color.BLUE));
});
}
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 f99a1fdb96..6aa8777777 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
@@ -2,7 +2,6 @@ package com.mapbox.mapboxsdk.testapp.activity.style;
import android.graphics.Color;
import android.os.Bundle;
-import android.support.annotation.NonNull;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
@@ -14,7 +13,6 @@ 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.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.style.layers.CircleLayer;
import com.mapbox.mapboxsdk.style.layers.LineLayer;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
@@ -56,16 +54,12 @@ public class CircleLayerActivity extends AppCompatActivity implements View.OnCli
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull
- final MapboxMap map) {
- mapboxMap = map;
- addBusStopSource();
- addBusStopCircleLayer();
- initFloatingActionButtons();
- isLoadingStyle = false;
- }
+ mapView.getMapAsync(map -> {
+ mapboxMap = map;
+ addBusStopSource();
+ addBusStopCircleLayer();
+ initFloatingActionButtons();
+ isLoadingStyle = false;
});
}
@@ -161,12 +155,9 @@ public class CircleLayerActivity extends AppCompatActivity implements View.OnCli
}
private void loadNewStyle() {
- mapboxMap.setStyleUrl(getNextStyle(), new MapboxMap.OnStyleLoadedListener() {
- @Override
- public void onStyleLoaded(String style) {
- addBusStop();
- isLoadingStyle = false;
- }
+ mapboxMap.setStyleUrl(getNextStyle(), style -> {
+ addBusStop();
+ isLoadingStyle = false;
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CustomSpriteActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CustomSpriteActivity.java
index 3763b45e7a..30cb0a8660 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CustomSpriteActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CustomSpriteActivity.java
@@ -2,25 +2,23 @@ package com.mapbox.mapboxsdk.testapp.activity.style;
import android.graphics.BitmapFactory;
import android.os.Bundle;
-import android.support.annotation.NonNull;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
+import com.mapbox.geojson.Feature;
+import com.mapbox.geojson.FeatureCollection;
+import com.mapbox.geojson.Point;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.style.layers.Layer;
import com.mapbox.mapboxsdk.style.layers.SymbolLayer;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.services.commons.geojson.Feature;
-import com.mapbox.services.commons.geojson.FeatureCollection;
-import com.mapbox.services.commons.geojson.Point;
-import com.mapbox.services.commons.models.Position;
+
import timber.log.Timber;
@@ -45,56 +43,51 @@ public class CustomSpriteActivity extends AppCompatActivity {
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull final MapboxMap map) {
- mapboxMap = map;
- final FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
- fab.setColorFilter(ContextCompat.getColor(CustomSpriteActivity.this, R.color.primary));
- fab.setOnClickListener(new View.OnClickListener() {
- private Point point;
-
- @Override
- public void onClick(View view) {
- if (point == null) {
- Timber.i("First click -> Car");
- // Add an icon to reference later
- mapboxMap.addImage(CUSTOM_ICON, BitmapFactory.decodeResource(getResources(), R.drawable.ic_car_top));
-
- // Add a source with a geojson point
- point = Point.fromCoordinates(Position.fromCoordinates(13.400972d, 52.519003d));
- source = new GeoJsonSource(
- "point",
- FeatureCollection.fromFeatures(new Feature[] {Feature.fromGeometry(point)})
- );
- mapboxMap.addSource(source);
-
- // Add a symbol layer that references that point source
- layer = new SymbolLayer("layer", "point");
- layer.setProperties(
- // Set the id of the sprite to use
- iconImage(CUSTOM_ICON)
- );
-
- // lets add a circle below labels!
- mapboxMap.addLayerBelow(layer, "waterway-label");
-
- fab.setImageResource(R.drawable.ic_directions_car_black);
- } else {
- // Update point
- point = Point.fromCoordinates(
- Position.fromCoordinates(point.getCoordinates().getLongitude() + 0.001,
- point.getCoordinates().getLatitude() + 0.001)
- );
- source.setGeoJson(FeatureCollection.fromFeatures(new Feature[] {Feature.fromGeometry(point)}));
-
- // Move the camera as well
- mapboxMap.moveCamera(CameraUpdateFactory.newLatLng(new LatLng(
- point.getCoordinates().getLatitude(), point.getCoordinates().getLongitude())));
- }
+ mapView.getMapAsync(map -> {
+ mapboxMap = map;
+ final FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
+ fab.setColorFilter(ContextCompat.getColor(CustomSpriteActivity.this, R.color.primary));
+ fab.setOnClickListener(new View.OnClickListener() {
+ private Point point;
+
+ @Override
+ public void onClick(View view) {
+ if (point == null) {
+ Timber.i("First click -> Car");
+ // Add an icon to reference later
+ mapboxMap.addImage(CUSTOM_ICON, BitmapFactory.decodeResource(getResources(), R.drawable.ic_car_top));
+
+ // Add a source with a geojson point
+ point = Point.fromLngLat(13.400972d, 52.519003d);
+ source = new GeoJsonSource(
+ "point",
+ FeatureCollection.fromFeatures(new Feature[] {Feature.fromGeometry(point)})
+ );
+ mapboxMap.addSource(source);
+
+ // Add a symbol layer that references that point source
+ layer = new SymbolLayer("layer", "point");
+ layer.setProperties(
+ // Set the id of the sprite to use
+ iconImage(CUSTOM_ICON)
+ );
+
+ // lets add a circle below labels!
+ mapboxMap.addLayerBelow(layer, "waterway-label");
+
+ fab.setImageResource(R.drawable.ic_directions_car_black);
+ } else {
+ // Update point
+ point = Point.fromLngLat(point.longitude() + 0.001,
+ point.latitude() + 0.001);
+ source.setGeoJson(FeatureCollection.fromFeatures(new Feature[] {Feature.fromGeometry(point)}));
+
+ // Move the camera as well
+ mapboxMap.moveCamera(CameraUpdateFactory.newLatLng(new LatLng(
+ point.latitude(), point.longitude())));
}
- });
- }
+ }
+ });
});
}
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 571b48daa6..cbac62bcc1 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
@@ -5,14 +5,14 @@ import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
+import android.widget.TextView;
import android.widget.Toast;
+import com.mapbox.mapboxsdk.camera.CameraPosition;
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.maps.OnMapReadyCallback;
-import com.mapbox.mapboxsdk.style.functions.stops.Stops;
import com.mapbox.mapboxsdk.style.layers.FillLayer;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
import com.mapbox.mapboxsdk.style.sources.Source;
@@ -23,13 +23,16 @@ import java.io.IOException;
import timber.log.Timber;
-import static com.mapbox.mapboxsdk.style.functions.Function.composite;
-import static com.mapbox.mapboxsdk.style.functions.Function.property;
-import static com.mapbox.mapboxsdk.style.functions.Function.zoom;
-import static com.mapbox.mapboxsdk.style.functions.stops.Stop.stop;
-import static com.mapbox.mapboxsdk.style.functions.stops.Stops.categorical;
-import static com.mapbox.mapboxsdk.style.functions.stops.Stops.exponential;
-import static com.mapbox.mapboxsdk.style.functions.stops.Stops.interval;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.color;
+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.interpolate;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.linear;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.literal;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.match;
+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.layers.PropertyFactory.fillAntialias;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillColor;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillOpacity;
@@ -53,22 +56,32 @@ public class DataDrivenStyleActivity extends AppCompatActivity {
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
+ mapView.getMapAsync(map -> {
+ // Store for later
+ mapboxMap = map;
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(MapboxMap map) {
- // Store for later
- mapboxMap = map;
+ // Add a parks layer
+ addParksLayer();
+
+ // Add debug overlay
+ setupDebugZoomView();
- // Add a parks layer
- addParksLayer();
+ // Center and Zoom (Amsterdam, zoomed to streets)
+ mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(52.379189, 4.899431), 14));
+ });
+ }
- // Center and Zoom (Amsterdam, zoomed to streets)
- mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(52.379189, 4.899431), 14));
+ private void setupDebugZoomView() {
+ final TextView textView = (TextView) findViewById(R.id.textZoom);
+ mapboxMap.setOnCameraChangeListener(new MapboxMap.OnCameraChangeListener() {
+ @Override
+ public void onCameraChange(CameraPosition position) {
+ textView.setText(String.format(getString(R.string.debug_zoom), position.zoom));
}
});
}
+
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_data_driven_style, menu);
@@ -159,12 +172,11 @@ public class DataDrivenStyleActivity extends AppCompatActivity {
assert layer != null;
layer.setProperties(
fillColor(
- zoom(
- exponential(
- stop(1, fillColor(Color.RED)),
- stop(5, fillColor(Color.BLUE)),
- stop(10, fillColor(Color.GREEN))
- ).withBase(0.5f)
+ interpolate(
+ exponential(0.5f), zoom(),
+ stop(1, color(Color.RED)),
+ stop(5, color(Color.BLUE)),
+ stop(10, color(Color.GREEN))
)
)
);
@@ -178,12 +190,11 @@ public class DataDrivenStyleActivity extends AppCompatActivity {
assert layer != null;
layer.setProperties(
fillColor(
- zoom(
- interval(
- stop(1, fillColor(Color.RED)),
- stop(5, fillColor(Color.BLUE)),
- stop(10, fillColor(Color.GREEN))
- )
+ step(zoom(),
+ color(Color.CYAN),
+ stop(1, color(Color.RED)),
+ stop(5, color(Color.BLUE)),
+ stop(10, color(Color.GREEN))
)
)
);
@@ -197,13 +208,12 @@ public class DataDrivenStyleActivity extends AppCompatActivity {
assert layer != null;
layer.setProperties(
fillColor(
- property(
- "stroke-width",
- exponential(
- stop(1f, fillColor(Color.RED)),
- stop(5f, fillColor(Color.BLUE)),
- stop(10f, fillColor(Color.GREEN))
- ).withBase(0.5f)
+ interpolate(
+ exponential(0.5f),
+ get("stroke-width"),
+ stop(1f, color(Color.RED)),
+ stop(5f, color(Color.BLUE)),
+ stop(10f, color(Color.GREEN))
)
)
);
@@ -217,13 +227,13 @@ public class DataDrivenStyleActivity extends AppCompatActivity {
assert layer != null;
layer.setProperties(
fillColor(
- property(
- "name",
- categorical(
- stop("Westerpark", fillColor(Color.RED)),
- stop("Jordaan", fillColor(Color.BLUE)),
- stop("Prinseneiland", fillColor(Color.GREEN))
- ))
+ match(
+ get("name"),
+ literal("Westerpark"), color(Color.RED),
+ literal("Jordaan"), color(Color.BLUE),
+ literal("Prinseneiland"), color(Color.GREEN),
+ color(Color.CYAN)
+ )
)
);
@@ -236,9 +246,7 @@ public class DataDrivenStyleActivity extends AppCompatActivity {
assert layer != null;
layer.setProperties(
fillOpacity(
- property(
- "fill-opacity",
- Stops.<Float>identity())
+ get("fill-opacity")
)
);
@@ -251,13 +259,13 @@ public class DataDrivenStyleActivity extends AppCompatActivity {
assert layer != null;
layer.setProperties(
fillColor(
- property(
- "stroke-width",
- interval(
- stop(1f, fillColor(Color.RED)),
- stop(5f, fillColor(Color.BLUE)),
- stop(10f, fillColor(Color.GREEN))
- ))
+ step(
+ get("stroke-width"),
+ color(Color.CYAN),
+ stop(1f, color(Color.RED)),
+ stop(2f, color(Color.BLUE)),
+ stop(3f, color(Color.GREEN))
+ )
)
);
@@ -270,16 +278,30 @@ public class DataDrivenStyleActivity extends AppCompatActivity {
assert layer != null;
layer.setProperties(
fillColor(
- composite(
- "stroke-width",
- exponential(
- stop(1, 1, fillColor(Color.RED)),
- stop(10, 2, fillColor(Color.BLUE)),
- stop(22, 3, fillColor(Color.GREEN)),
- stop(1, 1, fillColor(Color.CYAN)),
- stop(10, 2, fillColor(Color.GRAY)),
- stop(22, 3, fillColor(Color.YELLOW))
- ).withBase(1f)
+ interpolate(
+ exponential(1f),
+ zoom(),
+ stop(12, step(
+ get("stroke-width"),
+ color(Color.BLACK),
+ stop(1f, color(Color.RED)),
+ stop(2f, color(Color.WHITE)),
+ stop(3f, color(Color.BLUE))
+ )),
+ stop(15, step(
+ get("stroke-width"),
+ color(Color.BLACK),
+ stop(1f, color(Color.YELLOW)),
+ stop(2f, color(Color.LTGRAY)),
+ stop(3f, color(Color.CYAN))
+ )),
+ stop(18, step(
+ get("stroke-width"),
+ color(Color.BLACK),
+ stop(1f, color(Color.WHITE)),
+ stop(2f, color(Color.GRAY)),
+ stop(3f, color(Color.GREEN)))
+ )
)
)
);
@@ -288,21 +310,36 @@ public class DataDrivenStyleActivity extends AppCompatActivity {
}
private void addCompositeIntervalFunction() {
- Timber.i("Add composite exponential function");
+ Timber.i("Add composite interval function");
FillLayer layer = mapboxMap.getLayerAs(AMSTERDAM_PARKS_LAYER);
assert layer != null;
layer.setProperties(
fillColor(
- composite(
- "stroke-width",
- interval(
- stop(1, 1, fillColor(Color.RED)),
- stop(10, 2, fillColor(Color.BLUE)),
- stop(22, 3, fillColor(Color.GREEN)),
- stop(1, 1, fillColor(Color.CYAN)),
- stop(10, 2, fillColor(Color.GRAY)),
- stop(22, 3, fillColor(Color.YELLOW))
+ interpolate(
+ linear(),
+ zoom(),
+ stop(12, step(
+ get("stroke-width"),
+ color(Color.BLACK),
+ stop(1f, color(Color.RED)),
+ stop(2f, color(Color.WHITE)),
+ stop(3f, color(Color.BLUE))
+ )),
+ stop(15, step(
+ get("stroke-width"),
+ color(Color.BLACK),
+ stop(1f, color(Color.YELLOW)),
+ stop(2f, color(Color.LTGRAY)),
+ stop(3f, color(Color.CYAN))
+ )),
+ stop(18, step(
+ get("stroke-width"),
+ color(Color.BLACK),
+ stop(1f, color(Color.WHITE)),
+ stop(2f, color(Color.GRAY)),
+ stop(3f, color(Color.GREEN))
))
+ )
)
);
@@ -315,30 +352,92 @@ public class DataDrivenStyleActivity extends AppCompatActivity {
assert layer != null;
layer.setProperties(
fillColor(
- composite(
- "name",
- categorical(
- stop(7f, "Westerpark", fillColor(Color.RED)),
- stop(8f, "Westerpark", fillColor(Color.BLUE)),
- stop(9f, "Westerpark", fillColor(Color.RED)),
- stop(10f, "Westerpark", fillColor(Color.BLUE)),
- stop(11f, "Westerpark", fillColor(Color.RED)),
- stop(12f, "Westerpark", fillColor(Color.BLUE)),
- stop(13f, "Westerpark", fillColor(Color.RED)),
- stop(14f, "Westerpark", fillColor(Color.BLUE)),
- stop(15f, "Westerpark", fillColor(Color.RED)),
- stop(16f, "Westerpark", fillColor(Color.BLUE)),
- stop(17f, "Westerpark", fillColor(Color.RED)),
- stop(18f, "Westerpark", fillColor(Color.BLUE)),
- stop(19f, "Westerpark", fillColor(Color.RED)),
- stop(20f, "Westerpark", fillColor(Color.BLUE)),
- stop(21f, "Westerpark", fillColor(Color.RED)),
- stop(22f, "Westerpark", fillColor(Color.BLUE)),
- stop(14f, "Jordaan", fillColor(Color.GREEN)),
- stop(18f, "Jordaan", fillColor(Color.CYAN)),
- stop(14f, "Prinseneiland", fillColor(Color.WHITE)),
- stop(18f, "Prinseneiland", fillColor(Color.BLACK))
+ step(zoom(),
+ color(Color.BLACK),
+ stop(7f, match(
+ get("name"),
+ literal("Westerpark"), color(Color.RED),
+ color(Color.BLACK)
+ )),
+ stop(8f, match(
+ get("name"),
+ literal("Westerpark"), color(Color.BLUE),
+ color(Color.BLACK)
+ )),
+ stop(9f, match(
+ get("name"),
+ literal("Westerpark"), color(Color.RED),
+ color(Color.BLACK)
+ )),
+ stop(10f, match(
+ get("name"),
+ literal("Westerpark"), color(Color.BLUE),
+ color(Color.BLACK)
+ )),
+ stop(11f, match(
+ get("name"),
+ literal("Westerpark"), color(Color.RED),
+ color(Color.BLACK)
+ )),
+ stop(12f, match(
+ get("name"),
+ literal("Westerpark"), color(Color.BLUE),
+ color(Color.BLACK)
+ )),
+ stop(13f, match(
+ get("name"),
+ literal("Westerpark"), color(Color.RED),
+ color(Color.BLACK)
+ )),
+ stop(14f, match(
+ get("name"),
+ literal("Westerpark"), color(Color.BLUE),
+ literal("Jordaan"), color(Color.GREEN),
+ literal("PrinsenEiland"), color(Color.WHITE),
+ color(Color.BLACK)
+ )),
+ stop(15f, match(
+ get("name"),
+ literal("Westerpark"), color(Color.RED),
+ color(Color.BLACK)
+ )),
+ stop(16f, match(
+ get("name"),
+ literal("Westerpark"), color(Color.BLUE),
+ color(Color.BLACK)
+ )),
+ stop(17f, match(
+ get("name"),
+ literal("Westerpark"), color(Color.RED),
+ color(Color.BLACK)
+ )),
+ stop(18f, match(
+ get("name"),
+ literal("Westerpark"), color(Color.BLUE),
+ literal("Jordaan"), color(Color.CYAN),
+ color(Color.BLACK)
+ )),
+ stop(19f, match(
+ get("name"),
+ literal("Westerpark"), color(Color.RED),
+ color(Color.BLACK)
+ )),
+ stop(20f, match(
+ get("name"),
+ literal("Westerpark"), color(Color.BLUE),
+ color(Color.BLACK)
+ )),
+ stop(21f, match(
+ get("name"),
+ literal("Westerpark"), color(Color.RED),
+ color(Color.BLACK)
+ )),
+ stop(22f, match(
+ get("name"),
+ literal("Westerpark"), color(Color.BLUE),
+ color(Color.BLACK)
))
+ )
)
);
@@ -359,7 +458,6 @@ public class DataDrivenStyleActivity extends AppCompatActivity {
return;
}
-
// Add a fill layer
mapboxMap.addLayer(new FillLayer(AMSTERDAM_PARKS_LAYER, source.getId())
.withProperties(
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionActivity.java
index 52ba8d7c7b..15d7024abf 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionActivity.java
@@ -2,7 +2,6 @@ package com.mapbox.mapboxsdk.testapp.activity.style;
import android.graphics.Color;
import android.os.Bundle;
-import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import com.mapbox.mapboxsdk.camera.CameraPosition;
@@ -10,11 +9,10 @@ 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.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.style.layers.FillExtrusionLayer;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.services.commons.geojson.Polygon;
+import com.mapbox.geojson.Polygon;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionColor;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionHeight;
@@ -35,58 +33,55 @@ public class FillExtrusionActivity extends AppCompatActivity {
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull final MapboxMap map) {
- mapboxMap = map;
- Polygon domTower = Polygon.fromCoordinates(new double[][][] {
- new double[][] {
- new double[] {
- 5.12112557888031,
- 52.09071040847704
- },
- new double[] {
- 5.121227502822875,
- 52.09053901776669
- },
- new double[] {
- 5.121484994888306,
- 52.090601641371805
- },
- new double[] {
- 5.1213884353637695,
- 52.090766439912635
- },
- new double[] {
- 5.12112557888031,
- 52.09071040847704
- }
+ mapView.getMapAsync(map -> {
+ mapboxMap = map;
+ Polygon domTower = Polygon.fromLngLats(new double[][][] {
+ new double[][] {
+ new double[] {
+ 5.12112557888031,
+ 52.09071040847704
+ },
+ new double[] {
+ 5.121227502822875,
+ 52.09053901776669
+ },
+ new double[] {
+ 5.121484994888306,
+ 52.090601641371805
+ },
+ new double[] {
+ 5.1213884353637695,
+ 52.090766439912635
+ },
+ new double[] {
+ 5.12112557888031,
+ 52.09071040847704
}
- });
-
- GeoJsonSource source = new GeoJsonSource("extrusion-source", domTower);
- map.addSource(source);
-
- mapboxMap.addLayer(
- new FillExtrusionLayer("extrusion-layer", source.getId())
- .withProperties(
- fillExtrusionHeight(40f),
- fillExtrusionOpacity(0.5f),
- fillExtrusionColor(Color.RED)
- )
- );
-
- mapboxMap.animateCamera(
- CameraUpdateFactory.newCameraPosition(
- new CameraPosition.Builder()
- .target(new LatLng(52.09071040847704, 5.12112557888031))
- .tilt(45.0)
- .zoom(18)
- .build()
- ),
- 10000
- );
- }
+ }
+ });
+
+ GeoJsonSource source = new GeoJsonSource("extrusion-source", domTower);
+ map.addSource(source);
+
+ mapboxMap.addLayer(
+ new FillExtrusionLayer("extrusion-layer", source.getId())
+ .withProperties(
+ fillExtrusionHeight(40f),
+ fillExtrusionOpacity(0.5f),
+ fillExtrusionColor(Color.RED)
+ )
+ );
+
+ mapboxMap.animateCamera(
+ CameraUpdateFactory.newCameraPosition(
+ new CameraPosition.Builder()
+ .target(new LatLng(52.09071040847704, 5.12112557888031))
+ .tilt(45.0)
+ .zoom(18)
+ .build()
+ ),
+ 10000
+ );
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionStyleTestActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionStyleTestActivity.java
index 24914fcbb2..b872d022e3 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionStyleTestActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionStyleTestActivity.java
@@ -5,7 +5,6 @@ import android.support.v7.app.AppCompatActivity;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
/**
@@ -24,12 +23,7 @@ public class FillExtrusionStyleTestActivity extends AppCompatActivity {
// Initialize map as normal
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(MapboxMap mapboxMap) {
- FillExtrusionStyleTestActivity.this.mapboxMap = mapboxMap;
- }
- });
+ mapView.getMapAsync(mapboxMap -> FillExtrusionStyleTestActivity.this.mapboxMap = mapboxMap);
}
public MapboxMap getMapboxMap() {
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 a2111bc304..8664979292 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,7 +10,6 @@ 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.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.style.layers.CircleLayer;
import com.mapbox.mapboxsdk.style.layers.SymbolLayer;
import com.mapbox.mapboxsdk.style.sources.GeoJsonOptions;
@@ -50,15 +49,12 @@ public class GeoJsonClusteringActivity extends AppCompatActivity {
// noinspection ConstantConditions
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(MapboxMap map) {
- mapboxMap = map;
- mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(37.7749, 122.4194), 0));
+ mapView.getMapAsync(map -> {
+ mapboxMap = map;
+ mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(37.7749, 122.4194), 0));
- // Add a clustered source with some layers
- addClusteredGeoJsonSource();
- }
+ // Add a clustered source with some layers
+ addClusteredGeoJsonSource();
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GridSourceActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GridSourceActivity.java
new file mode 100644
index 0000000000..fdc3826fb1
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GridSourceActivity.java
@@ -0,0 +1,152 @@
+package com.mapbox.mapboxsdk.testapp.activity.style;
+
+import android.graphics.Color;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v7.app.AppCompatActivity;
+
+import com.mapbox.geojson.Feature;
+import com.mapbox.geojson.FeatureCollection;
+import com.mapbox.geojson.MultiLineString;
+import com.mapbox.geojson.Point;
+import com.mapbox.mapboxsdk.geometry.LatLngBounds;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.style.layers.LineLayer;
+import com.mapbox.mapboxsdk.style.sources.CustomGeometrySource;
+import com.mapbox.mapboxsdk.style.sources.GeometryTileProvider;
+import com.mapbox.mapboxsdk.testapp.R;
+
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineColor;
+
+/**
+ * Test activity showcasing using CustomGeometrySource to create a grid overlay on the map.
+ */
+public class GridSourceActivity extends AppCompatActivity implements OnMapReadyCallback {
+
+ private static final String ID_GRID_SOURCE = "grid_source";
+ private static final String ID_GRID_LAYER = "grid_layer";
+
+ private MapView mapView;
+ private MapboxMap mapboxMap;
+
+ /**
+ * Implementation of GeometryTileProvider that returns features representing a zoom-dependent
+ * grid.
+ */
+ static class GridProvider implements GeometryTileProvider {
+ public FeatureCollection getFeaturesForBounds(LatLngBounds bounds, int zoom) {
+ List<Feature> features = new ArrayList<>();
+ double gridSpacing;
+ if (zoom >= 13) {
+ gridSpacing = 0.01;
+ } else if (zoom >= 11) {
+ gridSpacing = 0.05;
+ } else if (zoom == 10) {
+ gridSpacing = .1;
+ } else if (zoom == 9) {
+ gridSpacing = 0.25;
+ } else if (zoom == 8) {
+ gridSpacing = 0.5;
+ } else if (zoom >= 6) {
+ gridSpacing = 1;
+ } else if (zoom == 5) {
+ gridSpacing = 2;
+ } else if (zoom >= 4) {
+ gridSpacing = 5;
+ } else if (zoom == 2) {
+ gridSpacing = 10;
+ } else {
+ gridSpacing = 20;
+ }
+
+ List gridLines = new ArrayList();
+ for (double y = Math.ceil(bounds.getLatNorth() / gridSpacing) * gridSpacing;
+ y >= Math.floor(bounds.getLatSouth() / gridSpacing) * gridSpacing; y -= gridSpacing) {
+ gridLines.add(Arrays.asList(Point.fromLngLat(bounds.getLonWest(), y),
+ Point.fromLngLat(bounds.getLonEast(), y)));
+ }
+ features.add(Feature.fromGeometry(MultiLineString.fromLngLats(gridLines)));
+
+ gridLines = new ArrayList();
+ for (double x = Math.floor(bounds.getLonWest() / gridSpacing) * gridSpacing;
+ x <= Math.ceil(bounds.getLonEast() / gridSpacing) * gridSpacing; x += gridSpacing) {
+ gridLines.add(Arrays.asList(Point.fromLngLat(x, bounds.getLatSouth()),
+ Point.fromLngLat(x, bounds.getLatNorth())));
+ }
+ features.add(Feature.fromGeometry(MultiLineString.fromLngLats(gridLines)));
+
+ return FeatureCollection.fromFeatures(features);
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_grid_source);
+
+ mapView = (MapView) findViewById(R.id.mapView);
+ mapView.onCreate(savedInstanceState);
+ mapView.getMapAsync(this);
+ }
+
+ @Override
+ public void onMapReady(@NonNull final MapboxMap map) {
+ mapboxMap = map;
+
+ // add source
+ CustomGeometrySource source = new CustomGeometrySource(ID_GRID_SOURCE, new GridProvider());
+ mapboxMap.addSource(source);
+
+ // add layer
+ LineLayer layer = new LineLayer(ID_GRID_LAYER, ID_GRID_SOURCE);
+ layer.setProperties(
+ lineColor(Color.parseColor("#000000"))
+ );
+
+ mapboxMap.addLayer(layer);
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ mapView.onStart();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mapView.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mapView.onPause();
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ mapView.onStop();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mapView.onDestroy();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mapView.onSaveInstanceState(outState);
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/HeatmapLayerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/HeatmapLayerActivity.java
new file mode 100644
index 0000000000..b42734ea67
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/HeatmapLayerActivity.java
@@ -0,0 +1,216 @@
+package com.mapbox.mapboxsdk.testapp.activity.style;
+
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+
+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.HeatmapLayer;
+import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
+import com.mapbox.mapboxsdk.testapp.R;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import timber.log.Timber;
+
+import static com.mapbox.mapboxsdk.style.expressions.Expression.get;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.interpolate;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.linear;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.literal;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.rgb;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.rgba;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.stop;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.zoom;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleColor;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleOpacity;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleRadius;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleStrokeColor;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleStrokeWidth;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.heatmapIntensity;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.heatmapOpacity;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.heatmapRadius;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.heatmapWeight;
+
+/**
+ * Test activity showcasing the heatmap layer api.
+ */
+public class HeatmapLayerActivity extends AppCompatActivity {
+
+ private static final String EARTHQUAKE_SOURCE_URL = "https://www.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson";
+ private static final String EARTHQUAKE_SOURCE_ID = "earthquakes";
+ private static final String HEATMAP_LAYER_ID = "earthquakes-heat";
+ private static final String HEATMAP_LAYER_SOURCE = "earthquakes";
+ private static final String CIRCLE_LAYER_ID = "earthquakes-circle";
+
+ private MapView mapView;
+ private MapboxMap mapboxMap;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_heatmaplayer);
+
+ mapView = (MapView) findViewById(R.id.mapView);
+ mapView.onCreate(savedInstanceState);
+ mapView.getMapAsync(map -> {
+ mapboxMap = map;
+ addEarthquakeSource();
+ addHeatmapLayer();
+ addCircleLayer();
+ });
+ }
+
+ private void addEarthquakeSource() {
+ try {
+ mapboxMap.addSource(new GeoJsonSource(EARTHQUAKE_SOURCE_ID, new URL(EARTHQUAKE_SOURCE_URL)));
+ } catch (MalformedURLException malformedUrlException) {
+ Timber.e(malformedUrlException, "That's not an url... ");
+ }
+ }
+
+ private void addHeatmapLayer() {
+ HeatmapLayer layer = new HeatmapLayer(HEATMAP_LAYER_ID, EARTHQUAKE_SOURCE_ID);
+ layer.setMaxZoom(9);
+ layer.setSourceLayer(HEATMAP_LAYER_SOURCE);
+ layer.setProperties(
+
+ // TODO add heatmap color https://github.com/mapbox/mapbox-gl-native/issues/11172
+ // Color ramp for heatmap. Domain is 0 (low) to 1 (high).
+ // Begin color ramp at 0-stop with a 0-transparancy color
+ // to create a blur-like effect.
+ //heatmapColor(),
+
+ // Increase the heatmap weight based on frequency and property magnitude
+ heatmapWeight(
+ interpolate(
+ linear(), get("mag"),
+ stop(0, 0),
+ stop(6, 1)
+ )
+ ),
+
+ // Increase the heatmap color weight weight by zoom level
+ // heatmap-intensity is a multiplier on top of heatmap-weight
+ heatmapIntensity(
+ interpolate(
+ linear(), zoom(),
+ stop(0, 1),
+ stop(9, 3)
+ )
+ ),
+
+ // Adjust the heatmap radius by zoom level
+ heatmapRadius(
+ interpolate(
+ linear(), zoom(),
+ stop(0, 2),
+ stop(9, 20)
+ )
+ ),
+
+ // Transition from heatmap to circle layer by zoom level
+ heatmapOpacity(
+ interpolate(
+ linear(), zoom(),
+ stop(7, 1),
+ stop(9, 0)
+ )
+ )
+ );
+
+ mapboxMap.addLayerAbove(layer, "waterway-label");
+ }
+
+ private void addCircleLayer() {
+ CircleLayer circleLayer = new CircleLayer(CIRCLE_LAYER_ID, EARTHQUAKE_SOURCE_ID);
+ circleLayer.setProperties(
+
+ // Size circle radius by earthquake magnitude and zoom level
+ circleRadius(
+ interpolate(
+ linear(), zoom(),
+ literal(7), interpolate(
+ linear(), get("mag"),
+ stop(1, 1),
+ stop(6, 4)
+ ),
+ literal(16), interpolate(
+ linear(), get("mag"),
+ stop(1, 5),
+ stop(6, 50)
+ )
+ )
+ ),
+
+ // Color circle by earthquake magnitude
+ circleColor(
+ interpolate(
+ linear(), get("mag"),
+ literal(1), rgba(33, 102, 172, 0),
+ literal(2), rgb(103, 169, 207),
+ literal(3), rgb(209, 229, 240),
+ literal(4), rgb(253, 219, 199),
+ literal(5), rgb(239, 138, 98),
+ literal(6), rgb(178, 24, 43)
+ )
+ ),
+
+ // Transition from heatmap to circle layer by zoom level
+ circleOpacity(
+ interpolate(
+ linear(), zoom(),
+ stop(7, 0),
+ stop(8, 1)
+ )
+ ),
+ circleStrokeColor("white"),
+ circleStrokeWidth(1.0f)
+ );
+
+ mapboxMap.addLayerBelow(circleLayer, HEATMAP_LAYER_ID);
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ mapView.onStart();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mapView.onResume();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mapView.onPause();
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ mapView.onStop();
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mapView.onLowMemory();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mapView.onDestroy();
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/HillshadeLayerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/HillshadeLayerActivity.java
new file mode 100644
index 0000000000..066446652b
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/HillshadeLayerActivity.java
@@ -0,0 +1,84 @@
+package com.mapbox.mapboxsdk.testapp.activity.style;
+
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.style.layers.HillshadeLayer;
+import com.mapbox.mapboxsdk.style.sources.RasterDemSource;
+import com.mapbox.mapboxsdk.testapp.R;
+
+/**
+ * Test activity showcasing using HillshadeLayer.
+ */
+public class HillshadeLayerActivity extends AppCompatActivity {
+
+ private static final String LAYER_ID = "hillshade-layer";
+ private static final String LAYER_BELOW_ID = "waterway-river-canal";
+ private static final String SOURCE_ID = "hillshade-source";
+ private static final String SOURCE_URL = "mapbox://mapbox.terrain-rgb";
+
+ private MapView mapView;
+ private MapboxMap mapboxMap;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_fill_extrusion_layer);
+
+ mapView = (MapView) findViewById(R.id.mapView);
+ mapView.onCreate(savedInstanceState);
+ mapView.getMapAsync(map -> {
+ mapboxMap = map;
+
+ RasterDemSource rasterDemSource = new RasterDemSource(SOURCE_ID, SOURCE_URL);
+ mapboxMap.addSource(rasterDemSource);
+
+ HillshadeLayer hillshadeLayer = new HillshadeLayer(LAYER_ID, SOURCE_ID);
+ mapboxMap.addLayerBelow(hillshadeLayer, LAYER_BELOW_ID);
+ });
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ mapView.onStart();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mapView.onResume();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mapView.onPause();
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ mapView.onStop();
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mapView.onLowMemory();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mapView.onDestroy();
+ }
+} \ No newline at end of file
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 adce889007..6eb4772b15 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java
@@ -8,14 +8,12 @@ import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
+import com.mapbox.geojson.Feature;
+import com.mapbox.geojson.FeatureCollection;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
-import com.mapbox.mapboxsdk.style.functions.Function;
-import com.mapbox.mapboxsdk.style.functions.stops.ExponentialStops;
-import com.mapbox.mapboxsdk.style.functions.stops.Stop;
import com.mapbox.mapboxsdk.style.layers.CircleLayer;
import com.mapbox.mapboxsdk.style.layers.FillLayer;
import com.mapbox.mapboxsdk.style.layers.Layer;
@@ -32,8 +30,7 @@ import com.mapbox.mapboxsdk.style.sources.TileSet;
import com.mapbox.mapboxsdk.style.sources.VectorSource;
import com.mapbox.mapboxsdk.testapp.R;
import com.mapbox.mapboxsdk.testapp.utils.ResourceUtils;
-import com.mapbox.services.commons.geojson.Feature;
-import com.mapbox.services.commons.geojson.FeatureCollection;
+
import java.io.IOException;
import java.util.ArrayList;
@@ -42,9 +39,11 @@ import java.util.List;
import timber.log.Timber;
-import static com.mapbox.mapboxsdk.style.functions.Function.zoom;
-import static com.mapbox.mapboxsdk.style.functions.stops.Stop.stop;
-import static com.mapbox.mapboxsdk.style.functions.stops.Stops.exponential;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.color;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.exponential;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.interpolate;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.stop;
+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;
@@ -85,18 +84,15 @@ public class RuntimeStyleActivity extends AppCompatActivity {
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(MapboxMap map) {
- // Store for later
- mapboxMap = map;
+ mapView.getMapAsync(map -> {
+ // Store for later
+ mapboxMap = map;
- // Center and Zoom (Amsterdam, zoomed to streets)
- mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(52.379189, 4.899431), 14));
+ // Center and Zoom (Amsterdam, zoomed to streets)
+ mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(52.379189, 4.899431), 14));
- mapboxMap.setTransitionDuration(250);
- mapboxMap.setTransitionDelay(50);
- }
+ mapboxMap.setTransitionDuration(250);
+ mapboxMap.setTransitionDelay(50);
});
}
@@ -362,32 +358,29 @@ public class RuntimeStyleActivity extends AppCompatActivity {
private void animateParksSource(final FeatureCollection parks, final int counter) {
Handler handler = new Handler(getMainLooper());
- handler.postDelayed(new Runnable() {
- @Override
- public void run() {
- if (mapboxMap == null) {
- return;
- }
+ handler.postDelayed(() -> {
+ if (mapboxMap == null) {
+ return;
+ }
- Timber.d("Updating parks source");
- // change the source
- int park = counter < parks.getFeatures().size() - 1 ? counter : 0;
+ Timber.d("Updating parks source");
+ // change the source
+ int park = counter < parks.features().size() - 1 ? counter : 0;
- GeoJsonSource source = mapboxMap.getSourceAs("dynamic-park-source");
+ GeoJsonSource source = mapboxMap.getSourceAs("dynamic-park-source");
- if (source == null) {
- Timber.e("Source not found");
- Toast.makeText(RuntimeStyleActivity.this, "Source not found", Toast.LENGTH_SHORT).show();
- return;
- }
+ if (source == null) {
+ Timber.e("Source not found");
+ Toast.makeText(RuntimeStyleActivity.this, "Source not found", Toast.LENGTH_SHORT).show();
+ return;
+ }
- List<Feature> features = new ArrayList<>();
- features.add(parks.getFeatures().get(park));
- source.setGeoJson(FeatureCollection.fromFeatures(features));
+ List<Feature> features = new ArrayList<>();
+ features.add(parks.features().get(park));
+ source.setGeoJson(FeatureCollection.fromFeatures(features));
- // Re-post
- animateParksSource(parks, park + 1);
- }
+ // Re-post
+ animateParksSource(parks, park + 1);
}, counter == 0 ? 100 : 1000);
}
@@ -453,44 +446,42 @@ public class RuntimeStyleActivity extends AppCompatActivity {
}
// Set a zoom function to update the color of the water
- layer.setProperties(fillColor(
- zoom(
- exponential(
- stop(1, fillColor(Color.GREEN)),
- stop(4, fillColor(Color.BLUE)),
- stop(12, fillColor(Color.RED)),
- stop(20, fillColor(Color.BLACK))
- ).withBase(0.8f)
+ layer.setProperties(
+ fillColor(
+ interpolate(
+ exponential(0.8f),
+ zoom(),
+ stop(1, color(Color.GREEN)),
+ stop(4, color(Color.BLUE)),
+ stop(12, color(Color.RED)),
+ stop(20, color(Color.BLACK))
+ )
)
- ));
+ );
// do some animations to show it off properly
mapboxMap.animateCamera(CameraUpdateFactory.zoomTo(1), 1500);
-
- PropertyValue<String> fillColor = layer.getFillColor();
- Function<Float, String> function = (Function<Float, String>) fillColor.getFunction();
- if (function != null) {
- ExponentialStops<Float, String> stops = (ExponentialStops) function.getStops();
- Timber.d("Fill color base: %s", stops.getBase());
- Timber.d("Fill color #stops: %s", stops.size());
- if (function.getStops() != null) {
- for (Stop<Float, String> stop : stops) {
- Timber.d("Fill color #stops: %s", stop);
- }
- }
- }
}
private void addCustomTileSource() {
// Add a source
- Source source = new VectorSource("custom-tile-source", new TileSet("2.1.0", "https://vector.mapzen.com/osm/all/{z}/{x}/{y}.mvt?api_key=vector-tiles-LM25tq4"));
+ TileSet tileSet = new TileSet("2.1.0", "https://d25uarhxywzl1j.cloudfront.net/v0.1/{z}/{x}/{y}.mvt");
+ tileSet.setMinZoom(0);
+ tileSet.setMaxZoom(14);
+ Source source = new VectorSource("custom-tile-source", tileSet);
mapboxMap.addSource(source);
// Add a layer
- mapboxMap.addLayer(
- new FillLayer("custom-tile-layers", "custom-tile-source")
- .withSourceLayer("water")
+ LineLayer lineLayer = new LineLayer("custom-tile-layers", "custom-tile-source");
+ lineLayer.setSourceLayer("mapillary-sequences");
+ lineLayer.setProperties(
+ lineCap(Property.LINE_CAP_ROUND),
+ lineJoin(Property.LINE_JOIN_ROUND),
+ lineOpacity(0.6f),
+ lineWidth(2.0f),
+ lineColor(Color.GREEN)
);
+ mapboxMap.addLayer(lineLayer);
}
private void styleFillFilterLayer() {
@@ -498,28 +489,25 @@ public class RuntimeStyleActivity extends AppCompatActivity {
mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(31, -100), 3));
Handler handler = new Handler(getMainLooper());
- handler.postDelayed(new Runnable() {
- @Override
- public void run() {
- if (mapboxMap == null) {
- return;
- }
+ handler.postDelayed(() -> {
+ if (mapboxMap == null) {
+ return;
+ }
- Timber.d("Styling filtered fill layer");
+ Timber.d("Styling filtered fill layer");
- FillLayer states = (FillLayer) mapboxMap.getLayer("states");
+ FillLayer states = (FillLayer) mapboxMap.getLayer("states");
- if (states != null) {
- states.setFilter(eq("name", "Texas"));
- states.setFillOpacityTransition(new TransitionOptions(2500, 0));
- states.setFillColorTransition(new TransitionOptions(2500, 0));
- states.setProperties(
- fillColor(Color.RED),
- fillOpacity(0.25f)
- );
- } else {
- Toast.makeText(RuntimeStyleActivity.this, "No states layer in this style", Toast.LENGTH_SHORT).show();
- }
+ if (states != null) {
+ states.setFilter(eq("name", "Texas"));
+ states.setFillOpacityTransition(new TransitionOptions(2500, 0));
+ states.setFillColorTransition(new TransitionOptions(2500, 0));
+ states.setProperties(
+ fillColor(Color.RED),
+ fillOpacity(0.25f)
+ );
+ } else {
+ Toast.makeText(RuntimeStyleActivity.this, "No states layer in this style", Toast.LENGTH_SHORT).show();
}
}, 2000);
}
@@ -529,28 +517,25 @@ public class RuntimeStyleActivity extends AppCompatActivity {
mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(40, -97), 5));
Handler handler = new Handler(getMainLooper());
- handler.postDelayed(new Runnable() {
- @Override
- public void run() {
- if (mapboxMap == null) {
- return;
- }
+ handler.postDelayed(() -> {
+ if (mapboxMap == null) {
+ return;
+ }
- Timber.d("Styling filtered line layer");
+ Timber.d("Styling filtered line layer");
- LineLayer counties = (LineLayer) mapboxMap.getLayer("counties");
+ LineLayer counties = (LineLayer) mapboxMap.getLayer("counties");
- if (counties != null) {
- counties.setFilter(eq("NAME10", "Washington"));
+ if (counties != null) {
+ counties.setFilter(eq("NAME10", "Washington"));
- counties.setProperties(
- lineColor(Color.RED),
- lineOpacity(0.75f),
- lineWidth(5f)
- );
- } else {
- Toast.makeText(RuntimeStyleActivity.this, "No counties layer in this style", Toast.LENGTH_SHORT).show();
- }
+ counties.setProperties(
+ lineColor(Color.RED),
+ lineOpacity(0.75f),
+ lineWidth(5f)
+ );
+ } else {
+ Toast.makeText(RuntimeStyleActivity.this, "No counties layer in this style", Toast.LENGTH_SHORT).show();
}
}, 2000);
}
@@ -560,27 +545,24 @@ public class RuntimeStyleActivity extends AppCompatActivity {
mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(40, -97), 5));
Handler handler = new Handler(getMainLooper());
- handler.postDelayed(new Runnable() {
- @Override
- public void run() {
- if (mapboxMap == null) {
- return;
- }
+ handler.postDelayed(() -> {
+ if (mapboxMap == null) {
+ return;
+ }
- Timber.d("Styling numeric fill layer");
+ Timber.d("Styling numeric fill layer");
- FillLayer regions = (FillLayer) mapboxMap.getLayer("regions");
+ FillLayer regions = (FillLayer) mapboxMap.getLayer("regions");
- if (regions != null) {
- regions.setFilter(all(gte("HRRNUM", 200), lt("HRRNUM", 300)));
+ if (regions != null) {
+ regions.setFilter(all(gte("HRRNUM", 200), lt("HRRNUM", 300)));
- regions.setProperties(
- fillColor(Color.BLUE),
- fillOpacity(0.5f)
- );
- } else {
- Toast.makeText(RuntimeStyleActivity.this, "No regions layer in this style", Toast.LENGTH_SHORT).show();
- }
+ regions.setProperties(
+ fillColor(Color.BLUE),
+ fillOpacity(0.5f)
+ );
+ } else {
+ Toast.makeText(RuntimeStyleActivity.this, "No regions layer in this style", Toast.LENGTH_SHORT).show();
}
}, 2000);
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleTestActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleTestActivity.java
index 910233accf..53f0870d90 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleTestActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleTestActivity.java
@@ -5,7 +5,6 @@ import android.support.v7.app.AppCompatActivity;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
/**
@@ -24,12 +23,7 @@ public class RuntimeStyleTestActivity extends AppCompatActivity {
// Initialize map as normal
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(MapboxMap mapboxMap) {
- RuntimeStyleTestActivity.this.mapboxMap = mapboxMap;
- }
- });
+ mapView.getMapAsync(mapboxMap -> RuntimeStyleTestActivity.this.mapboxMap = mapboxMap);
}
public MapboxMap getMapboxMap() {
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleTimingTestActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleTimingTestActivity.java
index 5057578731..e51a7ec8a0 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleTimingTestActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleTimingTestActivity.java
@@ -6,7 +6,6 @@ import android.support.v7.app.AppCompatActivity;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.style.layers.CircleLayer;
import com.mapbox.mapboxsdk.style.sources.VectorSource;
import com.mapbox.mapboxsdk.testapp.R;
@@ -32,23 +31,20 @@ public class RuntimeStyleTimingTestActivity extends AppCompatActivity {
// Initialize map as normal
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(MapboxMap mapboxMap) {
- RuntimeStyleTimingTestActivity.this.mapboxMap = mapboxMap;
- VectorSource museums = new VectorSource("museums_source", "mapbox://mapbox.2opop9hr");
- mapboxMap.addSource(museums);
-
- CircleLayer museumsLayer = new CircleLayer("museums", "museums_source");
- museumsLayer.setSourceLayer("museum-cusco");
- museumsLayer.setProperties(
- visibility(VISIBLE),
- circleRadius(8f),
- circleColor(Color.argb(1, 55, 148, 179))
- );
-
- mapboxMap.addLayer(museumsLayer);
- }
+ mapView.getMapAsync(mapboxMap -> {
+ RuntimeStyleTimingTestActivity.this.mapboxMap = mapboxMap;
+ VectorSource museums = new VectorSource("museums_source", "mapbox://mapbox.2opop9hr");
+ mapboxMap.addSource(museums);
+
+ CircleLayer museumsLayer = new CircleLayer("museums", "museums_source");
+ museumsLayer.setSourceLayer("museum-cusco");
+ museumsLayer.setProperties(
+ visibility(VISIBLE),
+ circleRadius(8f),
+ circleColor(Color.argb(1, 55, 148, 179))
+ );
+
+ mapboxMap.addLayer(museumsLayer);
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/StyleFileActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/StyleFileActivity.java
index 49015ec1e9..d2a46c63ae 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/StyleFileActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/StyleFileActivity.java
@@ -3,16 +3,13 @@ package com.mapbox.mapboxsdk.testapp.activity.style;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
-import android.support.annotation.NonNull;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
-import android.view.View;
import android.widget.Toast;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
import com.mapbox.mapboxsdk.testapp.utils.ResourceUtils;
@@ -39,29 +36,16 @@ public class StyleFileActivity extends AppCompatActivity {
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull final MapboxMap map) {
- mapboxMap = map;
-
- FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab_file);
- fab.setColorFilter(ContextCompat.getColor(StyleFileActivity.this, R.color.primary));
- fab.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- new CreateStyleFileTask(view.getContext(), mapboxMap).execute();
- }
- });
-
- FloatingActionButton fabStyleJson = (FloatingActionButton) findViewById(R.id.fab_style_json);
- fabStyleJson.setColorFilter(ContextCompat.getColor(StyleFileActivity.this, R.color.primary));
- fabStyleJson.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- new LoadStyleFileTask(view.getContext(), mapboxMap).execute();
- }
- });
- }
+ mapView.getMapAsync(map -> {
+ mapboxMap = map;
+
+ FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab_file);
+ fab.setColorFilter(ContextCompat.getColor(StyleFileActivity.this, R.color.primary));
+ fab.setOnClickListener(view -> new CreateStyleFileTask(view.getContext(), mapboxMap).execute());
+
+ FloatingActionButton fabStyleJson = (FloatingActionButton) findViewById(R.id.fab_style_json);
+ fabStyleJson.setColorFilter(ContextCompat.getColor(StyleFileActivity.this, R.color.primary));
+ fabStyleJson.setOnClickListener(view -> new LoadStyleFileTask(view.getContext(), mapboxMap).execute());
});
}
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 6e9e45cfaa..ca4176be6e 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java
@@ -15,22 +15,18 @@ import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
-import com.google.gson.GsonBuilder;
-import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.geojson.Feature;
+import com.mapbox.geojson.FeatureCollection;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+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;
import com.mapbox.mapboxsdk.testapp.R;
import com.mapbox.mapboxsdk.testapp.utils.ResourceUtils;
-import com.mapbox.services.commons.geojson.Feature;
-import com.mapbox.services.commons.geojson.FeatureCollection;
-import com.mapbox.services.commons.geojson.Geometry;
-import com.mapbox.services.commons.geojson.custom.GeometryDeserializer;
-import com.mapbox.services.commons.geojson.custom.PositionDeserializer;
-import com.mapbox.services.commons.models.Position;
+
import java.io.IOException;
import java.util.HashMap;
@@ -38,8 +34,24 @@ import java.util.List;
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.get;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.literal;
+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.upcase;
+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;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconAnchor;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconImage;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconOffset;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconSize;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textAnchor;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textField;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textSize;
/**
* Test activity showcasing using a symbol generator that generates Bitmaps from Android SDK Views.
@@ -49,7 +61,10 @@ public class SymbolGeneratorActivity extends AppCompatActivity implements OnMapR
private static final String SOURCE_ID = "com.mapbox.mapboxsdk.style.layers.symbol.source.id";
private static final String LAYER_ID = "com.mapbox.mapboxsdk.style.layers.symbol.layer.id";
private static final String FEATURE_ID = "brk_name";
- private static final String FEATURE_VALUE = "name_sort";
+ private static final String FEATURE_RANK = "scalerank";
+ private static final String FEATURE_NAME = "name_sort";
+ private static final String FEATURE_TYPE = "type";
+ private static final String FEATURE_REGION = "continent";
private MapView mapView;
private MapboxMap mapboxMap;
@@ -68,23 +83,20 @@ public class SymbolGeneratorActivity extends AppCompatActivity implements OnMapR
public void onMapReady(final MapboxMap map) {
mapboxMap = map;
addSymbolClickListener();
- new LoadDataTask(map, SymbolGeneratorActivity.this).execute();
+ new LoadDataTask(this).execute();
}
private void addSymbolClickListener() {
- mapboxMap.setOnMapClickListener(new MapboxMap.OnMapClickListener() {
- @Override
- public void onMapClick(@NonNull LatLng point) {
- PointF screenPoint = mapboxMap.getProjection().toScreenLocation(point);
- List<Feature> features = mapboxMap.queryRenderedFeatures(screenPoint, LAYER_ID);
- if (!features.isEmpty()) {
- Feature feature = features.get(0);
- Timber.v("Feature was clicked with data: %s", feature.toJson());
- Toast.makeText(
- SymbolGeneratorActivity.this,
- "hello from: " + feature.getStringProperty(FEATURE_VALUE),
- Toast.LENGTH_LONG).show();
- }
+ mapboxMap.setOnMapClickListener(point -> {
+ PointF screenPoint = mapboxMap.getProjection().toScreenLocation(point);
+ List<Feature> features = mapboxMap.queryRenderedFeatures(screenPoint, LAYER_ID);
+ if (!features.isEmpty()) {
+ Feature feature = features.get(0);
+ Timber.v("Feature was clicked with data: %s", feature.toJson());
+ Toast.makeText(
+ SymbolGeneratorActivity.this,
+ "hello from: " + feature.getStringProperty(FEATURE_NAME),
+ Toast.LENGTH_LONG).show();
}
});
}
@@ -100,6 +112,12 @@ public class SymbolGeneratorActivity extends AppCompatActivity implements OnMapR
if (item.getItemId() == R.id.menu_action_icon_overlap) {
SymbolLayer layer = mapboxMap.getLayerAs(LAYER_ID);
layer.setProperties(iconAllowOverlap(!layer.getIconAllowOverlap().getValue()));
+ 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));
+ return true;
}
return super.onOptionsItemSelected(item);
}
@@ -178,56 +196,72 @@ public class SymbolGeneratorActivity extends AppCompatActivity implements OnMapR
private static class LoadDataTask extends AsyncTask<Void, Void, FeatureCollection> {
- private final MapboxMap mapboxMap;
- private final Context context;
+ private SymbolGeneratorActivity activity;
- LoadDataTask(MapboxMap mapboxMap, Context context) {
- this.mapboxMap = mapboxMap;
- this.context = context;
+ LoadDataTask(SymbolGeneratorActivity activity) {
+ this.activity = activity;
}
@Override
protected FeatureCollection doInBackground(Void... params) {
try {
// read local geojson from raw folder
- String tinyCountriesJson = ResourceUtils.readRawResource(context, R.raw.tiny_countries);
-
- // convert geojson to a model
- FeatureCollection featureCollection = new GsonBuilder()
- .registerTypeAdapter(Geometry.class, new GeometryDeserializer())
- .registerTypeAdapter(Position.class, new PositionDeserializer())
- .create().fromJson(tinyCountriesJson, FeatureCollection.class);
+ String tinyCountriesJson = ResourceUtils.readRawResource(activity, R.raw.tiny_countries);
+ return FeatureCollection.fromJson(tinyCountriesJson);
- return featureCollection;
} catch (IOException exception) {
return null;
}
}
-
@Override
protected void onPostExecute(FeatureCollection featureCollection) {
super.onPostExecute(featureCollection);
- if (featureCollection == null) {
+ if (featureCollection == null || activity == null) {
return;
}
- // add a geojson to the map
- Source source = new GeoJsonSource(SOURCE_ID, featureCollection);
- mapboxMap.addSource(source);
-
- // create layer use
- mapboxMap.addLayer(new SymbolLayer(LAYER_ID, SOURCE_ID)
- .withProperties(
- iconImage("{" + FEATURE_ID + "}"), // { } is a token notation
- iconAllowOverlap(false)
- )
- );
-
- new GenerateSymbolTask(mapboxMap, context).execute(featureCollection);
+ activity.onDataLoaded(featureCollection);
}
}
+ public void onDataLoaded(@NonNull FeatureCollection featureCollection) {
+ // add a geojson to the map
+ Source source = new GeoJsonSource(SOURCE_ID, featureCollection);
+ mapboxMap.addSource(source);
+
+ // create layer use
+ mapboxMap.addLayer(new SymbolLayer(LAYER_ID, SOURCE_ID)
+ .withProperties(
+
+ // icon configuration
+ iconImage(get(literal(FEATURE_ID))),
+ iconAllowOverlap(false),
+ iconSize(
+ division(get(literal(FEATURE_RANK)), literal(2))
+ ),
+ iconAnchor(ICON_ANCHOR_BOTTOM),
+ iconOffset(new Float[] {0.0f, -5.0f}),
+
+ // text field configuration
+ textField(
+ concat(
+ upcase(literal("a ")),
+ get(literal(FEATURE_TYPE)),
+ downcase(literal(" IN ")),
+ get(literal(FEATURE_REGION))
+ )
+ ),
+ textSize(
+ product(get(literal(FEATURE_RANK)), pi())
+ ),
+ textAnchor(TEXT_ANCHOR_TOP)
+ )
+ );
+
+ new GenerateSymbolTask(mapboxMap, this).execute(featureCollection);
+ }
+
private static class GenerateSymbolTask extends AsyncTask<FeatureCollection, Void, HashMap<String, Bitmap>> {
private MapboxMap mapboxMap;
@@ -244,7 +278,7 @@ public class SymbolGeneratorActivity extends AppCompatActivity implements OnMapR
FeatureCollection featureCollection = params[0];
HashMap<String, Bitmap> imagesMap = new HashMap<>();
- for (Feature feature : featureCollection.getFeatures()) {
+ for (Feature feature : featureCollection.features()) {
String countryName = feature.getStringProperty(FEATURE_ID);
TextView textView = new TextView(context);
textView.setBackgroundColor(context.getResources().getColor(R.color.blueAccent));
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolLayerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolLayerActivity.java
index 82da905413..e3a4f4be93 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolLayerActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolLayerActivity.java
@@ -11,17 +11,17 @@ import android.view.MenuItem;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
+import com.mapbox.geojson.Feature;
+import com.mapbox.geojson.FeatureCollection;
+import com.mapbox.geojson.Point;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.style.layers.SymbolLayer;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.services.commons.geojson.Feature;
-import com.mapbox.services.commons.geojson.FeatureCollection;
-import com.mapbox.services.commons.geojson.Point;
+
import java.util.Arrays;
import java.util.List;
@@ -51,43 +51,40 @@ public class SymbolLayerActivity extends AppCompatActivity implements MapboxMap.
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull final MapboxMap map) {
- mapboxMap = map;
-
- // Add a image for the makers
- mapboxMap.addImage(
- "my-marker-image",
- BitmapFactory.decodeResource(SymbolLayerActivity.this.getResources(),
- R.drawable.mapbox_marker_icon_default)
- );
-
- // Add a source
- FeatureCollection markers = FeatureCollection.fromFeatures(new Feature[] {
- Feature.fromGeometry(Point.fromCoordinates(new double[] {4.91638, 52.35673}), featureProperties("Marker 1")),
- Feature.fromGeometry(Point.fromCoordinates(new double[] {4.91638, 52.34673}), featureProperties("Marker 2"))
- });
- mapboxMap.addSource(new GeoJsonSource(MARKER_SOURCE, markers));
-
- // Add the symbol-layer
- mapboxMap.addLayer(
- new SymbolLayer(MARKER_LAYER, MARKER_SOURCE)
- .withProperties(
- iconImage("my-marker-image"),
- iconAllowOverlap(true),
- textField("{title}"),
- textColor(Color.RED),
- textSize(10f)
- )
- );
-
- // Show
- mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(52.35273, 4.91638), 14));
-
- // Set a click-listener so we can manipulate the map
- mapboxMap.setOnMapClickListener(SymbolLayerActivity.this);
- }
+ mapView.getMapAsync(map -> {
+ mapboxMap = map;
+
+ // Add a image for the makers
+ mapboxMap.addImage(
+ "my-marker-image",
+ BitmapFactory.decodeResource(SymbolLayerActivity.this.getResources(),
+ R.drawable.mapbox_marker_icon_default)
+ );
+
+ // Add a source
+ FeatureCollection markers = FeatureCollection.fromFeatures(new Feature[] {
+ Feature.fromGeometry(Point.fromLngLat(4.91638, 52.35673), featureProperties("Marker 1")),
+ Feature.fromGeometry(Point.fromLngLat(4.91638, 52.34673), featureProperties("Marker 2"))
+ });
+ mapboxMap.addSource(new GeoJsonSource(MARKER_SOURCE, markers));
+
+ // Add the symbol-layer
+ mapboxMap.addLayer(
+ new SymbolLayer(MARKER_LAYER, MARKER_SOURCE)
+ .withProperties(
+ iconImage("my-marker-image"),
+ iconAllowOverlap(true),
+ textField("{title}"),
+ textColor(Color.RED),
+ textSize(10f)
+ )
+ );
+
+ // Show
+ mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(52.35273, 4.91638), 14));
+
+ // Set a click-listener so we can manipulate the map
+ mapboxMap.setOnMapClickListener(SymbolLayerActivity.this);
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/ZoomFunctionSymbolLayerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/ZoomFunctionSymbolLayerActivity.java
index abfd7ae529..180e2e726a 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/ZoomFunctionSymbolLayerActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/ZoomFunctionSymbolLayerActivity.java
@@ -2,24 +2,21 @@ package com.mapbox.mapboxsdk.testapp.activity.style;
import android.graphics.PointF;
import android.os.Bundle;
-import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
import com.google.gson.JsonObject;
-import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.geojson.Feature;
+import com.mapbox.geojson.FeatureCollection;
+import com.mapbox.geojson.Point;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.style.layers.Property;
import com.mapbox.mapboxsdk.style.layers.SymbolLayer;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.services.commons.geojson.Feature;
-import com.mapbox.services.commons.geojson.FeatureCollection;
-import com.mapbox.services.commons.geojson.Point;
-import com.mapbox.services.commons.models.Position;
+
import java.util.List;
@@ -64,14 +61,11 @@ public class ZoomFunctionSymbolLayerActivity extends AppCompatActivity {
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull final MapboxMap map) {
- mapboxMap = map;
- updateSource();
- addLayer();
- addMapClickListener();
- }
+ mapView.getMapAsync(map -> {
+ mapboxMap = map;
+ updateSource();
+ addLayer();
+ addMapClickListener();
});
}
@@ -93,15 +87,13 @@ public class ZoomFunctionSymbolLayerActivity extends AppCompatActivity {
}
private FeatureCollection createFeatureCollection() {
- Position position = isInitialPosition
- ? Position.fromCoordinates(-74.01618140, 40.701745)
- : Position.fromCoordinates(-73.988097, 40.749864);
+ Point point = isInitialPosition
+ ? Point.fromLngLat(-74.01618140, 40.701745)
+ : Point.fromLngLat(-73.988097, 40.749864);
- Point point = Point.fromCoordinates(position);
- Feature feature = Feature.fromGeometry(point);
JsonObject properties = new JsonObject();
properties.addProperty(KEY_PROPERTY_SELECTED, isSelected);
- feature.setProperties(properties);
+ Feature feature = Feature.fromGeometry(point, properties);
return FeatureCollection.fromFeatures(new Feature[] {feature});
}
@@ -131,19 +123,16 @@ public class ZoomFunctionSymbolLayerActivity extends AppCompatActivity {
}
private void addMapClickListener() {
- mapboxMap.setOnMapClickListener(new MapboxMap.OnMapClickListener() {
- @Override
- public void onMapClick(@NonNull LatLng point) {
- PointF screenPoint = mapboxMap.getProjection().toScreenLocation(point);
- List<Feature> featureList = mapboxMap.queryRenderedFeatures(screenPoint, LAYER_ID);
- if (!featureList.isEmpty()) {
- Feature feature = featureList.get(0);
- boolean selectedNow = feature.getBooleanProperty(KEY_PROPERTY_SELECTED);
- isSelected = !selectedNow;
- updateSource();
- } else {
- Timber.e("No features found");
- }
+ mapboxMap.setOnMapClickListener(point -> {
+ PointF screenPoint = mapboxMap.getProjection().toScreenLocation(point);
+ List<Feature> featureList = mapboxMap.queryRenderedFeatures(screenPoint, LAYER_ID);
+ if (!featureList.isEmpty()) {
+ Feature feature = featureList.get(0);
+ boolean selectedNow = feature.getBooleanProperty(KEY_PROPERTY_SELECTED);
+ isSelected = !selectedNow;
+ updateSource();
+ } else {
+ Timber.e("No features found");
}
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewAnimationActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewAnimationActivity.java
index dc95373663..1c023e5780 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewAnimationActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewAnimationActivity.java
@@ -11,7 +11,6 @@ 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.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
import java.util.Locale;
@@ -56,22 +55,19 @@ public class TextureViewAnimationActivity extends AppCompatActivity {
private void setupMapView(Bundle savedInstanceState) {
mapView = (MapView) findViewById(R.id.mapView);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(MapboxMap mapboxMap) {
- TextureViewAnimationActivity.this.mapboxMap = mapboxMap;
-
- setFpsView(mapboxMap);
-
- // Animate the map view
- ObjectAnimator animation = ObjectAnimator.ofFloat(mapView, "rotationY", 0.0f, 360f);
- animation.setDuration(3600);
- animation.setRepeatCount(ObjectAnimator.INFINITE);
- animation.start();
-
- // Start an animation on the map as well
- flyTo(mapboxMap, 0, 14);
- }
+ mapView.getMapAsync(mapboxMap -> {
+ TextureViewAnimationActivity.this.mapboxMap = mapboxMap;
+
+ setFpsView(mapboxMap);
+
+ // Animate the map view
+ ObjectAnimator animation = ObjectAnimator.ofFloat(mapView, "rotationY", 0.0f, 360f);
+ animation.setDuration(3600);
+ animation.setRepeatCount(ObjectAnimator.INFINITE);
+ animation.start();
+
+ // Start an animation on the map as well
+ flyTo(mapboxMap, 0, 14);
});
}
@@ -82,12 +78,9 @@ public class TextureViewAnimationActivity extends AppCompatActivity {
new MapboxMap.CancelableCallback() {
@Override
public void onCancel() {
- delayed = new Runnable() {
- @Override
- public void run() {
- delayed = null;
- flyTo(mapboxMap, place, zoom);
- }
+ delayed = () -> {
+ delayed = null;
+ flyTo(mapboxMap, place, zoom);
};
handler.postDelayed(delayed, 2000);
}
@@ -101,12 +94,7 @@ public class TextureViewAnimationActivity extends AppCompatActivity {
private void setFpsView(MapboxMap mapboxMap) {
final TextView fpsView = (TextView) findViewById(R.id.fpsView);
- mapboxMap.setOnFpsChangedListener(new MapboxMap.OnFpsChangedListener() {
- @Override
- public void onFpsChanged(double fps) {
- fpsView.setText(String.format(Locale.US, "FPS: %4.2f", fps));
- }
- });
+ mapboxMap.setOnFpsChangedListener(fps -> fpsView.setText(String.format(Locale.US, "FPS: %4.2f", fps)));
}
@Override
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewDebugModeActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewDebugModeActivity.java
index 87f78cf2c8..007e6c7f2e 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewDebugModeActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewDebugModeActivity.java
@@ -11,12 +11,10 @@ import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;
-import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.constants.Style;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
@@ -82,13 +80,10 @@ public class TextureViewDebugModeActivity extends AppCompatActivity implements O
private void setupMapView(Bundle savedInstanceState) {
mapView = (MapView) findViewById(R.id.mapView);
- mapView.addOnMapChangedListener(new MapView.OnMapChangedListener() {
- @Override
- public void onMapChanged(int change) {
- if (change == MapView.DID_FINISH_LOADING_STYLE && mapboxMap != null) {
- Timber.v("New style loaded with JSON: %s", mapboxMap.getStyleJson());
- setupNavigationView(mapboxMap.getLayers());
- }
+ mapView.addOnMapChangedListener(change -> {
+ if (change == MapView.DID_FINISH_LOADING_STYLE && mapboxMap != null) {
+ Timber.v("New style loaded with JSON: %s", mapboxMap.getStyleJson());
+ setupNavigationView(mapboxMap.getLayers());
}
});
@@ -110,25 +105,17 @@ public class TextureViewDebugModeActivity extends AppCompatActivity implements O
private void setFpsView() {
final TextView fpsView = (TextView) findViewById(R.id.fpsView);
- mapboxMap.setOnFpsChangedListener(new MapboxMap.OnFpsChangedListener() {
- @Override
- public void onFpsChanged(double fps) {
- fpsView.setText(String.format(Locale.US,"FPS: %4.2f", fps));
- }
- });
+ mapboxMap.setOnFpsChangedListener(fps -> fpsView.setText(String.format(Locale.US,"FPS: %4.2f", fps)));
}
private void setupNavigationView(List<Layer> layerList) {
final LayerListAdapter adapter = new LayerListAdapter(this, layerList);
ListView listView = (ListView) findViewById(R.id.listView);
listView.setAdapter(adapter);
- listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- Layer clickedLayer = adapter.getItem(position);
- toggleLayerVisibility(clickedLayer);
- closeNavigationView();
- }
+ listView.setOnItemClickListener((parent, view, position, id) -> {
+ Layer clickedLayer = adapter.getItem(position);
+ toggleLayerVisibility(clickedLayer);
+ closeNavigationView();
});
}
@@ -148,44 +135,29 @@ public class TextureViewDebugModeActivity extends AppCompatActivity implements O
private void setupZoomView() {
final TextView textView = (TextView) findViewById(R.id.textZoom);
- mapboxMap.setOnCameraChangeListener(new MapboxMap.OnCameraChangeListener() {
- @Override
- public void onCameraChange(CameraPosition position) {
- textView.setText(String.format(getString(R.string.debug_zoom), position.zoom));
- }
- });
+ mapboxMap.setOnCameraChangeListener(position ->
+ textView.setText(String.format(getString(R.string.debug_zoom), position.zoom)));
}
private void setupDebugChangeView() {
FloatingActionButton fabDebug = (FloatingActionButton) findViewById(R.id.fabDebug);
- fabDebug.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (mapboxMap != null) {
- Timber.d("Debug FAB: isDebug Active? %s", mapboxMap.isDebugActive());
- mapboxMap.cycleDebugOptions();
- }
+ fabDebug.setOnClickListener(view -> {
+ if (mapboxMap != null) {
+ Timber.d("Debug FAB: isDebug Active? %s", mapboxMap.isDebugActive());
+ mapboxMap.cycleDebugOptions();
}
});
}
private void setupStyleChangeView() {
FloatingActionButton fabStyles = (FloatingActionButton) findViewById(R.id.fabStyles);
- fabStyles.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (mapboxMap != null) {
- currentStyleIndex++;
- if (currentStyleIndex == STYLES.length) {
- currentStyleIndex = 0;
- }
- mapboxMap.setStyleUrl(STYLES[currentStyleIndex], new MapboxMap.OnStyleLoadedListener() {
- @Override
- public void onStyleLoaded(String style) {
- Timber.d("Style loaded %s", style);
- }
- });
+ fabStyles.setOnClickListener(view -> {
+ if (mapboxMap != null) {
+ currentStyleIndex++;
+ if (currentStyleIndex == STYLES.length) {
+ currentStyleIndex = 0;
}
+ mapboxMap.setStyleUrl(STYLES[currentStyleIndex], style -> Timber.d("Style loaded %s", style));
}
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewResizeActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewResizeActivity.java
index 388774e6c8..788003d867 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewResizeActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewResizeActivity.java
@@ -9,7 +9,6 @@ import android.view.View;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
/**
@@ -39,25 +38,17 @@ public class TextureViewResizeActivity extends AppCompatActivity {
private void setupMapView(Bundle savedInstanceState) {
mapView = (MapView) findViewById(R.id.mapView);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(MapboxMap mapboxMap) {
- TextureViewResizeActivity.this.mapboxMap = mapboxMap;
- }
- });
+ mapView.getMapAsync(mapboxMap -> TextureViewResizeActivity.this.mapboxMap = mapboxMap);
}
private void setupFab() {
FloatingActionButton fabDebug = (FloatingActionButton) findViewById(R.id.fabResize);
- fabDebug.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (mapView != null) {
- View parent = findViewById(R.id.coordinator_layout);
- int width = parent.getWidth() == mapView.getWidth() ? parent.getWidth() / 2 : parent.getWidth();
- int height = parent.getHeight() == mapView.getHeight() ? parent.getHeight() / 2 : parent.getHeight();
- mapView.setLayoutParams(new CoordinatorLayout.LayoutParams(width, height));
- }
+ fabDebug.setOnClickListener(view -> {
+ if (mapView != null) {
+ View parent = findViewById(R.id.coordinator_layout);
+ int width = parent.getWidth() == mapView.getWidth() ? parent.getWidth() / 2 : parent.getWidth();
+ int height = parent.getHeight() == mapView.getHeight() ? parent.getHeight() / 2 : parent.getHeight();
+ mapView.setLayoutParams(new CoordinatorLayout.LayoutParams(width, height));
}
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/BaseLocationActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/BaseLocationActivity.java
index 71b8115d2e..eec26cc9a7 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/BaseLocationActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/BaseLocationActivity.java
@@ -9,8 +9,8 @@ import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
-import com.mapbox.services.android.telemetry.permissions.PermissionsListener;
-import com.mapbox.services.android.telemetry.permissions.PermissionsManager;
+import com.mapbox.android.core.permissions.PermissionsListener;
+import com.mapbox.android.core.permissions.PermissionsManager;
import java.util.List;
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/CustomLocationEngineActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/CustomLocationEngineActivity.java
index 626f7c372d..ff2559089c 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/CustomLocationEngineActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/CustomLocationEngineActivity.java
@@ -4,12 +4,10 @@ import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.view.Menu;
import android.view.MenuItem;
-import android.view.View;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
/**
@@ -28,21 +26,15 @@ public class CustomLocationEngineActivity extends BaseLocationActivity {
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(MapboxMap map) {
- mapboxMap = map;
- mapboxMap.setLocationSource(MockLocationEngine.getInstance());
- }
+ mapView.getMapAsync(map -> {
+ mapboxMap = map;
+ mapboxMap.setLocationSource(MockLocationEngine.getInstance());
});
locationToggleFab = (FloatingActionButton) findViewById(R.id.fabLocationToggle);
- locationToggleFab.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (mapboxMap != null) {
- enableLocation(!mapboxMap.isMyLocationEnabled());
- }
+ locationToggleFab.setOnClickListener(view -> {
+ if (mapboxMap != null) {
+ enableLocation(!mapboxMap.isMyLocationEnabled());
}
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MockLocationEngine.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MockLocationEngine.java
index df52ccf727..f4fe710de1 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MockLocationEngine.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MockLocationEngine.java
@@ -5,8 +5,8 @@ import android.animation.TypeEvaluator;
import android.animation.ValueAnimator;
import android.location.Location;
-import com.mapbox.services.android.telemetry.location.LocationEngine;
-import com.mapbox.services.android.telemetry.location.LocationEngineListener;
+import com.mapbox.android.core.location.LocationEngine;
+import com.mapbox.android.core.location.LocationEngineListener;
import timber.log.Timber;
@@ -21,12 +21,9 @@ public class MockLocationEngine extends LocationEngine {
private static int counter;
MockLocationEngine(Location start, Location end) {
- locationAnimator = new LocationAnimator(start, end, new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- for (LocationEngineListener listener : locationListeners) {
- listener.onLocationChanged((Location) animation.getAnimatedValue());
- }
+ locationAnimator = new LocationAnimator(start, end, animation -> {
+ for (LocationEngineListener listener : locationListeners) {
+ listener.onLocationChanged((Location) animation.getAnimatedValue());
}
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationDrawableActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationDrawableActivity.java
index 62b4f4b987..f603050030 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationDrawableActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationDrawableActivity.java
@@ -14,9 +14,8 @@ import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.MapboxMapOptions;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.services.android.telemetry.location.LocationEngineListener;
+import com.mapbox.android.core.location.LocationEngineListener;
/**
* Test activity showcasing how to change the MyLocationView drawable.
@@ -50,12 +49,9 @@ public class MyLocationDrawableActivity extends BaseLocationActivity implements
parent.addView(mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(MapboxMap map) {
- mapboxMap = map;
- toggleGps(true);
- }
+ mapView.getMapAsync(map -> {
+ mapboxMap = map;
+ toggleGps(true);
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTintActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTintActivity.java
index 9dc8c1a607..ff3c4dfbc0 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTintActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTintActivity.java
@@ -15,11 +15,10 @@ import com.mapbox.mapboxsdk.constants.MyLocationTracking;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.maps.TrackingSettings;
import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings;
import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.services.android.telemetry.location.LocationEngineListener;
+import com.mapbox.android.core.location.LocationEngineListener;
/**
* Test activity showcasing how to tint the MyLocationView.
@@ -37,80 +36,65 @@ public class MyLocationTintActivity extends BaseLocationActivity implements Loca
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(MapboxMap map) {
- mapboxMap = map;
-
- // enable location updates
- toggleGps(true);
-
- // add some padding
- final MyLocationViewSettings myLocationViewSettings = mapboxMap.getMyLocationViewSettings();
- myLocationViewSettings.setPadding(0, 500, 0, 0);
-
- // enable tracking
- TrackingSettings settings = mapboxMap.getTrackingSettings();
- settings.setDismissLocationTrackingOnGesture(false);
- settings.setMyLocationTrackingMode(MyLocationTracking.TRACKING_FOLLOW);
-
- // handle default button clicks
- ViewUtils.attachClickListener(
- MyLocationTintActivity.this,
- R.id.default_user_dot_coloring_button,
- new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- myLocationViewSettings.setAccuracyTintColor(ContextCompat.getColor(
- MyLocationTintActivity.this, R.color.mapbox_blue));
- myLocationViewSettings.setForegroundTintColor(ContextCompat.getColor(
- MyLocationTintActivity.this, R.color.mapbox_blue));
- myLocationViewSettings.setBackgroundTintColor(Color.WHITE);
- }
- });
-
- // handle tint user dot button clicks
- ViewUtils.attachClickListener(
- MyLocationTintActivity.this,
- R.id.tint_user_dot_button,
- new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- myLocationViewSettings.setAccuracyTintColor(
- ContextCompat.getColor(MyLocationTintActivity.this, R.color.mapboxGreen));
- myLocationViewSettings.setForegroundTintColor(
- ContextCompat.getColor(MyLocationTintActivity.this, R.color.mapboxGreen));
- myLocationViewSettings.setBackgroundTintColor(Color.WHITE);
- }
- });
-
- // handle tint accuracy ring button clicks
- ViewUtils.attachClickListener(
- MyLocationTintActivity.this,
- R.id.user_accuracy_ring_tint_button,
- new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- myLocationViewSettings.setAccuracyTintColor(
- ContextCompat.getColor(MyLocationTintActivity.this, R.color.accent));
- myLocationViewSettings.setForegroundTintColor(
- ContextCompat.getColor(MyLocationTintActivity.this, R.color.mapbox_blue));
- myLocationViewSettings.setBackgroundTintColor(Color.WHITE);
- }
- });
-
- ViewUtils.attachClickListener(
- MyLocationTintActivity.this,
- R.id.user_dot_transparent_button,
- new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- myLocationViewSettings.setForegroundTintColor(Color.TRANSPARENT);
- myLocationViewSettings.setBackgroundTintColor(Color.TRANSPARENT);
- }
- }
- );
- }
+ mapView.getMapAsync(map -> {
+ mapboxMap = map;
+
+ // enable location updates
+ toggleGps(true);
+
+ // add some padding
+ final MyLocationViewSettings myLocationViewSettings = mapboxMap.getMyLocationViewSettings();
+ myLocationViewSettings.setPadding(0, 500, 0, 0);
+
+ // enable tracking
+ TrackingSettings settings = mapboxMap.getTrackingSettings();
+ settings.setDismissLocationTrackingOnGesture(false);
+ settings.setMyLocationTrackingMode(MyLocationTracking.TRACKING_FOLLOW);
+
+ // handle default button clicks
+ ViewUtils.attachClickListener(
+ MyLocationTintActivity.this,
+ R.id.default_user_dot_coloring_button,
+ view -> {
+ myLocationViewSettings.setAccuracyTintColor(ContextCompat.getColor(
+ MyLocationTintActivity.this, R.color.mapbox_blue));
+ myLocationViewSettings.setForegroundTintColor(ContextCompat.getColor(
+ MyLocationTintActivity.this, R.color.mapbox_blue));
+ myLocationViewSettings.setBackgroundTintColor(Color.WHITE);
+ });
+
+ // handle tint user dot button clicks
+ ViewUtils.attachClickListener(
+ MyLocationTintActivity.this,
+ R.id.tint_user_dot_button,
+ view -> {
+ myLocationViewSettings.setAccuracyTintColor(
+ ContextCompat.getColor(MyLocationTintActivity.this, R.color.mapboxGreen));
+ myLocationViewSettings.setForegroundTintColor(
+ ContextCompat.getColor(MyLocationTintActivity.this, R.color.mapboxGreen));
+ myLocationViewSettings.setBackgroundTintColor(Color.WHITE);
+ });
+
+ // handle tint accuracy ring button clicks
+ ViewUtils.attachClickListener(
+ MyLocationTintActivity.this,
+ R.id.user_accuracy_ring_tint_button,
+ view -> {
+ myLocationViewSettings.setAccuracyTintColor(
+ ContextCompat.getColor(MyLocationTintActivity.this, R.color.accent));
+ myLocationViewSettings.setForegroundTintColor(
+ ContextCompat.getColor(MyLocationTintActivity.this, R.color.mapbox_blue));
+ myLocationViewSettings.setBackgroundTintColor(Color.WHITE);
+ });
+
+ ViewUtils.attachClickListener(
+ MyLocationTintActivity.this,
+ R.id.user_dot_transparent_button,
+ view -> {
+ myLocationViewSettings.setForegroundTintColor(Color.TRANSPARENT);
+ myLocationViewSettings.setBackgroundTintColor(Color.TRANSPARENT);
+ }
+ );
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationToggleActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationToggleActivity.java
index 718c10c7cb..ea3a6f14bc 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationToggleActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationToggleActivity.java
@@ -2,11 +2,9 @@ package com.mapbox.mapboxsdk.testapp.activity.userlocation;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
-import android.view.View;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
import timber.log.Timber;
@@ -27,20 +25,12 @@ public class MyLocationToggleActivity extends BaseLocationActivity {
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(MapboxMap map) {
- mapboxMap = map;
- }
- });
+ mapView.getMapAsync(map -> mapboxMap = map);
locationToggleFab = (FloatingActionButton) findViewById(R.id.fabLocationToggle);
- locationToggleFab.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (mapboxMap != null) {
- toggleGps(!mapboxMap.isMyLocationEnabled());
- }
+ locationToggleFab.setOnClickListener(view -> {
+ if (mapboxMap != null) {
+ toggleGps(!mapboxMap.isMyLocationEnabled());
}
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTrackingModeActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTrackingModeActivity.java
index d910a19d5d..ffbb2c1a90 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTrackingModeActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTrackingModeActivity.java
@@ -25,7 +25,7 @@ import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.maps.TrackingSettings;
import com.mapbox.mapboxsdk.maps.UiSettings;
import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.services.android.telemetry.location.LocationEngineListener;
+import com.mapbox.android.core.location.LocationEngineListener;
import timber.log.Timber;
@@ -125,41 +125,35 @@ public class MyLocationTrackingModeActivity extends AppCompatActivity implements
bearingSpinner.setOnItemSelectedListener(MyLocationTrackingModeActivity.this);
setCheckBoxes();
- mapboxMap.setOnMyLocationTrackingModeChangeListener(new MapboxMap.OnMyLocationTrackingModeChangeListener() {
- @Override
- public void onMyLocationTrackingModeChange(@MyLocationTracking.Mode int myLocationTrackingMode) {
- locationSpinner.setOnItemSelectedListener(null);
- switch (myLocationTrackingMode) {
- case MyLocationTracking.TRACKING_NONE:
- locationSpinner.setSelection(TRACKING_NONE_INDEX);
- break;
- case MyLocationTracking.TRACKING_FOLLOW:
- locationSpinner.setSelection(TRACKING_FOLLOW_INDEX);
- break;
- }
- locationSpinner.setOnItemSelectedListener(MyLocationTrackingModeActivity.this);
+ mapboxMap.setOnMyLocationTrackingModeChangeListener(myLocationTrackingMode -> {
+ locationSpinner.setOnItemSelectedListener(null);
+ switch (myLocationTrackingMode) {
+ case MyLocationTracking.TRACKING_NONE:
+ locationSpinner.setSelection(TRACKING_NONE_INDEX);
+ break;
+ case MyLocationTracking.TRACKING_FOLLOW:
+ locationSpinner.setSelection(TRACKING_FOLLOW_INDEX);
+ break;
}
+ locationSpinner.setOnItemSelectedListener(MyLocationTrackingModeActivity.this);
});
- mapboxMap.setOnMyBearingTrackingModeChangeListener(new MapboxMap.OnMyBearingTrackingModeChangeListener() {
- @Override
- public void onMyBearingTrackingModeChange(@MyBearingTracking.Mode int myBearingTrackingMode) {
- bearingSpinner.setOnItemSelectedListener(null);
- switch (myBearingTrackingMode) {
- case MyBearingTracking.NONE:
- bearingSpinner.setSelection(BEARING_NONE_INDEX);
- break;
-
- case MyBearingTracking.GPS:
- bearingSpinner.setSelection(BEARING_GPS_INDEX);
- break;
-
- case MyBearingTracking.COMPASS:
- bearingSpinner.setSelection(BEARING_COMPASS_INDEX);
- break;
- }
- bearingSpinner.setOnItemSelectedListener(MyLocationTrackingModeActivity.this);
+ mapboxMap.setOnMyBearingTrackingModeChangeListener(myBearingTrackingMode -> {
+ bearingSpinner.setOnItemSelectedListener(null);
+ switch (myBearingTrackingMode) {
+ case MyBearingTracking.NONE:
+ bearingSpinner.setSelection(BEARING_NONE_INDEX);
+ break;
+
+ case MyBearingTracking.GPS:
+ bearingSpinner.setSelection(BEARING_GPS_INDEX);
+ break;
+
+ case MyBearingTracking.COMPASS:
+ bearingSpinner.setSelection(BEARING_COMPASS_INDEX);
+ break;
}
+ bearingSpinner.setOnItemSelectedListener(MyLocationTrackingModeActivity.this);
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/adapter/FeatureSectionAdapter.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/adapter/FeatureSectionAdapter.java
index 1d89f89f08..65e2b4f185 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/adapter/FeatureSectionAdapter.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/adapter/FeatureSectionAdapter.java
@@ -14,7 +14,6 @@ import android.widget.TextView;
import com.mapbox.mapboxsdk.testapp.utils.FontCache;
import java.util.Arrays;
-import java.util.Comparator;
public class FeatureSectionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
@@ -126,14 +125,9 @@ public class FeatureSectionAdapter extends RecyclerView.Adapter<RecyclerView.Vie
public void setSections(Section[] sections) {
this.sections.clear();
- Arrays.sort(sections, new Comparator<Section>() {
- @Override
- public int compare(Section section, Section section1) {
- return (section.firstPosition == section1.firstPosition)
- ? 0
- : ((section.firstPosition < section1.firstPosition) ? -1 : 1);
- }
- });
+ Arrays.sort(sections, (section, section1) -> (section.firstPosition == section1.firstPosition)
+ ? 0
+ : ((section.firstPosition < section1.firstPosition) ? -1 : 1));
int offset = 0;
for (Section section : sections) {
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/other/OfflineDownloadRegionDialog.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/other/OfflineDownloadRegionDialog.java
index c4aa934139..89096a0a6b 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/other/OfflineDownloadRegionDialog.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/other/OfflineDownloadRegionDialog.java
@@ -2,18 +2,16 @@ package com.mapbox.mapboxsdk.testapp.model.other;
import android.app.Activity;
import android.app.Dialog;
-import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
-
-import timber.log.Timber;
-
import android.widget.EditText;
import com.mapbox.mapboxsdk.testapp.R;
+import timber.log.Timber;
+
public class OfflineDownloadRegionDialog extends DialogFragment {
public interface DownloadRegionDialogListener {
@@ -39,18 +37,10 @@ public class OfflineDownloadRegionDialog extends DialogFragment {
builder.setTitle("Choose a name for the region")
.setIcon(R.drawable.ic_airplanemode_active_black)
.setView(regionNameEdit)
- .setPositiveButton("Start", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- String regionName = regionNameEdit.getText().toString();
- listener.onDownloadRegionDialogPositiveClick(regionName);
- }
- }).setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- Timber.d("Download cancelled.");
- }
- });
+ .setPositiveButton("Start", (dialog, which) -> {
+ String regionName = regionNameEdit.getText().toString();
+ listener.onDownloadRegionDialogPositiveClick(regionName);
+ }).setNegativeButton("Cancel", (dialog, which) -> Timber.d("Download cancelled."));
return builder.create();
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/other/OfflineListRegionsDialog.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/other/OfflineListRegionsDialog.java
index 76f07ba526..dbaae589ef 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/other/OfflineListRegionsDialog.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/other/OfflineListRegionsDialog.java
@@ -1,18 +1,17 @@
package com.mapbox.mapboxsdk.testapp.model.other;
import android.app.Dialog;
-import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
-import timber.log.Timber;
-
import com.mapbox.mapboxsdk.testapp.R;
import java.util.ArrayList;
+import timber.log.Timber;
+
public class OfflineListRegionsDialog extends DialogFragment {
public static final String ITEMS = "ITEMS";
@@ -29,18 +28,8 @@ public class OfflineListRegionsDialog extends DialogFragment {
builder.setTitle("List of offline regions")
.setIcon(R.drawable.ic_airplanemode_active_black)
- .setItems(items, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- Timber.d("Selected item: %s", which);
- }
- })
- .setPositiveButton("Accept", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- Timber.d("Dialog dismissed");
- }
- });
+ .setItems(items, (dialog, which) -> Timber.d("Selected item: %s", which))
+ .setPositiveButton("Accept", (dialog, which) -> Timber.d("Dialog dismissed"));
return builder.create();
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/FontCache.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/FontCache.java
index 10ecf43bd3..a4352d0b1c 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/FontCache.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/FontCache.java
@@ -3,10 +3,10 @@ package com.mapbox.mapboxsdk.testapp.utils;
import android.content.Context;
import android.graphics.Typeface;
-import timber.log.Timber;
-
import java.util.Hashtable;
+import timber.log.Timber;
+
public class FontCache {
private static Hashtable<String, Typeface> fontCache = new Hashtable<>();
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/GeoParseUtil.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/GeoParseUtil.java
index 0d21fd2c71..c21e479659 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/GeoParseUtil.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/GeoParseUtil.java
@@ -3,12 +3,11 @@ package com.mapbox.mapboxsdk.testapp.utils;
import android.content.Context;
import android.text.TextUtils;
+import com.mapbox.geojson.Feature;
+import com.mapbox.geojson.FeatureCollection;
+import com.mapbox.geojson.Point;
import com.mapbox.mapboxsdk.geometry.LatLng;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
@@ -29,34 +28,13 @@ public class GeoParseUtil {
return readAll(rd);
}
- public static List<LatLng> parseGeoJsonCoordinates(String geojsonStr) throws JSONException {
+ public static List<LatLng> parseGeoJsonCoordinates(String geojsonStr) {
List<LatLng> latLngs = new ArrayList<>();
- JSONObject jsonObject = new JSONObject(geojsonStr);
- JSONArray features = jsonObject.getJSONArray("features");
- int featureLength = features.length();
- for (int j = 0; j < featureLength; ++j) {
- JSONObject feature = features.getJSONObject(j);
- JSONObject geometry = feature.getJSONObject("geometry");
- String type = geometry.getString("type");
- JSONArray coordinates;
- if (type.equals("Polygon")) {
- coordinates = geometry.getJSONArray("coordinates").getJSONArray(0);
- } else {
- coordinates = geometry.getJSONArray("coordinates");
- }
- int len = coordinates.length();
- for (int i = 0; i < len; ++i) {
- if (coordinates.get(i) instanceof JSONArray) {
- JSONArray coord = coordinates.getJSONArray(i);
- double lng = coord.getDouble(0);
- double lat = coord.getDouble(1);
- latLngs.add(new LatLng(lat, lng));
- } else {
- double lng = coordinates.getDouble(0);
- double lat = coordinates.getDouble(1);
- latLngs.add(new LatLng(lat, lng));
- break;
- }
+ FeatureCollection featureCollection = FeatureCollection.fromJson(geojsonStr);
+ for (Feature feature : featureCollection.features()) {
+ if (feature.geometry() instanceof Point) {
+ Point point = (Point) feature.geometry();
+ latLngs.add(new LatLng(point.latitude(), point.longitude()));
}
}
return latLngs;
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/OfflineUtils.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/OfflineUtils.java
index 6220dc7e69..d4ec95ce5c 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/OfflineUtils.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/OfflineUtils.java
@@ -2,9 +2,10 @@ package com.mapbox.mapboxsdk.testapp.utils;
import android.support.annotation.NonNull;
-import timber.log.Timber;
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
-import org.json.JSONObject;
+import timber.log.Timber;
import static com.mapbox.mapboxsdk.testapp.activity.offline.OfflineActivity.JSON_CHARSET;
import static com.mapbox.mapboxsdk.testapp.activity.offline.OfflineActivity.JSON_FIELD_REGION_NAME;
@@ -14,24 +15,22 @@ public class OfflineUtils {
public static String convertRegionName(@NonNull byte[] metadata) {
try {
String json = new String(metadata, JSON_CHARSET);
- JSONObject jsonObject = new JSONObject(json);
- return jsonObject.getString(JSON_FIELD_REGION_NAME);
+ JsonObject jsonObject = new Gson().fromJson(json, JsonObject.class);
+ return jsonObject.get(JSON_FIELD_REGION_NAME).getAsString();
} catch (Exception exception) {
return null;
}
}
public static byte[] convertRegionName(String regionName) {
- byte[] metadata = null;
try {
- JSONObject jsonObject = new JSONObject();
- jsonObject.put(JSON_FIELD_REGION_NAME, regionName);
- String json = jsonObject.toString();
- metadata = json.getBytes(JSON_CHARSET);
+ JsonObject jsonObject = new JsonObject();
+ jsonObject.addProperty(JSON_FIELD_REGION_NAME, regionName);
+ return jsonObject.toString().getBytes(JSON_CHARSET);
} catch (Exception exception) {
Timber.e(exception, "Failed to encode metadata: ");
}
- return metadata;
+ return null;
}
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_data_driven_style.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_data_driven_style.xml
index 7454ce5860..2cbe20b47f 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_data_driven_style.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_data_driven_style.xml
@@ -9,4 +9,16 @@
android:layout_width="match_parent"
android:layout_height="match_parent"/>
+ <TextView
+ android:id="@+id/textZoom"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:layout_gravity="bottom|start"
+ android:layout_margin="8dp"
+ android:textIsSelectable="true"
+ android:textSize="14sp"/>
+
</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_fill_extrusion_layer.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_fill_extrusion_layer.xml
index 304841dc69..5672687ef8 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_fill_extrusion_layer.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_fill_extrusion_layer.xml
@@ -9,9 +9,11 @@
android:id="@id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
- app:mapbox_cameraTargetLat="52.090710"
- app:mapbox_cameraTargetLng="5.121125"
- app:mapbox_cameraZoom="10"
+ app:mapbox_cameraTargetLat="46.343350"
+ app:mapbox_cameraTargetLng="7.497989"
+ app:mapbox_cameraZoom="9"
+ app:mapbox_cameraBearing="140"
+ app:mapbox_cameraTilt="60"
app:mapbox_styleUrl="@string/mapbox_style_mapbox_streets"/>
</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_grid_source.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_grid_source.xml
new file mode 100644
index 0000000000..26b40b9ab6
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_grid_source.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <com.mapbox.mapboxsdk.maps.MapView
+ android:id="@id/mapView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:mapbox_cameraTargetLat="41.9567"
+ app:mapbox_cameraTargetLng="-78.6430"
+ app:mapbox_cameraZoom="5"
+ app:mapbox_styleUrl="@string/mapbox_style_mapbox_streets"/>
+
+</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_heatmaplayer.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_heatmaplayer.xml
new file mode 100644
index 0000000000..23ef9ea336
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_heatmaplayer.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <com.mapbox.mapboxsdk.maps.MapView
+ android:id="@id/mapView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:mapbox_styleUrl="@string/mapbox_style_dark"/>
+
+</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_hillshade_layer.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_hillshade_layer.xml
new file mode 100644
index 0000000000..a2410c547c
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_hillshade_layer.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <com.mapbox.mapboxsdk.maps.MapView
+ android:id="@id/mapView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:mapbox_cameraTargetLat="52.090710"
+ app:mapbox_cameraTargetLng="5.121125"
+ app:mapbox_cameraZoom="10"
+ app:mapbox_styleUrl="@string/mapbox_style_mapbox_streets"/>
+
+</RelativeLayout> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_local_glyph.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_local_glyph.xml
index 1f8bc93d2c..856dd24752 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_local_glyph.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_local_glyph.xml
@@ -12,6 +12,6 @@
android:id="@id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
- app:mapbox_localIdeographFontFamily="sans-serif" />
+ app:mapbox_localIdeographFontFamily="Droid Sans" />
</LinearLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_generator_symbol.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_generator_symbol.xml
index 168361a263..613bdd21ef 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_generator_symbol.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_generator_symbol.xml
@@ -5,4 +5,8 @@
android:id="@+id/menu_action_icon_overlap"
android:title="@string/menuitem_change_icon_overlap"
app:showAsAction="never"/>
+ <item
+ android:id="@+id/menu_action_filter"
+ android:title="@string/menuitem_filter"
+ app:showAsAction="never"/>
</menu>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/actions.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/actions.xml
index 5c0828ab74..e7d140d7d4 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/actions.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/actions.xml
@@ -11,6 +11,7 @@
<string name="menuitem_title_change_location_source_mock">Change to mock location source</string>
<string name="menuitem_title_change_location_source_null">Reset location source to null</string>
<string name="menuitem_change_icon_overlap">Toggle icon overlap</string>
+ <string name="menuitem_filter">Filter layer</string>
<string name="menuitem_change_location">Change location</string>
<string name="menuitem_title_accelerate_decelerate">Accelerate/Decelerate interpolator</string>
<string name="menuitem_title_bounce">Bounce interpolator</string>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml
index a13dd5d876..d17ec20546 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml
@@ -43,7 +43,7 @@
<string name="description_query_rendered_feature_properties_point">Query rendered feature properties on click</string>
<string name="description_query_rendered_features_box_count">Count all rendered features in box</string>
<string name="description_query_rendered_features_box_symbol_count">Count all rendered symbols in box</string>
- <string name="description_query_rendered_features_box_highlight">Hightligh buildings in box</string>
+ <string name="description_query_rendered_features_box_highlight">Highlight buildings in box</string>
<string name="description_query_source_features">Query source for features</string>
<string name="description_simple_map">Shows a simple map</string>
<string name="description_map_change">Logs map change events to Logcat</string>
@@ -67,5 +67,8 @@
<string name="description_textureview_debug">Use TextureView to render the map</string>
<string name="description_textureview_resize">Resize a map rendered on a TextureView</string>
<string name="description_textureview_animate">Animate a map rendered on a TextureView</string>
- <string name="description_local_glyph">Suzhou using local sans-serif for Chinese glyphs</string>
-</resources> \ No newline at end of file
+ <string name="description_grid_source">Example Custom Geometry Source</string>
+ <string name="description_local_glyph">Suzhou using Droid Sans for Chinese glyphs</string>
+ <string name="description_hillshade">Example raster-dem source and hillshade layer</string>
+ <string name="description_heatmaplayer">Use HeatmapLayer to visualise earthquakes</string>
+</resources>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml
index c4d13e1068..0323d7c428 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml
@@ -69,4 +69,6 @@
<string name="activity_textureview_animate">TextureView animation</string>
<string name="activity_grid_source">Grid Source</string>
<string name="activity_local_glyph">Local CJK glyph generation</string>
+ <string name="activity_hillshade">Hillshade</string>
+ <string name="activity_heatmaplayer">Heatmap layer</string>
</resources> \ No newline at end of file
diff --git a/platform/android/README.md b/platform/android/README.md
index 3787ff85fe..1dbdeee343 100644
--- a/platform/android/README.md
+++ b/platform/android/README.md
@@ -2,9 +2,9 @@
[![Circle CI build status](https://circleci.com/gh/mapbox/mapbox-gl-native.svg?style=shield)](https://circleci.com/gh/mapbox/workflows/mapbox-gl-native/tree/master)
-A library based on [Mapbox GL Native](../../README.md) for embedding interactive map views with scalable, customizable vector maps into Java applications on Android devices.
+A library based on [Mapbox GL Native](../../README.md) for embedding interactive map views with scalable, customizable vector maps onto Android devices.
-## Getting Started
+## Getting Started
Alright. So, actually, you may be in the wrong place. From here on in, this README is going to be for people who are interested in working on and improving on Mapbox GL Native for Android.
@@ -23,79 +23,92 @@ Alright. So, actually, you may be in the wrong place. From here on in, this READ
Clone the git repository
```bash
-git clone https://github.com/mapbox/mapbox-gl-native.git
-cd mapbox-gl-native
+git clone git@github.com:mapbox/mapbox-gl-native.git && cd mapbox-gl-native
```
#### Installing dependencies
These dependencies are required for all operating systems and all platform targets.
-- Latest stable [Android Studio](https://developer.android.com/studio/index.html)
-- Update Android SDK with latest
- - Android SDK Build-Tools
+- Latest stable [Android Studio](https://developer.android.com/studio/index.html)
+- Update the Mapbox Maps SDK for Android with the latest
+ - Android SDK Build-Tools
- Android Platform-Tools
- Android SDK Tools
- CMake
- NDK
- LLDB
-
- Modern C++ compiler that supports `-std=c++14`\*
- clang++ 3.5 or later or
- g++-4.9 or later
-- [cURL](https://curl.haxx.se) (for build only)
-- [Node.js](https://nodejs.org/) or later (for build only)
-- [pkg-config](https://wiki.freedesktop.org/www/Software/pkg-config/) (for build only)
+- [Node.js](https://nodejs.org/)
+ - make sure [npm](https://www.npmjs.com) is installed as well
+- [ccache](https://ccache.samba.org/) (optional)
**Note**: We partially support C++14 because GCC 4.9 does not fully implement the
final draft of the C++14 standard. More information in [DEVELOPING.md](DEVELOPING.md).
-##### Additional Dependencies for Linux
+**Note**: On macOS you can install clang with installing the [Apple command line developer tools](https://developer.apple.com/download/).
+
+### Opening the project
-_These instructions were tested on Ubuntu 16.04 LTS (aka Xenial Xerus)._
+#### macOS
+
+Execute the following command in this repository's root folder to generate the required build files and open the project with Android Studio:
```
-$ sudo apt-get install -y build-essential curl lib32stdc++6 lib32z1 pkg-config python
+make aproj
```
-##### Additional Dependencies for macOS
+#### linux
-- Apple Command Line Tools (available at [Apple Developer](https://developer.apple.com/download/more/))
-- [xcpretty](https://github.com/supermarin/xcpretty) (`gem install xcpretty`)
+run `make android-configuration` in the root folder of the project and open the Android Studio project in `/platform/android`.
+If you are using Arch Linux, install [ncurses5-compat-libs](https://aur.archlinux.org/packages/ncurses5-compat-libs).
-#### Open project in Android Studio
+### Project configuration
-##### macOS
+#### Setup Checkstyle
-Execute the following to generate the required build files and open the project with Android Studio:
+Mapbox uses specific IDE settings related to code and check style.
+See [checkstyle guide](https://github.com/mapbox/mapbox-gl-native/wiki/Setting-up-Mapbox-checkstyle) for configuration details.
-```
-make aproj
-```
+##### Setting Mapbox Access Token
-##### linux
+_The test application (used for development purposes) uses Mapbox vector tiles, which require a Mapbox account and API access token. Obtain a free access token on the [Mapbox account page](https://www.mapbox.com/studio/account/tokens/)._
-Open Android Studio project in `/platform/android`, run `make android-configuration` in the root folder of the project.
+With the first gradle invocation, gradle will take the value of the `MAPBOX_ACCESS_TOKEN` environment variable and save it to `MapboxGLAndroidSDKTestApp/src/main/res/values/developer-config.xml`. If the environment variable wasn't set, you can edit `developer-config.xml` manually and add your access token to the `mapbox_access_token` resource.
-##### Setup Checkstyle
+### Running project
-Mapbox uses specific IDE settings related to code and check style.
-See [checkstyle guide](https://github.com/mapbox/mapbox-gl-native/wiki/Setting-up-Mapbox-checkstyle) for configuration details.
+Run the configuration for the `MapboxGLAndroidSDKTestApp` module and select a device or emulator to deploy on. Based on the selected device, the c++ code will be compiled for the related processor architecture. You can see the project compiling in the `View > Tool Windows > Gradle Console`.
-##### Setting Mapbox Access Token
+More information about building and distributing this project in [DISTRIBUTE.md](https://github.com/mapbox/mapbox-gl-native/blob/master/platform/android/DISTRIBUTE.md).
-_The test application (used for development purposes) uses Mapbox vector tiles, which require a Mapbox account and API access token. Obtain a free access token on the [Mapbox account page](https://www.mapbox.com/studio/account/tokens/)._
+### Additional resources
-With the first gradle invocation, gradle will take the value of the `MAPBOX_ACCESS_TOKEN` environment variable and save it to `MapboxGLAndroidSDKTestApp/src/main/res/values/developer-config.xml`. If the environement variable wasn't set, you can edit `developer-config.xml` manually and add your access token to the `mapbox_access_token` resource.
+#### Using the SDK snapshot
-#### Running project
+Instead of using the latest stable release of the Maps SDK for Android, you can use a "snapshot" or the beta version if there is one available. Our snapshots are built every time a Github pull request adds code to this repository's `master` branch. If you'd like to use a snapshot build, your Android project's gradle file should have -SNAPSHOT appended to the SDK version number. For example `5.2.0-SNAPSHOT` or:
-Run the configuration for the `MapboxGLAndroidSDKTestApp` module and select a device or emulator to deploy on. Based on the selected device, the c++ code will be compiled for the related processor architecture. You can see the project compiling in the `View > Tool Windows > Gradle Console`.
+```java
+// Mapbox SDK dependency
+compile('com.mapbox.mapboxsdk:mapbox-android-sdk:5.2.0-SNAPSHOT@aar') {
+ transitive = true
+}
+```
+You need to have the section below in your build.gradle root folder to be able to resolve the SNAPSHOT dependencies:
+```
+allprojects {
+ repositories {
+ jcenter()
+ maven { url "http://oss.sonatype.org/content/repositories/snapshots/" }
+ }
+}
+```
-More information about building and distributing this project in [DISTRIBUTE.md][https://github.com/mapbox/mapbox-gl-native/blob/master/platform/android/DISTRIBUTE.md].
#### Symbolicating native crashes
-When hitting native crashes you can use ndk-stack to symbolicate crashes.
-More information in [this](https://github.com/mapbox/mapbox-gl-native/wiki/Getting-line-numbers-from-an-Android-crash-with-ndk-stack) guide. \ No newline at end of file
+When hitting native crashes you can use ndk-stack to symbolicate crashes.
+More information in [this](https://github.com/mapbox/mapbox-gl-native/wiki/Getting-line-numbers-from-an-Android-crash-with-ndk-stack) guide.
diff --git a/platform/android/build.gradle b/platform/android/build.gradle
index f3301d0b5a..6cd9505447 100644
--- a/platform/android/build.gradle
+++ b/platform/android/build.gradle
@@ -1,26 +1,29 @@
buildscript {
+
repositories {
jcenter()
+ google()
}
dependencies {
- classpath 'com.android.tools.build:gradle:2.3.1'
+ classpath 'com.android.tools.build:gradle:3.0.1'
}
}
allprojects {
repositories {
+ mavenCentral()
jcenter()
- maven { url 'https://maven.google.com' }
+ google()
maven { url "http://oss.sonatype.org/content/repositories/snapshots/" }
}
}
-task wrapper(type: Wrapper) {
- gradleVersion = '3.2.1'
+subprojects {
+ apply from: "${rootDir}/gradle/dependencies.gradle"
}
-apply from: rootProject.file('dependencies.gradle')
+apply from: "${rootDir}/gradle/dependencies.gradle"
// Load build system information. If this file does not exist, run
-// `make platform/android/configuration.gradle`
-apply from: rootProject.file('configuration.gradle')
+// `make platform/android/gradle/configuration.gradle`
+apply from: "${rootDir}/gradle/configuration.gradle"
diff --git a/platform/android/config.cmake b/platform/android/config.cmake
index 34a8963042..30182bbc06 100644
--- a/platform/android/config.cmake
+++ b/platform/android/config.cmake
@@ -1,5 +1,4 @@
add_definitions(-DMBGL_USE_GLES2=1)
-
include(cmake/test-files.cmake)
# Build thin archives.
@@ -8,6 +7,9 @@ set(CMAKE_C_ARCHIVE_CREATE "<CMAKE_AR> cruT <TARGET> <LINK_FLAGS> <OBJECTS>")
set(CMAKE_CXX_ARCHIVE_APPEND "<CMAKE_AR> ruT <TARGET> <LINK_FLAGS> <OBJECTS>")
set(CMAKE_C_ARCHIVE_APPEND "<CMAKE_AR> ruT <TARGET> <LINK_FLAGS> <OBJECTS>")
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffunction-sections -fdata-sections")
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ffunction-sections -fdata-sections")
+
if ((ANDROID_ABI STREQUAL "armeabi") OR (ANDROID_ABI STREQUAL "armeabi-v7a") OR (ANDROID_ABI STREQUAL "arm64-v8a") OR
(ANDROID_ABI STREQUAL "x86") OR (ANDROID_ABI STREQUAL "x86_64"))
# Use Identical Code Folding on platforms that support the gold linker.
@@ -15,6 +17,9 @@ if ((ANDROID_ABI STREQUAL "armeabi") OR (ANDROID_ABI STREQUAL "armeabi-v7a") OR
set(CMAKE_SHARED_LINKER_FLAGS "-fuse-ld=gold -Wl,--icf=safe ${CMAKE_SHARED_LINKER_FLAGS}")
endif()
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections -Wl,--version-script=${CMAKE_SOURCE_DIR}/platform/android/version-script")
+set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections -Wl,--version-script=${CMAKE_SOURCE_DIR}/platform/android/version-script")
+
mason_use(jni.hpp VERSION 3.0.0 HEADER_ONLY)
mason_use(nunicode VERSION 1.7.1)
mason_use(sqlite VERSION 3.14.2)
@@ -69,11 +74,11 @@ macro(mbgl_platform_core)
PRIVATE platform/default/mbgl/map/map_snapshotter.cpp
PRIVATE platform/default/mbgl/map/map_snapshotter.hpp
PRIVATE platform/linux/src/headless_backend_egl.cpp
- PRIVATE platform/linux/src/headless_display_egl.cpp
)
target_include_directories(mbgl-core
PUBLIC platform/default
+ PRIVATE platform/android
)
target_add_mason_package(mbgl-core PUBLIC nunicode)
@@ -82,12 +87,6 @@ macro(mbgl_platform_core)
target_add_mason_package(mbgl-core PUBLIC rapidjson)
target_add_mason_package(mbgl-core PRIVATE icu)
- target_compile_options(mbgl-core
- PRIVATE -fvisibility=hidden
- PRIVATE -ffunction-sections
- PRIVATE -fdata-sections
- )
-
target_link_libraries(mbgl-core
PUBLIC -llog
PUBLIC -landroid
@@ -116,12 +115,6 @@ macro(mbgl_filesource)
target_add_mason_package(mbgl-filesource PUBLIC sqlite)
target_add_mason_package(mbgl-filesource PUBLIC jni.hpp)
- target_compile_options(mbgl-filesource
- PRIVATE -fvisibility=hidden
- PRIVATE -ffunction-sections
- PRIVATE -fdata-sections
- )
-
target_link_libraries(mbgl-filesource
PUBLIC -llog
PUBLIC -landroid
@@ -148,7 +141,6 @@ add_library(mbgl-android STATIC
# Style conversion Java -> C++
platform/android/src/style/android_conversion.hpp
- platform/android/src/style/conversion/geojson.hpp
platform/android/src/style/value.cpp
platform/android/src/style/value.hpp
platform/android/src/style/conversion/url_or_tileset.hpp
@@ -166,6 +158,10 @@ add_library(mbgl-android STATIC
platform/android/src/style/layers/fill_extrusion_layer.hpp
platform/android/src/style/layers/fill_layer.cpp
platform/android/src/style/layers/fill_layer.hpp
+ platform/android/src/style/layers/heatmap_layer.cpp
+ platform/android/src/style/layers/heatmap_layer.hpp
+ platform/android/src/style/layers/hillshade_layer.cpp
+ platform/android/src/style/layers/hillshade_layer.hpp
platform/android/src/style/layers/layer.cpp
platform/android/src/style/layers/layer.hpp
platform/android/src/style/layers/layers.cpp
@@ -180,10 +176,10 @@ add_library(mbgl-android STATIC
platform/android/src/style/layers/unknown_layer.hpp
platform/android/src/style/sources/geojson_source.cpp
platform/android/src/style/sources/geojson_source.hpp
+ platform/android/src/style/sources/custom_geometry_source.cpp
+ platform/android/src/style/sources/custom_geometry_source.hpp
platform/android/src/style/sources/source.cpp
platform/android/src/style/sources/source.hpp
- platform/android/src/style/sources/sources.cpp
- platform/android/src/style/sources/sources.hpp
platform/android/src/style/sources/raster_source.cpp
platform/android/src/style/sources/raster_source.hpp
platform/android/src/style/sources/unknown_source.cpp
@@ -192,6 +188,8 @@ add_library(mbgl-android STATIC
platform/android/src/style/sources/vector_source.hpp
platform/android/src/style/sources/image_source.hpp
platform/android/src/style/sources/image_source.cpp
+ platform/android/src/style/sources/raster_dem_source.cpp
+ platform/android/src/style/sources/raster_dem_source.hpp
platform/android/src/style/functions/stop.cpp
platform/android/src/style/functions/stop.hpp
platform/android/src/style/functions/categorical_stops.cpp
@@ -252,8 +250,6 @@ add_library(mbgl-android STATIC
platform/android/src/geojson/point.hpp
platform/android/src/geojson/polygon.cpp
platform/android/src/geojson/polygon.hpp
- platform/android/src/geojson/position.cpp
- platform/android/src/geojson/position.hpp
# Geometry
platform/android/src/geometry/lat_lng.cpp
@@ -312,12 +308,6 @@ add_library(mbgl-android STATIC
platform/android/src/jni.cpp
)
-target_compile_options(mbgl-android
- PRIVATE -fvisibility=hidden
- PRIVATE -ffunction-sections
- PRIVATE -fdata-sections
-)
-
target_link_libraries(mbgl-android
PUBLIC mbgl-filesource
PUBLIC mbgl-core
@@ -331,8 +321,6 @@ add_library(mapbox-gl SHARED
target_link_libraries(mapbox-gl
PRIVATE mbgl-android
- PRIVATE -Wl,--gc-sections
- PRIVATE -Wl,--version-script=${CMAKE_SOURCE_DIR}/platform/android/version-script
)
## Test library ##
@@ -344,25 +332,14 @@ macro(mbgl_platform_test)
# Main test entry point
platform/android/src/test/main.jni.cpp
-
- # Headless view
- platform/default/mbgl/gl/headless_frontend.cpp
- platform/default/mbgl/gl/headless_frontend.hpp
- platform/default/mbgl/gl/headless_backend.cpp
- platform/default/mbgl/gl/headless_backend.hpp
-
- platform/linux/src/headless_backend_egl.cpp
- platform/linux/src/headless_display_egl.cpp
)
- target_compile_options(mbgl-test
- PRIVATE -fvisibility=hidden
+ target_include_directories(mbgl-test
+ PRIVATE platform/android
)
target_link_libraries(mbgl-test
PRIVATE mbgl-android
- PRIVATE -Wl,--gc-sections
- PRIVATE -Wl,--version-script=${CMAKE_SOURCE_DIR}/platform/android/version-script
)
endmacro()
@@ -372,14 +349,6 @@ add_library(example-custom-layer SHARED
platform/android/src/example_custom_layer.cpp
)
-target_compile_options(example-custom-layer
- PRIVATE -fvisibility=hidden
- PRIVATE -ffunction-sections
- PRIVATE -fdata-sections
-)
-
target_link_libraries(example-custom-layer
PRIVATE mbgl-core
- PRIVATE -Wl,--gc-sections
- PRIVATE -Wl,--version-script=${CMAKE_SOURCE_DIR}/platform/android/version-script
)
diff --git a/platform/android/dependencies.gradle b/platform/android/dependencies.gradle
deleted file mode 100644
index 541b3c5236..0000000000
--- a/platform/android/dependencies.gradle
+++ /dev/null
@@ -1,49 +0,0 @@
-ext {
- minSdkVersion = 14
- targetSdkVersion = 25
- compileSdkVersion = 25
- buildToolsVersion = "25.0.2"
-
- versionCode = 11
- versionName = "5.0.0"
-
- mapboxServicesVersion = "2.2.9"
- supportLibVersion = "25.4.0"
- espressoVersion = '3.0.1'
- testRunnerVersion = '1.0.1'
- leakCanaryVersion = '1.5.1'
-
- dep = [
- // mapbox
- mapboxJavaServices : "com.mapbox.mapboxsdk:mapbox-java-services:${mapboxServicesVersion}@jar",
- mapboxJavaGeoJSON : "com.mapbox.mapboxsdk:mapbox-java-geojson:${mapboxServicesVersion}@jar",
- mapboxAndroidTelemetry : "com.mapbox.mapboxsdk:mapbox-android-telemetry:${mapboxServicesVersion}@aar",
-
- // mapzen lost
- lost : 'com.mapzen.android:lost:3.0.4',
-
- // unit test
- junit : 'junit:junit:4.12',
- mockito : 'org.mockito:mockito-core:2.10.0',
- robolectric : 'org.robolectric:robolectric:3.5.1',
-
- // instrumentation test
- testRunner : "com.android.support.test:runner:${testRunnerVersion}",
- testRules : "com.android.support.test:rules:${testRunnerVersion}",
- testEspressoCore : "com.android.support.test.espresso:espresso-core:${espressoVersion}",
- testEspressoIntents : "com.android.support.test.espresso:espresso-intents:${espressoVersion}",
-
- // support
- supportAnnotations : "com.android.support:support-annotations:${supportLibVersion}",
- supportAppcompatV7 : "com.android.support:appcompat-v7:${supportLibVersion}",
- supportFragmentV4 : "com.android.support:support-fragment:${supportLibVersion}",
- supportDesign : "com.android.support:design:${supportLibVersion}",
- supportRecyclerView : "com.android.support:recyclerview-v7:${supportLibVersion}",
-
- // square crew
- timber : 'com.jakewharton.timber:timber:4.5.1',
- okhttp3 : 'com.squareup.okhttp3:okhttp:3.9.1',
- leakCanaryDebug : "com.squareup.leakcanary:leakcanary-android:${leakCanaryVersion}",
- leakCanaryRelease : "com.squareup.leakcanary:leakcanary-android-no-op:${leakCanaryVersion}"
- ]
-} \ No newline at end of file
diff --git a/platform/android/gradle/dependencies.gradle b/platform/android/gradle/dependencies.gradle
new file mode 100644
index 0000000000..4ef4ae2f7d
--- /dev/null
+++ b/platform/android/gradle/dependencies.gradle
@@ -0,0 +1,55 @@
+ext {
+
+ androidVersions = [
+ minSdkVersion : 14,
+ targetSdkVersion : 25,
+ compileSdkVersion: 25,
+ buildToolsVersion: '26.0.3'
+ ]
+
+ versions = [
+ mapboxServices: '3.0.0-beta.2',
+ mapboxTelemetry: '3.0.0-beta.1',
+ supportLib : '25.4.0',
+ espresso : '3.0.1',
+ testRunner : '1.0.1',
+ leakCanary : '1.5.1',
+ lost : '3.0.4',
+ junit : '4.12',
+ mockito : '2.10.0',
+ robolectric : '3.5.1',
+ timber : '4.5.1',
+ okhttp : '3.9.1'
+ ]
+
+ 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}",
+
+ // for testApp
+ mapboxJavaTurf : "com.mapbox.mapboxsdk:mapbox-sdk-turf:${versions.mapboxServices}",
+
+ junit : "junit:junit:${versions.junit}",
+ mockito : "org.mockito:mockito-core:${versions.mockito}",
+ robolectric : "org.robolectric:robolectric:${versions.robolectric}",
+
+ testRunner : "com.android.support.test:runner:${versions.testRunner}",
+ testRules : "com.android.support.test:rules:${versions.testRunner}",
+ testEspressoCore : "com.android.support.test.espresso:espresso-core:${versions.espresso}",
+ testEspressoIntents : "com.android.support.test.espresso:espresso-intents:${versions.espresso}",
+
+ supportAnnotations : "com.android.support:support-annotations:${versions.supportLib}",
+ supportAppcompatV7 : "com.android.support:appcompat-v7:${versions.supportLib}",
+ supportFragmentV4 : "com.android.support:support-fragment:${versions.supportLib}",
+ supportDesign : "com.android.support:design:${versions.supportLib}",
+ supportRecyclerView : "com.android.support:recyclerview-v7:${versions.supportLib}",
+
+ lost : "com.mapzen.android:lost:${versions.lost}",
+ gmsLocation : 'com.google.android.gms:play-services-location:11.0.4',
+ timber : "com.jakewharton.timber:timber:${versions.timber}",
+ okhttp3 : "com.squareup.okhttp3:okhttp:${versions.okhttp}",
+ leakCanaryDebug : "com.squareup.leakcanary:leakcanary-android:${versions.leakCanary}",
+ leakCanaryRelease : "com.squareup.leakcanary:leakcanary-android-no-op:${versions.leakCanary}"
+ ]
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/gradle-checkstyle.gradle b/platform/android/gradle/gradle-checkstyle.gradle
index e0bc076d3d..41a68f90ce 100644
--- a/platform/android/MapboxGLAndroidSDK/gradle-checkstyle.gradle
+++ b/platform/android/gradle/gradle-checkstyle.gradle
@@ -12,10 +12,13 @@ task checkstyle(type: Checkstyle) {
source 'src'
include '**/*.java'
exclude '**/gen/**'
+ exclude '**/style/*LayerTest.java'
+ exclude '**/style/LightTest.java'
exclude '**/style/layers/Property.java'
exclude '**/style/layers/PropertyFactory.java'
exclude '**/style/layers/*Layer.java'
exclude '**/style/light/Light.java'
+ exclude '**/Expression.java' // allowing single character signature as e()
classpath = files()
ignoreFailures = false
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/gradle-config.gradle b/platform/android/gradle/gradle-config.gradle
index 8346806633..8346806633 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/gradle-config.gradle
+++ b/platform/android/gradle/gradle-config.gradle
diff --git a/platform/android/MapboxGLAndroidSDK/gradle-javadoc.gradle b/platform/android/gradle/gradle-javadoc.gradle
index eeb5e9b4d6..39372b4378 100644
--- a/platform/android/MapboxGLAndroidSDK/gradle-javadoc.gradle
+++ b/platform/android/gradle/gradle-javadoc.gradle
@@ -10,7 +10,7 @@ android.libraryVariants.all { variant ->
options.windowTitle("Mapbox Android SDK $VERSION_NAME Reference")
options.docTitle("Mapbox Android SDK $VERSION_NAME")
options.header("Mapbox Android SDK $VERSION_NAME Reference")
- options.bottom("&copy; 2015&ndash;2017 Mapbox. All rights reserved.")
+ options.bottom("&copy; 2015&ndash;2018 Mapbox. All rights reserved.")
options.links("http://docs.oracle.com/javase/7/docs/api/")
options.linksOffline("http://d.android.com/reference/", "$System.env.ANDROID_HOME/docs/reference")
options.overview("src/main/java/overview.html")
diff --git a/platform/android/gradle-lint.gradle b/platform/android/gradle/gradle-lint.gradle
index cbebeaa74a..cbebeaa74a 100644
--- a/platform/android/gradle-lint.gradle
+++ b/platform/android/gradle/gradle-lint.gradle
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/gradle-make.gradle b/platform/android/gradle/gradle-make.gradle
index 65d971cc96..65d971cc96 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/gradle-make.gradle
+++ b/platform/android/gradle/gradle-make.gradle
diff --git a/platform/android/MapboxGLAndroidSDK/gradle-publish.gradle b/platform/android/gradle/gradle-publish.gradle
index 9805763a99..9805763a99 100644
--- a/platform/android/MapboxGLAndroidSDK/gradle-publish.gradle
+++ b/platform/android/gradle/gradle-publish.gradle
diff --git a/platform/android/MapboxGLAndroidSDK/gradle-tests-staticblockremover.gradle b/platform/android/gradle/gradle-tests-staticblockremover.gradle
index 523dc99dd1..523dc99dd1 100644
--- a/platform/android/MapboxGLAndroidSDK/gradle-tests-staticblockremover.gradle
+++ b/platform/android/gradle/gradle-tests-staticblockremover.gradle
diff --git a/platform/android/gradle/wrapper/gradle-wrapper.properties b/platform/android/gradle/wrapper/gradle-wrapper.properties
index 1d35abd7b2..bf1b63c346 100644
--- a/platform/android/gradle/wrapper/gradle-wrapper.properties
+++ b/platform/android/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,5 @@
-#Fri Mar 03 10:22:19 EST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip \ No newline at end of file
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
diff --git a/platform/android/mbgl/gl/gl_impl.hpp b/platform/android/mbgl/gl/gl_impl.hpp
new file mode 100644
index 0000000000..b9b5d8e315
--- /dev/null
+++ b/platform/android/mbgl/gl/gl_impl.hpp
@@ -0,0 +1,5 @@
+#pragma once
+
+#define GL_GLEXT_PROTOTYPES
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
diff --git a/platform/android/scripts/exclude-activity-gen.json b/platform/android/scripts/exclude-activity-gen.json
index f05001c6ae..c1a6b5bb48 100644
--- a/platform/android/scripts/exclude-activity-gen.json
+++ b/platform/android/scripts/exclude-activity-gen.json
@@ -27,5 +27,10 @@
"QueryRenderedFeaturesBoxHighlightActivity",
"MultiMapActivity",
"MapInDialogActivity",
- "SimpleMapActivity"
+ "SimpleMapActivity",
+ "ManualZoomActivity",
+ "MaxMinZoomActivity",
+ "ScrollByActivity",
+ "ZoomFunctionSymbolLayerActivity",
+ "SymbolGeneratorActivity"
] \ No newline at end of file
diff --git a/platform/android/scripts/generate-style-code.js b/platform/android/scripts/generate-style-code.js
index abc0796bc1..6e6d3cfa67 100644..100755
--- a/platform/android/scripts/generate-style-code.js
+++ b/platform/android/scripts/generate-style-code.js
@@ -1,8 +1,9 @@
+#!/usr/bin/env node
'use strict';
const fs = require('fs');
const ejs = require('ejs');
-const spec = require('../../../mapbox-gl-js/src/style-spec/reference/v8');
+const spec = require('../../../scripts/style-spec');
const _ = require('lodash');
require('../../../scripts/style-code');
@@ -27,6 +28,9 @@ var layers = Object.keys(spec.layer.type.values).map((type) => {
}, []);
const paintProperties = Object.keys(spec[`paint_${type}`]).reduce((memo, name) => {
+ // disabled for now, see https://github.com/mapbox/mapbox-gl-native/issues/11172
+ if (name === 'heatmap-color') return memo;
+
spec[`paint_${type}`][name].name = name;
memo.push(spec[`paint_${type}`][name]);
return memo;
diff --git a/platform/android/src/android_renderer_backend.cpp b/platform/android/src/android_renderer_backend.cpp
index 9e49915650..ae35acc5da 100755
--- a/platform/android/src/android_renderer_backend.cpp
+++ b/platform/android/src/android_renderer_backend.cpp
@@ -21,7 +21,7 @@ void AndroidRendererBackend::bind() {
/**
* From mbgl::RendererBackend.
*/
-gl::ProcAddress AndroidRendererBackend::initializeExtension(const char* name) {
+gl::ProcAddress AndroidRendererBackend::getExtensionFunctionPointer(const char* name) {
assert(BackendScope::exists());
return eglGetProcAddress(name);
}
diff --git a/platform/android/src/android_renderer_backend.hpp b/platform/android/src/android_renderer_backend.hpp
index c5c552459f..d2c100dcc1 100755
--- a/platform/android/src/android_renderer_backend.hpp
+++ b/platform/android/src/android_renderer_backend.hpp
@@ -24,7 +24,7 @@ public:
protected:
// mbgl::RendererBackend //
- gl::ProcAddress initializeExtension(const char*) override;
+ gl::ProcAddress getExtensionFunctionPointer(const char*) override;
void activate() override {};
void deactivate() override {};
diff --git a/platform/android/src/android_renderer_frontend.cpp b/platform/android/src/android_renderer_frontend.cpp
index afdb08a10e..2a03d9de9e 100644
--- a/platform/android/src/android_renderer_frontend.cpp
+++ b/platform/android/src/android_renderer_frontend.cpp
@@ -77,8 +77,8 @@ void AndroidRendererFrontend::update(std::shared_ptr<UpdateParameters> params) {
mapRenderer.requestRender();
}
-void AndroidRendererFrontend::onLowMemory() {
- mapRenderer.actor().invoke(&Renderer::onLowMemory);
+void AndroidRendererFrontend::reduceMemoryUse() {
+ mapRenderer.actor().invoke(&Renderer::reduceMemoryUse);
}
std::vector<Feature> AndroidRendererFrontend::querySourceFeatures(const std::string& sourceID,
diff --git a/platform/android/src/android_renderer_frontend.hpp b/platform/android/src/android_renderer_frontend.hpp
index 178870c452..b61904e388 100644
--- a/platform/android/src/android_renderer_frontend.hpp
+++ b/platform/android/src/android_renderer_frontend.hpp
@@ -39,7 +39,7 @@ public:
AnnotationIDs queryShapeAnnotations(const ScreenBox& box) const;
// Memory
- void onLowMemory();
+ void reduceMemoryUse();
private:
MapRenderer& mapRenderer;
diff --git a/platform/android/src/example_custom_layer.cpp b/platform/android/src/example_custom_layer.cpp
index 1ed68d0835..6d0bd4de4b 100644
--- a/platform/android/src/example_custom_layer.cpp
+++ b/platform/android/src/example_custom_layer.cpp
@@ -96,7 +96,7 @@ void nativeContextLost(void */*context*/) {
mbgl::Log::Info(mbgl::Event::General, "nativeContextLost");
}
-void nativeDenitialize(void *context) {
+void nativeDeinitialize(void *context) {
mbgl::Log::Info(mbgl::Event::General, "nativeDeinitialize");
delete reinterpret_cast<ExampleCustomLayer*>(context);
}
@@ -132,7 +132,7 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
env->SetStaticLongField(customLayerClass,
env->GetStaticFieldID(customLayerClass, "DeinitializeFunction", "J"),
- reinterpret_cast<jlong>(nativeDenitialize));
+ reinterpret_cast<jlong>(nativeDeinitialize));
return JNI_VERSION_1_6;
}
diff --git a/platform/android/src/geojson/conversion/geometry.hpp b/platform/android/src/geojson/conversion/geometry.hpp
index 2ca63e2c11..5d2aab4c2d 100644
--- a/platform/android/src/geojson/conversion/geometry.hpp
+++ b/platform/android/src/geojson/conversion/geometry.hpp
@@ -21,88 +21,91 @@ public:
jni::JNIEnv& env;
/**
- * Point (double[])
+ * static Point fromLngLat(double longitude,double latitude)
*/
jni::jobject* operator()(const mapbox::geometry::point<T> &geometry) const {
- static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/Point")).release();
- static jni::jmethodID* fromCoordinates = &jni::GetStaticMethodID(env, *javaClass, "fromCoordinates", "([D)Lcom/mapbox/services/commons/geojson/Point;");
+ static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/geojson/Point")).release();
+ static jni::jmethodID* fromLngLat = &jni::GetStaticMethodID(env, *javaClass, "fromLngLat", "(DD)Lcom/mapbox/geojson/Point;");
- // Create Point
- jni::LocalObject<jni::jarray<jni::jdouble>> position = jni::NewLocalObject(env, toGeoJsonPosition(env, geometry.x, geometry.y));
- return reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromCoordinates, position.get()));
+ return reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromLngLat, geometry.x, geometry.y));
}
/**
- * LineString (double[][])
+ * static LineString fromLngLats(List<Point> points)
*/
jni::jobject* operator()(const mapbox::geometry::line_string<T> &geometry) const {
- static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/LineString")).release();
- static jni::jmethodID* fromCoordinates = &jni::GetStaticMethodID(env, *javaClass, "fromCoordinates", "([[D)Lcom/mapbox/services/commons/geojson/LineString;");
+ static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/geojson/LineString")).release();
+ static jni::jmethodID* fromLngLats = &jni::GetStaticMethodID(env, *javaClass, "fromLngLats", "(Ljava/util/List;)Lcom/mapbox/geojson/LineString;");
// Create
- jni::LocalObject<jni::jarray<jni::jobject>> coordinates = jni::NewLocalObject(env, toGeoJsonCoordinates(env, geometry));
- return reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromCoordinates, coordinates.get()));
+ jni::LocalObject<jni::jobject> listOfPoints = jni::NewLocalObject(env, toGeoJsonListOfPoints(env, geometry));
+ return reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromLngLats, listOfPoints.get()));
}
/**
- * MultiPoint (double[][])
+ * static MultiPoint fromLngLats(List<Point> points)
*/
jni::jobject* operator()(const mapbox::geometry::multi_point<T> &geometry) const {
- static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/MultiPoint")).release();
- static jni::jmethodID* fromCoordinates = &jni::GetStaticMethodID(env, *javaClass, "fromCoordinates", "([[D)Lcom/mapbox/services/commons/geojson/MultiPoint;");
+ static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/geojson/MultiPoint")).release();
+ static jni::jmethodID* fromLngLats = &jni::GetStaticMethodID(env, *javaClass, "fromLngLats", "(Ljava/util/List;)Lcom/mapbox/geojson/MultiPoint;");
// Create
- jni::LocalObject<jni::jarray<jni::jobject>> coordinates = jni::NewLocalObject(env, toGeoJsonCoordinates(env, geometry));
- return reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromCoordinates, coordinates.get()));
+ jni::LocalObject<jni::jobject> coordinates = jni::NewLocalObject(env, toGeoJsonListOfPoints(env, geometry));
+ return reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromLngLats, coordinates.get()));
}
/**
- * Polygon (double[][][])
+ * static Polygon fromLngLats(List<List<Point>> coordinates)
*/
jni::jobject* operator()(const mapbox::geometry::polygon<T> &geometry) const {
- static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/Polygon")).release();
- static jni::jmethodID* fromCoordinates = &jni::GetStaticMethodID(env, *javaClass, "fromCoordinates", "([[[D)Lcom/mapbox/services/commons/geojson/Polygon;");
+ static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/geojson/Polygon")).release();
+ static jni::jmethodID* fromLngLats = &jni::GetStaticMethodID(env, *javaClass, "fromLngLats", "(Ljava/util/List;)Lcom/mapbox/geojson/Polygon;");
// Create
- jni::LocalObject<jni::jarray<jni::jobject>> shape = jni::NewLocalObject(env, toShape<>(env, geometry));
- return reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromCoordinates, shape.get()));
+ jni::LocalObject<jni::jobject> shape = jni::NewLocalObject(env, toShape<>(env, geometry));
+ return reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromLngLats, shape.get()));
}
/**
- * MultiLineString (double[][][])
+ * static MultiLineString fromLngLats(List<List<Point>> points)
*/
jni::jobject* operator()(const mapbox::geometry::multi_line_string<T> &geometry) const {
- static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/MultiLineString")).release();
- static jni::jmethodID* fromCoordinates = &jni::GetStaticMethodID(env, *javaClass, "fromCoordinates", "([[[D)Lcom/mapbox/services/commons/geojson/MultiLineString;");
+ static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/geojson/MultiLineString")).release();
+ static jni::jmethodID* fromLngLats = &jni::GetStaticMethodID(env, *javaClass, "fromLngLats", "(Ljava/util/List;)Lcom/mapbox/geojson/MultiLineString;");
// Create
- jni::LocalObject<jni::jarray<jni::jobject>> shape = jni::NewLocalObject(env, toShape<>(env, geometry));
- return reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromCoordinates, shape.get()));
+ jni::LocalObject<jni::jobject> shape = jni::NewLocalObject(env, toShape<>(env, geometry));
+ return reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromLngLats, shape.get()));
}
/**
* MultiPolygon (double[][][][]) -> [[[D + Object array == [[[[D
+ *
+ * static MultiPolygon fromLngLats(List<List<List<Point>>> points)
*/
jni::jobject* operator()(const mapbox::geometry::multi_polygon<T> &geometry) const {
- static jni::jclass* listClass = jni::NewGlobalRef(env, &jni::FindClass(env, "[[[D")).release();
- jni::LocalObject<jni::jarray<jni::jobject>> jarray = jni::NewLocalObject(env, &jni::NewObjectArray(env, geometry.size(), *listClass));
+ // ArrayList
+ static jni::jclass* arrayListClass = jni::NewGlobalRef(env, &jni::FindClass(env, "java/util/ArrayList")).release();
+ static jni::jmethodID* constructor = &jni::GetMethodID(env, *arrayListClass, "<init>", "(I)V");
+ static jni::jmethodID* add = &jni::GetMethodID(env, *arrayListClass, "add", "(ILjava/lang/Object;)V");
+ jni::jobject* arrayList = &jni::NewObject(env, *arrayListClass, *constructor, geometry.size());
for(size_t i = 0; i < geometry.size(); i = i + 1) {
- jni::LocalObject<jni::jarray<jni::jobject>> shape = jni::NewLocalObject(env, toShape<>(env, geometry.at(i)));
- jni::SetObjectArrayElement(env, *jarray, i, shape.get());
+ jni::LocalObject<jni::jobject> shape = jni::NewLocalObject(env, toShape<>(env, geometry.at(i)));
+ jni::CallMethod<void>(env, arrayList, *add, i, shape.get());
}
// Create the MultiPolygon
- static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/MultiPolygon")).release();
- static jni::jmethodID* fromGeometries = &jni::GetStaticMethodID(env, *javaClass, "fromCoordinates", "([[[[D)Lcom/mapbox/services/commons/geojson/MultiPolygon;");
- return reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromGeometries, jarray.get()));
+ static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/geojson/MultiPolygon")).release();
+ static jni::jmethodID* fromGeometries = &jni::GetStaticMethodID(env, *javaClass, "fromLngLats", "(Ljava/util/List;)Lcom/mapbox/geojson/MultiPolygon;");
+ return reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromGeometries, arrayList));
}
/**
* GeometryCollection
*/
jni::jobject* operator()(const mapbox::geometry::geometry_collection<T> &collection) const {
- static jni::jclass* geometryClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/Geometry")).release();
+ static jni::jclass* geometryClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/geojson/Geometry")).release();
jni::LocalObject<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) {
@@ -112,8 +115,8 @@ public:
}
// Turn into array list and create the GeometryCollection
- static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/GeometryCollection")).release();
- static jni::jmethodID* fromGeometries = &jni::GetStaticMethodID(env, *javaClass, "fromGeometries", "(Ljava/util/List;)Lcom/mapbox/services/commons/geojson/GeometryCollection;");
+ static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/geojson/GeometryCollection")).release();
+ static jni::jmethodID* fromGeometries = &jni::GetStaticMethodID(env, *javaClass, "fromGeometries", "(Ljava/util/List;)Lcom/mapbox/geojson/GeometryCollection;");
jni::LocalObject<jni::jobject> list = jni::NewLocalObject(env, toArrayList<>(env, *jarray));
return reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromGeometries, list.get()));
@@ -122,47 +125,50 @@ public:
private:
/**
- * x, y -> jarray<jdouble> ([x,y])
- */
- static jni::jarray<jni::jdouble>* toGeoJsonPosition(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>> -> List<Point>
+ */
+ static jni::jobject* toGeoJsonListOfPoints(JNIEnv& env, std::vector<mapbox::geometry::point<T>> points) {
- /**
- * vector<point<T>> -> jarray<jobject> (double[][]) -> [D + Object array == [[D
- */
- static jni::jarray<jni::jobject>* toGeoJsonCoordinates(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);
+ // ArrayList
+ static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "java/util/ArrayList")).release();
+ static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "<init>", "(I)V");
+ static jni::jmethodID* add = &jni::GetMethodID(env, *javaClass, "add", "(ILjava/lang/Object;)V");
+ jni::jobject* arrayList = &jni::NewObject(env, *javaClass, *constructor, points.size());
+
+
+ // Point
+ static jni::jclass* pointJavaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/geojson/Point")).release();
+ static jni::jmethodID* fromLngLat = &jni::GetStaticMethodID(env, *pointJavaClass, "fromLngLat", "(DD)Lcom/mapbox/geojson/Point;");
for(size_t i = 0; i < points.size(); i = i + 1) {
mapbox::geometry::point<T> point = points.at(i);
- jni::LocalObject<jni::jarray<jni::jdouble>> position = jni::NewLocalObject(env, toGeoJsonPosition(env, point.x, point.y));
- jni::SetObjectArrayElement(env, jarray, i, position.get());
+ jni::LocalObject<jni::jobject> pointObject =
+ jni::NewLocalObject(env, jni::CallStaticMethod<jni::jobject*>(env, *pointJavaClass, *fromLngLat, point.x, point.y));
+ jni::CallMethod<void>(env, arrayList, *add, i, pointObject.get());
}
- return &jarray;
+ return arrayList;
}
/**
- * polygon<T>
- * multi_line_string<T>
- * -> jarray<jobject> (double[][][]) -> [[D + Object array == [[[D
+ * geometry -> List<List<Point>>
*/
template <class SHAPE>
- static jni::jarray<jni::jobject>* toShape(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);
+ static jni::jobject* toShape(JNIEnv& env, SHAPE value) {
+
+ // ArrayList
+ static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "java/util/ArrayList")).release();
+ static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "<init>", "(I)V");
+ static jni::jmethodID* add = &jni::GetMethodID(env, *javaClass, "add", "(ILjava/lang/Object;)V");
+ jni::jobject* arrayList = &jni::NewObject(env, *javaClass, *constructor, value.size());
+
for(size_t i = 0; i < value.size(); i = i + 1) {
- jni::LocalObject<jni::jarray<jni::jobject>> coordinates = jni::NewLocalObject(env, toGeoJsonCoordinates(env, value.at(i)));
- jni::SetObjectArrayElement(env, jarray, i, coordinates.get());
+ jni::LocalObject<jni::jobject> listOfPoints = jni::NewLocalObject(env, toGeoJsonListOfPoints(env, value.at(i)));
+ jni::CallMethod<void>(env, arrayList, *add, i, listOfPoints.get());
}
- return &jarray;
+ return arrayList;
}
};
diff --git a/platform/android/src/geojson/feature.cpp b/platform/android/src/geojson/feature.cpp
index a6b387cd15..d8a4e829e2 100644
--- a/platform/android/src/geojson/feature.cpp
+++ b/platform/android/src/geojson/feature.cpp
@@ -8,11 +8,11 @@ namespace geojson {
mbgl::Feature Feature::convert(jni::JNIEnv& env, jni::Object<Feature> jFeature) {
// Convert
- auto jGeometry = getGeometry(env, jFeature);
- auto jProperties = Feature::getProperties(env, jFeature);
+ auto jGeometry = geometry(env, jFeature);
+ auto jProperties = Feature::properties(env, jFeature);
std::experimental::optional<mapbox::geometry::identifier> id;
- auto jId = Feature::getId(env, jFeature);
+ auto jId = Feature::id(env, jFeature);
if (jId) {
id = { jni::Make<std::string>(env, jId) };
}
@@ -31,18 +31,18 @@ mbgl::Feature Feature::convert(jni::JNIEnv& env, jni::Object<Feature> jFeature)
return feature;
}
-jni::Object<Geometry> Feature::getGeometry(jni::JNIEnv& env, jni::Object<Feature> jFeature) {
- static auto method = Feature::javaClass.GetMethod<jni::Object<Geometry> ()>(env, "getGeometry");
+jni::Object<Geometry> Feature::geometry(jni::JNIEnv& env, jni::Object<Feature> jFeature) {
+ static auto method = Feature::javaClass.GetMethod<jni::Object<Geometry> ()>(env, "geometry");
return jFeature.Call(env, method);
}
-jni::Object<gson::JsonObject> Feature::getProperties(jni::JNIEnv& env, jni::Object<Feature> jFeature) {
- static auto method = Feature::javaClass.GetMethod<jni::Object<gson::JsonObject> ()>(env, "getProperties");
+jni::Object<gson::JsonObject> Feature::properties(jni::JNIEnv& env, jni::Object<Feature> jFeature) {
+ static auto method = Feature::javaClass.GetMethod<jni::Object<gson::JsonObject> ()>(env, "properties");
return jFeature.Call(env, method);
}
-jni::String Feature::getId(jni::JNIEnv& env, jni::Object<Feature> jFeature) {
- static auto method = Feature::javaClass.GetMethod<jni::String ()>(env, "getId");
+jni::String Feature::id(jni::JNIEnv& env, jni::Object<Feature> jFeature) {
+ static auto method = Feature::javaClass.GetMethod<jni::String ()>(env, "id");
return jFeature.Call(env, method);
}
diff --git a/platform/android/src/geojson/feature.hpp b/platform/android/src/geojson/feature.hpp
index b5d856cc42..ab59d783e5 100644
--- a/platform/android/src/geojson/feature.hpp
+++ b/platform/android/src/geojson/feature.hpp
@@ -16,17 +16,17 @@ namespace geojson {
class Feature : private mbgl::util::noncopyable {
public:
- static constexpr auto Name() { return "com/mapbox/services/commons/geojson/Feature"; };
+ static constexpr auto Name() { return "com/mapbox/geojson/Feature"; };
static jni::Object<Feature> fromGeometry(jni::JNIEnv&, jni::Object<Geometry>, jni::Object<gson::JsonObject>, jni::String);
static mbgl::Feature convert(jni::JNIEnv&, jni::Object<Feature>);
- static jni::Object<Geometry> getGeometry(jni::JNIEnv&, jni::Object<Feature>);
+ static jni::Object<Geometry> geometry(jni::JNIEnv&, jni::Object<Feature>);
- static jni::String getId(jni::JNIEnv&, jni::Object<Feature>);
+ static jni::String id(jni::JNIEnv&, jni::Object<Feature>);
- static jni::Object<gson::JsonObject> getProperties(jni::JNIEnv&, jni::Object<Feature>);
+ static jni::Object<gson::JsonObject> properties(jni::JNIEnv&, jni::Object<Feature>);
static jni::Class<Feature> javaClass;
diff --git a/platform/android/src/geojson/feature_collection.cpp b/platform/android/src/geojson/feature_collection.cpp
index 2f156532ae..06f8f10b9a 100644
--- a/platform/android/src/geojson/feature_collection.cpp
+++ b/platform/android/src/geojson/feature_collection.cpp
@@ -1,3 +1,4 @@
+#include <android/log.h>
#include "feature_collection.hpp"
#include "feature.hpp"
@@ -7,24 +8,28 @@ namespace android {
namespace geojson {
mbgl::FeatureCollection FeatureCollection::convert(jni::JNIEnv& env, jni::Object<FeatureCollection> jCollection) {
- auto jFeatureList = FeatureCollection::getFeatures(env, jCollection);
- auto jFeatures = java::util::List::toArray<Feature>(env, jFeatureList);
- auto size = size_t(jFeatures.Length(env));
-
auto collection = mbgl::FeatureCollection();
- collection.reserve(size);
- for (size_t i = 0; i < size; i++) {
- auto jFeature = jFeatures.Get(env, i);
- collection.push_back(Feature::convert(env, jFeature));
- jni::DeleteLocalRef(env, jFeature);
- }
+ if (jCollection) {
+ auto jFeatureList = FeatureCollection::features(env, jCollection);
+ auto jFeatures = java::util::List::toArray<Feature>(env, jFeatureList);
+ auto size = size_t(jFeatures.Length(env));
+ collection.reserve(size);
+ for (size_t i = 0; i < size; i++) {
+ auto jFeature = jFeatures.Get(env, i);
+ collection.push_back(Feature::convert(env, jFeature));
+ jni::DeleteLocalRef(env, jFeature);
+ }
+
+ jni::DeleteLocalRef(env, jFeatures);
+ jni::DeleteLocalRef(env, jFeatureList);
+ }
return collection;
}
-jni::Object<java::util::List> FeatureCollection::getFeatures(jni::JNIEnv& env, jni::Object<FeatureCollection> jCollection) {
- static auto method = FeatureCollection::javaClass.GetMethod<jni::Object<java::util::List> ()>(env, "getFeatures");
+jni::Object<java::util::List> FeatureCollection::features(jni::JNIEnv& env, jni::Object<FeatureCollection> jCollection) {
+ static auto method = FeatureCollection::javaClass.GetMethod<jni::Object<java::util::List> ()>(env, "features");
return jCollection.Call(env, method);
}
diff --git a/platform/android/src/geojson/feature_collection.hpp b/platform/android/src/geojson/feature_collection.hpp
index 8e9717e82b..259ffab370 100644
--- a/platform/android/src/geojson/feature_collection.hpp
+++ b/platform/android/src/geojson/feature_collection.hpp
@@ -13,11 +13,11 @@ namespace geojson {
class FeatureCollection : private mbgl::util::noncopyable {
public:
- static constexpr auto Name() { return "com/mapbox/services/commons/geojson/FeatureCollection"; };
+ static constexpr auto Name() { return "com/mapbox/geojson/FeatureCollection"; };
static mbgl::FeatureCollection convert(jni::JNIEnv&, jni::Object<FeatureCollection>);
- static jni::Object<java::util::List> getFeatures(jni::JNIEnv&, jni::Object<FeatureCollection>);
+ static jni::Object<java::util::List> features(jni::JNIEnv&, jni::Object<FeatureCollection>);
static jni::Class<FeatureCollection> javaClass;
diff --git a/platform/android/src/geojson/geometry.cpp b/platform/android/src/geojson/geometry.cpp
index 33bb4ee3db..ca19d8fb03 100644
--- a/platform/android/src/geojson/geometry.cpp
+++ b/platform/android/src/geojson/geometry.cpp
@@ -33,7 +33,7 @@ mapbox::geojson::geometry Geometry::convert(jni::JNIEnv &env, jni::Object<Geomet
}
std::string Geometry::getType(jni::JNIEnv &env, jni::Object<Geometry> jGeometry) {
- static auto method = Geometry::javaClass.GetMethod<jni::String ()>(env, "getType");
+ static auto method = Geometry::javaClass.GetMethod<jni::String ()>(env, "type");
auto jType = jGeometry.Call(env, method);
auto type = jni::Make<std::string>(env, jType);
jni::DeleteLocalRef(env, jType);
diff --git a/platform/android/src/geojson/geometry.hpp b/platform/android/src/geojson/geometry.hpp
index bdcff6bb3e..b7f8909f6f 100644
--- a/platform/android/src/geojson/geometry.hpp
+++ b/platform/android/src/geojson/geometry.hpp
@@ -11,7 +11,7 @@ namespace geojson {
class Geometry : private mbgl::util::noncopyable {
public:
- static constexpr auto Name() { return "com/mapbox/services/commons/geojson/Geometry"; };
+ static constexpr auto Name() { return "com/mapbox/geojson/Geometry"; };
static mapbox::geojson::geometry convert(jni::JNIEnv&, jni::Object<Geometry>);
diff --git a/platform/android/src/geojson/line_string.cpp b/platform/android/src/geojson/line_string.cpp
index d0719f2538..8eebd53550 100644
--- a/platform/android/src/geojson/line_string.cpp
+++ b/platform/android/src/geojson/line_string.cpp
@@ -1,6 +1,6 @@
#include "line_string.hpp"
-#include "position.hpp"
+#include "point.hpp"
namespace mbgl {
namespace android {
@@ -10,35 +10,36 @@ mapbox::geojson::line_string LineString::convert(jni::JNIEnv &env, jni::Object<L
mapbox::geojson::line_string lineString;
if (jLineString) {
- auto jPositionList = LineString::getCoordinates(env, jLineString);
- lineString = LineString::convert(env, jPositionList);
- jni::DeleteLocalRef(env, jPositionList);
+ auto jPointList = LineString::coordinates(env, jLineString);
+ lineString = LineString::convert(env, jPointList);
+ jni::DeleteLocalRef(env, jPointList);
}
return lineString;
}
-mapbox::geojson::line_string LineString::convert(jni::JNIEnv &env, jni::Object<java::util::List/*<Position>*/> jPositionList) {
+mapbox::geojson::line_string LineString::convert(jni::JNIEnv &env, jni::Object<java::util::List/*<Point>*/> jPointList) {
mapbox::geojson::line_string lineString;
- if (jPositionList) {
- auto jPositionArray = java::util::List::toArray<Position>(env, jPositionList);
+ if (jPointList) {
+ auto jPointArray = java::util::List::toArray<Point>(env, jPointList);
+ auto size = jPointArray.Length(env);
+ lineString.reserve(size);
- auto size = jPositionArray.Length(env);
for (std::size_t i = 0; i < size; i++) {
- auto jPosition = jPositionArray.Get(env, i);
- lineString.push_back(Position::convert(env, jPosition));
- jni::DeleteLocalRef(env, jPosition);
+ auto jPoint = jPointArray.Get(env, i);
+ lineString.push_back(Point::convert(env, jPoint));
+ jni::DeleteLocalRef(env, jPoint);
}
- jni::DeleteLocalRef(env, jPositionArray);
+ jni::DeleteLocalRef(env, jPointArray);
}
return lineString;
}
-jni::Object<java::util::List> LineString::getCoordinates(jni::JNIEnv &env, jni::Object<LineString> jLineString) {
- static auto method = LineString::javaClass.GetMethod<jni::Object<java::util::List> ()>(env, "getCoordinates");
+jni::Object<java::util::List> LineString::coordinates(jni::JNIEnv &env, jni::Object<LineString> jLineString) {
+ static auto method = LineString::javaClass.GetMethod<jni::Object<java::util::List> ()>(env, "coordinates");
return jLineString.Call(env, method);
}
diff --git a/platform/android/src/geojson/line_string.hpp b/platform/android/src/geojson/line_string.hpp
index d3be68d0a5..86033c2e6a 100644
--- a/platform/android/src/geojson/line_string.hpp
+++ b/platform/android/src/geojson/line_string.hpp
@@ -14,15 +14,15 @@ namespace geojson {
class LineString : private mbgl::util::noncopyable {
public:
- static constexpr auto Name() { return "com/mapbox/services/commons/geojson/LineString"; };
+ static constexpr auto Name() { return "com/mapbox/geojson/LineString"; };
static constexpr auto Type() { return "LineString"; };
static mapbox::geojson::line_string convert(jni::JNIEnv&, jni::Object<LineString>);
- static mapbox::geojson::line_string convert(jni::JNIEnv&, jni::Object<java::util::List/*<Position>*/>);
+ static mapbox::geojson::line_string convert(jni::JNIEnv&, jni::Object<java::util::List/*<Point>*/>);
- static jni::Object<java::util::List> getCoordinates(jni::JNIEnv&, jni::Object<LineString>);
+ static jni::Object<java::util::List> coordinates(jni::JNIEnv&, jni::Object<LineString>);
static jni::Class<LineString> javaClass;
diff --git a/platform/android/src/geojson/multi_line_string.cpp b/platform/android/src/geojson/multi_line_string.cpp
index b676144bf5..c748d4786f 100644
--- a/platform/android/src/geojson/multi_line_string.cpp
+++ b/platform/android/src/geojson/multi_line_string.cpp
@@ -10,27 +10,27 @@ mapbox::geojson::multi_line_string MultiLineString::convert(jni::JNIEnv &env, jn
mapbox::geojson::multi_line_string multiLineString;
if (jMultiLineString) {
- auto jPositionListsList = MultiLineString::getCoordinates(env, jMultiLineString);
- multiLineString = MultiLineString::convert(env, jPositionListsList);
- jni::DeleteLocalRef(env, jPositionListsList);
+ auto jPointListsList = MultiLineString::coordinates(env, jMultiLineString);
+ multiLineString = MultiLineString::convert(env, jPointListsList);
+ jni::DeleteLocalRef(env, jPointListsList);
}
return multiLineString;
}
-mapbox::geojson::multi_line_string MultiLineString::convert(jni::JNIEnv &env, jni::Object<java::util::List/*<java::util::List<Position>>*/> jPositionListsList) {
+mapbox::geojson::multi_line_string MultiLineString::convert(jni::JNIEnv &env, jni::Object<java::util::List/*<java::util::List<Point>>*/> jPointListsList) {
mapbox::geojson::multi_line_string multiLineString;
- if (jPositionListsList) {
- auto jPositionListsArray = java::util::List::toArray<java::util::List>(env, jPositionListsList);
+ if (jPointListsList) {
+ auto jPositionListsArray = java::util::List::toArray<java::util::List>(env, jPointListsList);
auto size = jPositionListsArray.Length(env);
multiLineString.reserve(size);
for (std::size_t i = 0; i < size; i++) {
- auto jPositionList = jPositionListsArray.Get(env, i);
- multiLineString.push_back(LineString::convert(env, jPositionList));
- jni::DeleteLocalRef(env, jPositionList);
+ auto jPointsList = jPositionListsArray.Get(env, i);
+ multiLineString.push_back(LineString::convert(env, jPointsList));
+ jni::DeleteLocalRef(env, jPointsList);
}
jni::DeleteLocalRef(env, jPositionListsArray);
@@ -39,8 +39,8 @@ mapbox::geojson::multi_line_string MultiLineString::convert(jni::JNIEnv &env, jn
return multiLineString;
}
-jni::Object<java::util::List> MultiLineString::getCoordinates(jni::JNIEnv &env, jni::Object<MultiLineString> jLineString) {
- static auto method = MultiLineString::javaClass.GetMethod<jni::Object<java::util::List> ()>(env, "getCoordinates");
+jni::Object<java::util::List> MultiLineString::coordinates(jni::JNIEnv &env, jni::Object<MultiLineString> jLineString) {
+ static auto method = MultiLineString::javaClass.GetMethod<jni::Object<java::util::List> ()>(env, "coordinates");
return jLineString.Call(env, method);
}
diff --git a/platform/android/src/geojson/multi_line_string.hpp b/platform/android/src/geojson/multi_line_string.hpp
index af33fe72d6..358a6b5dda 100644
--- a/platform/android/src/geojson/multi_line_string.hpp
+++ b/platform/android/src/geojson/multi_line_string.hpp
@@ -13,15 +13,15 @@ namespace geojson {
class MultiLineString : private mbgl::util::noncopyable {
public:
- static constexpr auto Name() { return "com/mapbox/services/commons/geojson/MultiLineString"; };
+ static constexpr auto Name() { return "com/mapbox/geojson/MultiLineString"; };
static constexpr auto Type() { return "MultiLineString"; };
static mapbox::geojson::multi_line_string convert(jni::JNIEnv&, jni::Object<MultiLineString>);
- static mapbox::geojson::multi_line_string convert(jni::JNIEnv&, jni::Object<java::util::List/*<java::util::List<Position>>*/>);
+ static mapbox::geojson::multi_line_string convert(jni::JNIEnv&, jni::Object<java::util::List/*<java::util::List<Point>>*/>);
- static jni::Object<java::util::List> getCoordinates(jni::JNIEnv&, jni::Object<MultiLineString>);
+ static jni::Object<java::util::List> coordinates(jni::JNIEnv&, jni::Object<MultiLineString>);
static jni::Class<MultiLineString> javaClass;
diff --git a/platform/android/src/geojson/multi_point.cpp b/platform/android/src/geojson/multi_point.cpp
index f3acdb1ea6..4f9ff596b2 100644
--- a/platform/android/src/geojson/multi_point.cpp
+++ b/platform/android/src/geojson/multi_point.cpp
@@ -12,16 +12,16 @@ mapbox::geojson::multi_point MultiPoint::convert(jni::JNIEnv &env, jni::Object<M
mapbox::geojson::multi_point multiPoint;
if (jMultiPoint) {
- auto jPositionListsList = MultiPoint::getCoordinates(env, jMultiPoint);
- multiPoint = convertExplicit<mapbox::geojson::multi_point>(LineString::convert(env, jPositionListsList));
- jni::DeleteLocalRef(env, jPositionListsList);
+ auto jPointListsList = MultiPoint::coordinates(env, jMultiPoint);
+ multiPoint = convertExplicit<mapbox::geojson::multi_point>(LineString::convert(env, jPointListsList));
+ jni::DeleteLocalRef(env, jPointListsList);
}
return multiPoint;
}
-jni::Object<java::util::List> MultiPoint::getCoordinates(jni::JNIEnv &env, jni::Object<MultiPoint> jMultiPoint) {
- static auto method = MultiPoint::javaClass.GetMethod<jni::Object<java::util::List> ()>(env, "getCoordinates");
+jni::Object<java::util::List> MultiPoint::coordinates(jni::JNIEnv &env, jni::Object<MultiPoint> jMultiPoint) {
+ static auto method = MultiPoint::javaClass.GetMethod<jni::Object<java::util::List> ()>(env, "coordinates");
return jMultiPoint.Call(env, method);
}
diff --git a/platform/android/src/geojson/multi_point.hpp b/platform/android/src/geojson/multi_point.hpp
index 7a698287eb..e893e879af 100644
--- a/platform/android/src/geojson/multi_point.hpp
+++ b/platform/android/src/geojson/multi_point.hpp
@@ -13,13 +13,13 @@ namespace geojson {
class MultiPoint : private mbgl::util::noncopyable {
public:
- static constexpr auto Name() { return "com/mapbox/services/commons/geojson/MultiPoint"; };
+ static constexpr auto Name() { return "com/mapbox/geojson/MultiPoint"; };
static constexpr auto Type() { return "MultiPoint"; };
static mapbox::geojson::multi_point convert(jni::JNIEnv&, jni::Object<MultiPoint>);
- static jni::Object<java::util::List> getCoordinates(jni::JNIEnv&, jni::Object<MultiPoint>);
+ static jni::Object<java::util::List> coordinates(jni::JNIEnv&, jni::Object<MultiPoint>);
static jni::Class<MultiPoint> javaClass;
diff --git a/platform/android/src/geojson/multi_polygon.cpp b/platform/android/src/geojson/multi_polygon.cpp
index a55884a110..aadba8c8a6 100644
--- a/platform/android/src/geojson/multi_polygon.cpp
+++ b/platform/android/src/geojson/multi_polygon.cpp
@@ -10,27 +10,27 @@ mapbox::geojson::multi_polygon MultiPolygon::convert(jni::JNIEnv &env, jni::Obje
mapbox::geojson::multi_polygon multiPolygon;
if (jMultiPolygon) {
- auto jPositionListsListList = MultiPolygon::getCoordinates(env, jMultiPolygon);
- auto jPositionListsListArray = java::util::List::toArray<java::util::List>(env, jPositionListsListList);
+ auto jPointListsListList = MultiPolygon::coordinates(env, jMultiPolygon);
+ auto jPointListsListArray = java::util::List::toArray<java::util::List>(env, jPointListsListList);
- auto size = jPositionListsListArray.Length(env);
+ auto size = jPointListsListArray.Length(env);
multiPolygon.reserve(size);
for (size_t i = 0; i < size; i++) {
- auto jPositionListsList = jPositionListsListArray.Get(env, i);
+ auto jPositionListsList = jPointListsListArray.Get(env, i);
multiPolygon.push_back(Polygon::convert(env, jPositionListsList));
jni::DeleteLocalRef(env, jPositionListsList);
}
- jni::DeleteLocalRef(env, jPositionListsListList);
- jni::DeleteLocalRef(env, jPositionListsListArray);
+ jni::DeleteLocalRef(env, jPointListsListArray);
+ jni::DeleteLocalRef(env, jPointListsListList);
}
return multiPolygon;
}
-jni::Object<java::util::List> MultiPolygon::getCoordinates(jni::JNIEnv &env, jni::Object<MultiPolygon> jPolygon) {
- static auto method = MultiPolygon::javaClass.GetMethod<jni::Object<java::util::List> ()>(env, "getCoordinates");
+jni::Object<java::util::List> MultiPolygon::coordinates(jni::JNIEnv &env, jni::Object<MultiPolygon> jPolygon) {
+ static auto method = MultiPolygon::javaClass.GetMethod<jni::Object<java::util::List> ()>(env, "coordinates");
return jPolygon.Call(env, method);
}
diff --git a/platform/android/src/geojson/multi_polygon.hpp b/platform/android/src/geojson/multi_polygon.hpp
index 1f144cffd2..6e1dfacfc8 100644
--- a/platform/android/src/geojson/multi_polygon.hpp
+++ b/platform/android/src/geojson/multi_polygon.hpp
@@ -13,13 +13,13 @@ namespace geojson {
class MultiPolygon : private mbgl::util::noncopyable {
public:
- static constexpr auto Name() { return "com/mapbox/services/commons/geojson/MultiPolygon"; };
+ static constexpr auto Name() { return "com/mapbox/geojson/MultiPolygon"; };
static constexpr auto Type() { return "MultiPolygon"; };
static mapbox::geojson::multi_polygon convert(jni::JNIEnv&, jni::Object<MultiPolygon>);
- static jni::Object<java::util::List> getCoordinates(jni::JNIEnv&, jni::Object<MultiPolygon>);
+ static jni::Object<java::util::List> coordinates(jni::JNIEnv&, jni::Object<MultiPolygon>);
static jni::Class<MultiPolygon> javaClass;
diff --git a/platform/android/src/geojson/point.cpp b/platform/android/src/geojson/point.cpp
index 3d19a119d7..d064547145 100644
--- a/platform/android/src/geojson/point.cpp
+++ b/platform/android/src/geojson/point.cpp
@@ -1,19 +1,49 @@
+#include <android/log.h>
#include "point.hpp"
+#include "../java/util.hpp"
+#include "../java_types.hpp"
+#include "../style/value.hpp"
namespace mbgl {
namespace android {
namespace geojson {
mapbox::geojson::point Point::convert(jni::JNIEnv &env, jni::Object<Point> jPoint) {
- auto jPosition = Point::getPosition(env, jPoint);
- auto point = Position::convert(env, jPosition);
- jni::DeleteLocalRef(env, jPosition);
+ mapbox::geojson::point point;
+
+ if (jPoint) {
+ auto jDoubleList = Point::coordinates(env, jPoint);
+ point = Point::convert(env, jDoubleList);
+ jni::DeleteLocalRef(env, jDoubleList);
+ }
+
+ return point;
+}
+
+mapbox::geojson::point Point::convert(jni::JNIEnv &env, jni::Object<java::util::List/*<Double>*/> jDoubleList) {
+ mapbox::geojson::point point;
+
+ if (jDoubleList) {
+ auto jDoubleArray = java::util::List::toArray<jobject>(env, jDoubleList);
+
+ auto lonObject = jDoubleArray.Get(env, 0);
+ auto latObject = jDoubleArray.Get(env, 1);
+
+ point.x = jni::CallMethod<jni::jdouble>(env, lonObject,
+ *java::Number::doubleValueMethodId);
+ point.y = jni::CallMethod<jni::jdouble>(env, latObject,
+ *java::Number::doubleValueMethodId);
+
+ jni::DeleteLocalRef(env, lonObject);
+ jni::DeleteLocalRef(env, latObject);
+ jni::DeleteLocalRef(env, jDoubleArray);
+ }
return point;
}
-jni::Object<Position> Point::getPosition(JNIEnv& env, jni::Object<Point> jPoint) {
- static auto method = Point::javaClass.GetMethod<jni::Object<Position> ()>(env, "getCoordinates");
- return jPoint.Call(env, method);
+jni::Object<java::util::List> Point::coordinates(jni::JNIEnv &env, jni::Object<Point> jPoint) {
+ static auto method = Point::javaClass.GetMethod<jni::Object<java::util::List> ()>(env, "coordinates");
+ return jPoint.Call(env, method);
}
void Point::registerNative(jni::JNIEnv &env) {
diff --git a/platform/android/src/geojson/point.hpp b/platform/android/src/geojson/point.hpp
index 64ac0af9cc..c6412299bf 100644
--- a/platform/android/src/geojson/point.hpp
+++ b/platform/android/src/geojson/point.hpp
@@ -3,23 +3,25 @@
#include <mbgl/util/geojson.hpp>
#include <mbgl/util/noncopyable.hpp>
-#include "position.hpp"
-
#include <jni/jni.hpp>
+#include "../java/util.hpp"
+
namespace mbgl {
namespace android {
namespace geojson {
class Point : private mbgl::util::noncopyable {
public:
- static constexpr auto Name() { return "com/mapbox/services/commons/geojson/Point"; };
+ static constexpr auto Name() { return "com/mapbox/geojson/Point"; };
static constexpr auto Type() { return "Point"; };
static mapbox::geojson::point convert(jni::JNIEnv&, jni::Object<Point>);
- static jni::Object<Position> getPosition(JNIEnv&, jni::Object<Point>);
+ static mapbox::geojson::point convert(jni::JNIEnv&, jni::Object<java::util::List/*<Double>*/>);
+
+ static jni::Object<java::util::List> coordinates(JNIEnv&, jni::Object<Point>);
static jni::Class<Point> javaClass;
diff --git a/platform/android/src/geojson/polygon.cpp b/platform/android/src/geojson/polygon.cpp
index ef00f9df3f..30ba996640 100644
--- a/platform/android/src/geojson/polygon.cpp
+++ b/platform/android/src/geojson/polygon.cpp
@@ -12,7 +12,7 @@ mapbox::geojson::polygon Polygon::convert(jni::JNIEnv &env, jni::Object<Polygon>
mapbox::geojson::polygon polygon;
if (jPolygon) {
- auto jPositionListsList = Polygon::getCoordinates(env, jPolygon);
+ auto jPositionListsList = Polygon::coordinates(env, jPolygon);
polygon = Polygon::convert(env, jPositionListsList);
jni::DeleteLocalRef(env, jPositionListsList);
}
@@ -20,11 +20,11 @@ mapbox::geojson::polygon Polygon::convert(jni::JNIEnv &env, jni::Object<Polygon>
return polygon;
}
-mapbox::geojson::polygon Polygon::convert(jni::JNIEnv &env, jni::Object<java::util::List/*<java::util::List<Position>>*/> jPositionListsList) {
+mapbox::geojson::polygon Polygon::convert(jni::JNIEnv &env, jni::Object<java::util::List/*<java::util::List<Point>>*/> jPointListsList) {
mapbox::geojson::polygon polygon;
- if (jPositionListsList) {
- auto multiLine = MultiLineString::convert(env, jPositionListsList);
+ if (jPointListsList) {
+ auto multiLine = MultiLineString::convert(env, jPointListsList);
polygon.reserve(multiLine.size());
for (auto&& line : multiLine) {
polygon.emplace_back(convertExplicit<mapbox::geojson::linear_ring>(std::move(line)));
@@ -35,8 +35,8 @@ mapbox::geojson::polygon Polygon::convert(jni::JNIEnv &env, jni::Object<java::ut
}
-jni::Object<java::util::List> Polygon::getCoordinates(jni::JNIEnv &env, jni::Object<Polygon> jPolygon) {
- static auto method = Polygon::javaClass.GetMethod<jni::Object<java::util::List> ()>(env, "getCoordinates");
+jni::Object<java::util::List> Polygon::coordinates(jni::JNIEnv &env, jni::Object<Polygon> jPolygon) {
+ static auto method = Polygon::javaClass.GetMethod<jni::Object<java::util::List> ()>(env, "coordinates");
return jPolygon.Call(env, method);
}
diff --git a/platform/android/src/geojson/polygon.hpp b/platform/android/src/geojson/polygon.hpp
index e5362cedf1..a8b2b93827 100644
--- a/platform/android/src/geojson/polygon.hpp
+++ b/platform/android/src/geojson/polygon.hpp
@@ -13,15 +13,15 @@ namespace geojson {
class Polygon : private mbgl::util::noncopyable {
public:
- static constexpr auto Name() { return "com/mapbox/services/commons/geojson/Polygon"; };
+ static constexpr auto Name() { return "com/mapbox/geojson/Polygon"; };
static constexpr auto Type() { return "Polygon"; };
static mapbox::geojson::polygon convert(jni::JNIEnv &, jni::Object<Polygon>);
- static mapbox::geojson::polygon convert(jni::JNIEnv&, jni::Object<java::util::List/*<java::util::List<Position>>*/>);
+ static mapbox::geojson::polygon convert(jni::JNIEnv&, jni::Object<java::util::List/*<java::util::List<Point>>*/>);
- static jni::Object<java::util::List> getCoordinates(jni::JNIEnv&, jni::Object<Polygon>);
+ static jni::Object<java::util::List> coordinates(jni::JNIEnv&, jni::Object<Polygon>);
static jni::Class<Polygon> javaClass;
diff --git a/platform/android/src/geojson/position.cpp b/platform/android/src/geojson/position.cpp
deleted file mode 100644
index c0e6da3887..0000000000
--- a/platform/android/src/geojson/position.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-#include "position.hpp"
-
-namespace mbgl {
-namespace android {
-namespace geojson {
-
-mapbox::geojson::point Position::convert(jni::JNIEnv &env, jni::Object<Position> jPosition) {
- static auto method = Position::javaClass.GetMethod<jni::Array<jdouble> ()>(env, "getCoordinates");
- // Array with 0: longitude, 1: latitude (and optionally 2: altitude)
- auto coordinates = jPosition.Call(env, method);
- jdouble lngLat[2];
- coordinates.GetRegion(env, 0, lngLat);
- mapbox::geojson::point point(lngLat[0], lngLat[1]);
- jni::DeleteLocalRef(env, coordinates);
- return point;
-}
-
-void Position::registerNative(jni::JNIEnv &env) {
- // Lookup the class
- javaClass = *jni::Class<Position>::Find(env).NewGlobalRef(env).release();
-}
-
-jni::Class<Position> Position::javaClass;
-
-} // namespace geojson
-} // namespace android
-} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/geojson/position.hpp b/platform/android/src/geojson/position.hpp
deleted file mode 100644
index 7017a8172a..0000000000
--- a/platform/android/src/geojson/position.hpp
+++ /dev/null
@@ -1,27 +0,0 @@
-#pragma once
-
-#include <mbgl/util/geojson.hpp>
-#include <mbgl/util/noncopyable.hpp>
-
-#include <jni/jni.hpp>
-
-namespace mbgl {
-namespace android {
-namespace geojson {
-
-class Position : private mbgl::util::noncopyable {
-public:
- static constexpr auto Name() { return "com/mapbox/services/commons/models/Position"; };
-
- static constexpr auto Type() { return "Position"; };
-
- static mapbox::geojson::point convert(jni::JNIEnv&, jni::Object<Position>);
-
- static jni::Class<Position> javaClass;
-
- static void registerNative(jni::JNIEnv&);
-};
-
-} // namespace geojson
-} // namespace android
-} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/jni.cpp b/platform/android/src/jni.cpp
index e75a9c6985..c2fd1c95ad 100755
--- a/platform/android/src/jni.cpp
+++ b/platform/android/src/jni.cpp
@@ -20,7 +20,6 @@
#include "geojson/multi_polygon.hpp"
#include "geojson/point.hpp"
#include "geojson/polygon.hpp"
-#include "geojson/position.hpp"
#include "geometry/lat_lng.hpp"
#include "geometry/lat_lng_bounds.hpp"
#include "geometry/lat_lng_quad.hpp"
@@ -47,7 +46,7 @@
#include "style/functions/interval_stops.hpp"
#include "style/functions/stop.hpp"
#include "style/layers/layers.hpp"
-#include "style/sources/sources.hpp"
+#include "style/sources/source.hpp"
#include "style/light.hpp"
#include "snapshotter/map_snapshotter.hpp"
#include "snapshotter/map_snapshot.hpp"
@@ -128,7 +127,6 @@ void registerNatives(JavaVM *vm) {
geojson::MultiPolygon::registerNative(env);
geojson::Point::registerNative(env);
geojson::Polygon::registerNative(env);
- geojson::Position::registerNative(env);
// Geometry
LatLng::registerNative(env);
@@ -162,7 +160,7 @@ void registerNatives(JavaVM *vm) {
// Style
TransitionOptions::registerNative(env);
registerNativeLayers(env);
- registerNativeSources(env);
+ Source::registerNative(env);
Light::registerNative(env);
Position::registerNative(env);
Stop::registerNative(env);
diff --git a/platform/android/src/native_map_view.cpp b/platform/android/src/native_map_view.cpp
index 04cbb21927..67fc132204 100755
--- a/platform/android/src/native_map_view.cpp
+++ b/platform/android/src/native_map_view.cpp
@@ -450,7 +450,7 @@ jni::Array<jni::jlong> NativeMapView::addMarkers(jni::JNIEnv& env, jni::Array<jn
}
void NativeMapView::onLowMemory(JNIEnv&) {
- rendererFrontend->onLowMemory();
+ rendererFrontend->reduceMemoryUse();
}
using DebugOptions = mbgl::MapDebugOptions;
@@ -856,9 +856,7 @@ jni::Array<jni::Object<Source>> NativeMapView::getSources(JNIEnv& env) {
jni::Array<jni::Object<Source>> jSources = jni::Array<jni::Object<Source>>::New(env, sources.size(), Source::javaClass);
int index = 0;
for (auto source : sources) {
- auto jSource = jni::Object<Source>(createJavaSourcePeer(env, *rendererFrontend, *source));
- jSources.Set(env, index, jSource);
- jni::DeleteLocalRef(env, jSource);
+ jSources.Set(env, index, Source::peerForCoreSource(env, *source, *rendererFrontend));
index++;
}
@@ -874,38 +872,25 @@ jni::Object<Source> NativeMapView::getSource(JNIEnv& env, jni::String sourceId)
}
// Create and return the source's native peer
- return jni::Object<Source>(createJavaSourcePeer(env, *rendererFrontend, *coreSource));
+ return Source::peerForCoreSource(env, *coreSource, *rendererFrontend);
}
-void NativeMapView::addSource(JNIEnv& env, jni::jlong sourcePtr) {
+void NativeMapView::addSource(JNIEnv& env, jni::Object<Source> obj, jlong sourcePtr) {
assert(sourcePtr != 0);
Source *source = reinterpret_cast<Source *>(sourcePtr);
try {
- source->addToMap(*map);
- source->setRendererFrontend(*rendererFrontend);
+ source->addToMap(env, obj, *map, *rendererFrontend);
} catch (const std::runtime_error& error) {
jni::ThrowNew(env, jni::FindClass(env, "com/mapbox/mapboxsdk/style/sources/CannotAddSourceException"), error.what());
}
}
-jni::Object<Source> NativeMapView::removeSourceById(JNIEnv& env, jni::String id) {
- std::unique_ptr<mbgl::style::Source> coreSource = map->getStyle().removeSource(jni::Make<std::string>(env, id));
- if (coreSource) {
- return jni::Object<Source>(createJavaSourcePeer(env, *rendererFrontend, *coreSource));
- } else {
- return jni::Object<Source>();
- }
-}
-
-void NativeMapView::removeSource(JNIEnv&, jlong sourcePtr) {
+void NativeMapView::removeSource(JNIEnv& env, jni::Object<Source> obj, jlong sourcePtr) {
assert(sourcePtr != 0);
mbgl::android::Source *source = reinterpret_cast<mbgl::android::Source *>(sourcePtr);
- std::unique_ptr<mbgl::style::Source> coreSource = map->getStyle().removeSource(source->get().getID());
- if (coreSource) {
- source->setSource(std::move(coreSource));
- }
+ source->removeFromMap(env, obj, *map);
}
void NativeMapView::addImage(JNIEnv& env, jni::String name, jni::jint w, jni::jint h, jni::jfloat scale, jni::Array<jbyte> pixels) {
@@ -1048,7 +1033,6 @@ void NativeMapView::registerNative(jni::JNIEnv& env) {
METHOD(&NativeMapView::getSources, "nativeGetSources"),
METHOD(&NativeMapView::getSource, "nativeGetSource"),
METHOD(&NativeMapView::addSource, "nativeAddSource"),
- METHOD(&NativeMapView::removeSourceById, "nativeRemoveSourceById"),
METHOD(&NativeMapView::removeSource, "nativeRemoveSource"),
METHOD(&NativeMapView::addImage, "nativeAddImage"),
METHOD(&NativeMapView::addImages, "nativeAddImages"),
diff --git a/platform/android/src/native_map_view.hpp b/platform/android/src/native_map_view.hpp
index d7e3b17b99..507d77ac5f 100755
--- a/platform/android/src/native_map_view.hpp
+++ b/platform/android/src/native_map_view.hpp
@@ -19,7 +19,7 @@
#include "geometry/lat_lng.hpp"
#include "geometry/projected_meters.hpp"
#include "style/layers/layers.hpp"
-#include "style/sources/sources.hpp"
+#include "style/sources/source.hpp"
#include "geometry/lat_lng_bounds.hpp"
#include "map/camera_position.hpp"
#include "map/image.hpp"
@@ -230,11 +230,11 @@ public:
jni::Object<Source> getSource(JNIEnv&, jni::String);
- void addSource(JNIEnv&, jni::jlong);
+ void addSource(JNIEnv&, jni::Object<Source>, jlong nativePtr);
jni::Object<Source> removeSourceById(JNIEnv&, jni::String);
- void removeSource(JNIEnv&, jlong);
+ void removeSource(JNIEnv&, jni::Object<Source>, jlong nativePtr);
void addImage(JNIEnv&, jni::String, jni::jint, jni::jint, jni::jfloat, jni::Array<jbyte>);
diff --git a/platform/android/src/run_loop.cpp b/platform/android/src/run_loop.cpp
index 1d284a9e72..34366d836a 100644
--- a/platform/android/src/run_loop.cpp
+++ b/platform/android/src/run_loop.cpp
@@ -216,11 +216,8 @@ LOOP_HANDLE RunLoop::getLoopHandle() {
return Get()->impl.get();
}
-void RunLoop::push(std::shared_ptr<WorkTask> task) {
- withMutex([&] {
- queue.push(std::move(task));
- impl->wake();
- });
+void RunLoop::wake() {
+ impl->wake();
}
void RunLoop::run() {
diff --git a/platform/android/src/style/android_conversion.hpp b/platform/android/src/style/android_conversion.hpp
index 082fe411e2..510a9f8444 100644
--- a/platform/android/src/style/android_conversion.hpp
+++ b/platform/android/src/style/android_conversion.hpp
@@ -4,8 +4,9 @@
#include <mbgl/util/feature.hpp>
#include <mbgl/util/logging.hpp>
-#include <mbgl/style/conversion.hpp>
#include <mbgl/util/optional.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/geojson.hpp>
#include <jni/jni.hpp>
@@ -13,89 +14,105 @@ namespace mbgl {
namespace style {
namespace conversion {
-inline bool isUndefined(const mbgl::android::Value& value) {
- return value.isNull();
-}
+template <>
+class ConversionTraits<mbgl::android::Value> {
+public:
+ static bool isUndefined(const mbgl::android::Value& value) {
+ return value.isNull();
+ }
-inline bool isArray(const mbgl::android::Value& value) {
- return value.isArray();
-}
+ static bool isArray(const mbgl::android::Value& value) {
+ return value.isArray();
+ }
-inline bool isObject(const mbgl::android::Value& value) {
- return value.isObject();
-}
+ static bool isObject(const mbgl::android::Value& value) {
+ return value.isObject();
+ }
-inline std::size_t arrayLength(const mbgl::android::Value& value) {
- return value.getLength();;
-}
+ static std::size_t arrayLength(const mbgl::android::Value& value) {
+ return value.getLength();;
+ }
-inline mbgl::android::Value arrayMember(const mbgl::android::Value& value, std::size_t i) {
- return value.get(i);
-}
+ static mbgl::android::Value arrayMember(const mbgl::android::Value& value, std::size_t i) {
+ return value.get(i);
+ }
-inline optional<mbgl::android::Value> objectMember(const mbgl::android::Value& value, const char* key) {
- mbgl::android::Value member = value.get(key);
+ static optional<mbgl::android::Value> objectMember(const mbgl::android::Value& value, const char* key) {
+ mbgl::android::Value member = value.get(key);
+ if (!member.isNull()) {
+ return member;
+ } else {
+ return {};
+ }
+ }
- if (!member.isNull()) {
- return member;
- } else {
+ template <class Fn>
+ static optional<Error> eachMember(const mbgl::android::Value&, Fn&&) {
+ // TODO
+ mbgl::Log::Warning(mbgl::Event::Android, "eachMember not implemented");
return {};
}
-}
-template <class Fn>
-optional<Error> eachMember(const mbgl::android::Value&, Fn&&) {
- // TODO
- mbgl::Log::Warning(mbgl::Event::Android, "eachMember not implemented");
- return {};
-}
+ static optional<bool> toBool(const mbgl::android::Value& value) {
+ if (value.isBool()) {
+ return value.toBool();
+ } else {
+ return {};
+ }
+ }
-inline optional<bool> toBool(const mbgl::android::Value& value) {
- if (value.isBool()) {
- return value.toBool();
- } else {
- return {};
+ static optional<float> toNumber(const mbgl::android::Value& value) {
+ if (value.isNumber()) {
+ auto num = value.toFloat();
+ return num;
+ } else {
+ return {};
+ }
}
-}
-inline optional<float> toNumber(const mbgl::android::Value& value) {
- if (value.isNumber()) {
- auto num = value.toFloat();
- return num;
- } else {
- return {};
+ static optional<double> toDouble(const mbgl::android::Value& value) {
+ if (value.isNumber()) {
+ return value.toDouble();
+ } else {
+ return {};
+ }
}
-}
-inline optional<double> toDouble(const mbgl::android::Value& value) {
- if (value.isNumber()) {
- return value.toDouble();
- } else {
- return {};
+ static optional<std::string> toString(const mbgl::android::Value& value) {
+ if (value.isString()) {
+ return value.toString();
+ } else {
+ return {};
+ }
}
-}
-inline optional<std::string> toString(const mbgl::android::Value& value) {
- if (value.isString()) {
- return value.toString();
- } else {
- return {};
+ static optional<Value> toValue(const mbgl::android::Value& value) {
+ if (value.isNull()) {
+ return {};
+ } else if (value.isBool()) {
+ return { value.toBool() };
+ } else if (value.isString()) {
+ return { value.toString() };
+ } else if (value.isNumber()) {
+ auto doubleVal = value.toDouble();
+ return { doubleVal - (int) doubleVal > 0.0 ? doubleVal : value.toLong() };
+ } else {
+ return {};
+ }
}
-}
-inline optional<Value> toValue(const mbgl::android::Value& value) {
- if (value.isNull()) {
- return {};
- } else if (value.isBool()) {
- return { value.toBool() };
- } else if (value.isString()) {
- return { value.toString() };
- } else if (value.isNumber()) {
- auto doubleVal = value.toDouble();
- return { doubleVal - (int) doubleVal > 0.0 ? doubleVal : value.toLong() };
- } else {
- return {};
+ static optional<GeoJSON> toGeoJSON(const mbgl::android::Value &value, Error &error) {
+ if (value.isNull() || !value.isString()) {
+ error = { "no json data found" };
+ return {};
+ }
+ return parseGeoJSON(value.toString(), error);
}
+};
+
+template <class T, class...Args>
+optional<T> convert(mbgl::android::Value&& value, Error& error, Args&&...args) {
+ return convert<T>(Convertible(std::move(value)), error, std::forward<Args>(args)...);
}
} // namespace conversion
diff --git a/platform/android/src/style/conversion/filter.hpp b/platform/android/src/style/conversion/filter.hpp
index 1f0abcf3a4..c154e88e7c 100644
--- a/platform/android/src/style/conversion/filter.hpp
+++ b/platform/android/src/style/conversion/filter.hpp
@@ -16,9 +16,8 @@ namespace conversion {
inline optional<mbgl::style::Filter> toFilter(jni::JNIEnv& env, jni::Array<jni::Object<>> jfilter) {
mbgl::optional<mbgl::style::Filter> filter;
if (jfilter) {
- Value filterValue(env, jfilter);
mbgl::style::conversion::Error error;
- auto converted = mbgl::style::conversion::convert<mbgl::style::Filter>(filterValue, error);
+ auto converted = mbgl::style::conversion::convert<mbgl::style::Filter>(Value(env, jfilter), error);
if (!converted) {
mbgl::Log::Error(mbgl::Event::JNI, "Error converting filter: " + error.message);
}
diff --git a/platform/android/src/style/conversion/geojson.hpp b/platform/android/src/style/conversion/geojson.hpp
deleted file mode 100644
index 748fe7361e..0000000000
--- a/platform/android/src/style/conversion/geojson.hpp
+++ /dev/null
@@ -1,24 +0,0 @@
-#pragma once
-
-#include <mapbox/geojson.hpp>
-#include <mbgl/style/conversion.hpp>
-#include <mbgl/style/conversion/geojson.hpp>
-#include <jni/jni.hpp>
-
-namespace mbgl {
-namespace style {
-namespace conversion {
-
-template <>
-optional<GeoJSON> Converter<GeoJSON>::operator()(const mbgl::android::Value& value, Error& error) const {
- if(value.isNull() || !value.isString()) {
- error = { "no json data found" };
- return {};
- }
-
- return convert<GeoJSON>(value.toString(), error);
-}
-
-} // namespace conversion
-} // namespace style
-} // namespace mbgl
diff --git a/platform/android/src/style/conversion/types.hpp b/platform/android/src/style/conversion/types.hpp
index 375d1a33aa..8a75b870b3 100644
--- a/platform/android/src/style/conversion/types.hpp
+++ b/platform/android/src/style/conversion/types.hpp
@@ -93,6 +93,13 @@ struct Converter<jni::jobject*, mbgl::style::CirclePitchScaleType> {
};
template <>
+struct Converter<jni::jobject*, mbgl::style::HillshadeIlluminationAnchorType> {
+ Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::HillshadeIlluminationAnchorType& value) const {
+ return convert<jni::jobject*, std::string>(env, toString(value));
+ }
+};
+
+template <>
struct Converter<jni::jobject*, mbgl::style::LightAnchorType> {
Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::LightAnchorType& value) const {
return convert<jni::jobject*, std::string>(env, toString(value));
diff --git a/platform/android/src/style/conversion/types_string_values.hpp b/platform/android/src/style/conversion/types_string_values.hpp
index a19ca33a2f..7e4fd4a7f7 100644
--- a/platform/android/src/style/conversion/types_string_values.hpp
+++ b/platform/android/src/style/conversion/types_string_values.hpp
@@ -206,6 +206,20 @@ namespace conversion {
}
}
+ // hillshade-illumination-anchor
+ inline std::string toString(mbgl::style::HillshadeIlluminationAnchorType value) {
+ switch (value) {
+ case mbgl::style::HillshadeIlluminationAnchorType::Map:
+ return "map";
+ break;
+ case mbgl::style::HillshadeIlluminationAnchorType::Viewport:
+ return "viewport";
+ break;
+ default:
+ throw std::runtime_error("Not implemented");
+ }
+ }
+
// anchor
inline std::string toString(mbgl::style::LightAnchorType value) {
switch (value) {
diff --git a/platform/android/src/style/conversion/url_or_tileset.hpp b/platform/android/src/style/conversion/url_or_tileset.hpp
index 00ef913d41..92c1182a63 100644
--- a/platform/android/src/style/conversion/url_or_tileset.hpp
+++ b/platform/android/src/style/conversion/url_or_tileset.hpp
@@ -17,18 +17,19 @@ namespace android {
// This conversion is expected not to fail because it's used only in contexts where
// the value was originally a String or TileSet object on the Java side. If it fails
// to convert, it's a bug in our serialization or Java-side static typing.
-inline variant<std::string, Tileset> convertURLOrTileset(const Value& value) {
+inline variant<std::string, Tileset> convertURLOrTileset(mbgl::android::Value&& value) {
using namespace mbgl::style::conversion;
- if (isObject(value)) {
+ const Convertible convertible(std::move(value));
+ if (isObject(convertible)) {
Error error;
- optional<Tileset> tileset = convert<Tileset>(value, error);
+ optional<Tileset> tileset = convert<Tileset>(convertible, error);
if (!tileset) {
throw std::logic_error(error.message);
}
return { *tileset };
} else {
- return { *toString(value) };
+ return { *toString(convertible) };
}
}
diff --git a/platform/android/src/style/layers/heatmap_layer.cpp b/platform/android/src/style/layers/heatmap_layer.cpp
new file mode 100644
index 0000000000..609499ec93
--- /dev/null
+++ b/platform/android/src/style/layers/heatmap_layer.cpp
@@ -0,0 +1,134 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`.
+
+#include "heatmap_layer.hpp"
+
+#include <string>
+
+#include "../conversion/property_value.hpp"
+#include "../conversion/transition_options.hpp"
+
+namespace mbgl {
+namespace android {
+
+ /**
+ * Creates an owning peer object (for layers not attached to the map) from the JVM side
+ */
+ HeatmapLayer::HeatmapLayer(jni::JNIEnv& env, jni::String layerId, jni::String sourceId)
+ : Layer(env, std::make_unique<mbgl::style::HeatmapLayer>(jni::Make<std::string>(env, layerId), jni::Make<std::string>(env, sourceId))) {
+ }
+
+ /**
+ * Creates a non-owning peer object (for layers currently attached to the map)
+ */
+ HeatmapLayer::HeatmapLayer(mbgl::Map& map, mbgl::style::HeatmapLayer& coreLayer)
+ : Layer(map, coreLayer) {
+ }
+
+ /**
+ * Creates an owning peer object (for layers not attached to the map)
+ */
+ HeatmapLayer::HeatmapLayer(mbgl::Map& map, std::unique_ptr<mbgl::style::HeatmapLayer> coreLayer)
+ : Layer(map, std::move(coreLayer)) {
+ }
+
+ HeatmapLayer::~HeatmapLayer() = default;
+
+ // Property getters
+
+ jni::Object<jni::ObjectTag> HeatmapLayer::getHeatmapRadius(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::HeatmapLayer>()->HeatmapLayer::getHeatmapRadius());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<TransitionOptions> HeatmapLayer::getHeatmapRadiusTransition(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ mbgl::style::TransitionOptions options = layer.as<mbgl::style::HeatmapLayer>()->HeatmapLayer::getHeatmapRadiusTransition();
+ return *convert<jni::Object<TransitionOptions>>(env, options);
+ }
+
+ void HeatmapLayer::setHeatmapRadiusTransition(jni::JNIEnv&, jlong duration, jlong delay) {
+ mbgl::style::TransitionOptions options;
+ options.duration.emplace(mbgl::Milliseconds(duration));
+ options.delay.emplace(mbgl::Milliseconds(delay));
+ layer.as<mbgl::style::HeatmapLayer>()->HeatmapLayer::setHeatmapRadiusTransition(options);
+ }
+
+ jni::Object<jni::ObjectTag> HeatmapLayer::getHeatmapWeight(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::HeatmapLayer>()->HeatmapLayer::getHeatmapWeight());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> HeatmapLayer::getHeatmapIntensity(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::HeatmapLayer>()->HeatmapLayer::getHeatmapIntensity());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<TransitionOptions> HeatmapLayer::getHeatmapIntensityTransition(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ mbgl::style::TransitionOptions options = layer.as<mbgl::style::HeatmapLayer>()->HeatmapLayer::getHeatmapIntensityTransition();
+ return *convert<jni::Object<TransitionOptions>>(env, options);
+ }
+
+ void HeatmapLayer::setHeatmapIntensityTransition(jni::JNIEnv&, jlong duration, jlong delay) {
+ mbgl::style::TransitionOptions options;
+ options.duration.emplace(mbgl::Milliseconds(duration));
+ options.delay.emplace(mbgl::Milliseconds(delay));
+ layer.as<mbgl::style::HeatmapLayer>()->HeatmapLayer::setHeatmapIntensityTransition(options);
+ }
+
+ jni::Object<jni::ObjectTag> HeatmapLayer::getHeatmapOpacity(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::HeatmapLayer>()->HeatmapLayer::getHeatmapOpacity());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<TransitionOptions> HeatmapLayer::getHeatmapOpacityTransition(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ mbgl::style::TransitionOptions options = layer.as<mbgl::style::HeatmapLayer>()->HeatmapLayer::getHeatmapOpacityTransition();
+ return *convert<jni::Object<TransitionOptions>>(env, options);
+ }
+
+ void HeatmapLayer::setHeatmapOpacityTransition(jni::JNIEnv&, jlong duration, jlong delay) {
+ mbgl::style::TransitionOptions options;
+ options.duration.emplace(mbgl::Milliseconds(duration));
+ options.delay.emplace(mbgl::Milliseconds(delay));
+ layer.as<mbgl::style::HeatmapLayer>()->HeatmapLayer::setHeatmapOpacityTransition(options);
+ }
+
+
+ jni::Class<HeatmapLayer> HeatmapLayer::javaClass;
+
+ jni::jobject* HeatmapLayer::createJavaPeer(jni::JNIEnv& env) {
+ static auto constructor = HeatmapLayer::javaClass.template GetConstructor<jni::jlong>(env);
+ return HeatmapLayer::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this));
+ }
+
+ void HeatmapLayer::registerNative(jni::JNIEnv& env) {
+ // Lookup the class
+ HeatmapLayer::javaClass = *jni::Class<HeatmapLayer>::Find(env).NewGlobalRef(env).release();
+
+ #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
+
+ // Register the peer
+ jni::RegisterNativePeer<HeatmapLayer>(
+ env, HeatmapLayer::javaClass, "nativePtr",
+ std::make_unique<HeatmapLayer, JNIEnv&, jni::String, jni::String>,
+ "initialize",
+ "finalize",
+ METHOD(&HeatmapLayer::getHeatmapRadiusTransition, "nativeGetHeatmapRadiusTransition"),
+ METHOD(&HeatmapLayer::setHeatmapRadiusTransition, "nativeSetHeatmapRadiusTransition"),
+ METHOD(&HeatmapLayer::getHeatmapRadius, "nativeGetHeatmapRadius"),
+ METHOD(&HeatmapLayer::getHeatmapWeight, "nativeGetHeatmapWeight"),
+ METHOD(&HeatmapLayer::getHeatmapIntensityTransition, "nativeGetHeatmapIntensityTransition"),
+ METHOD(&HeatmapLayer::setHeatmapIntensityTransition, "nativeSetHeatmapIntensityTransition"),
+ METHOD(&HeatmapLayer::getHeatmapIntensity, "nativeGetHeatmapIntensity"),
+ METHOD(&HeatmapLayer::getHeatmapOpacityTransition, "nativeGetHeatmapOpacityTransition"),
+ METHOD(&HeatmapLayer::setHeatmapOpacityTransition, "nativeSetHeatmapOpacityTransition"),
+ METHOD(&HeatmapLayer::getHeatmapOpacity, "nativeGetHeatmapOpacity"));
+ }
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/layers/heatmap_layer.hpp b/platform/android/src/style/layers/heatmap_layer.hpp
new file mode 100644
index 0000000000..85f9f0292e
--- /dev/null
+++ b/platform/android/src/style/layers/heatmap_layer.hpp
@@ -0,0 +1,50 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`.
+
+#pragma once
+
+#include "layer.hpp"
+#include "../transition_options.hpp"
+#include <mbgl/style/layers/heatmap_layer.hpp>
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace android {
+
+class HeatmapLayer : public Layer {
+public:
+
+ static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/layers/HeatmapLayer"; };
+
+ static jni::Class<HeatmapLayer> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+
+ HeatmapLayer(jni::JNIEnv&, jni::String, jni::String);
+
+ HeatmapLayer(mbgl::Map&, mbgl::style::HeatmapLayer&);
+
+ HeatmapLayer(mbgl::Map&, std::unique_ptr<mbgl::style::HeatmapLayer>);
+
+ ~HeatmapLayer();
+
+ // Properties
+
+ jni::Object<jni::ObjectTag> getHeatmapRadius(jni::JNIEnv&);
+ void setHeatmapRadiusTransition(jni::JNIEnv&, jlong duration, jlong delay);
+ jni::Object<TransitionOptions> getHeatmapRadiusTransition(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getHeatmapWeight(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getHeatmapIntensity(jni::JNIEnv&);
+ void setHeatmapIntensityTransition(jni::JNIEnv&, jlong duration, jlong delay);
+ jni::Object<TransitionOptions> getHeatmapIntensityTransition(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getHeatmapOpacity(jni::JNIEnv&);
+ void setHeatmapOpacityTransition(jni::JNIEnv&, jlong duration, jlong delay);
+ jni::Object<TransitionOptions> getHeatmapOpacityTransition(jni::JNIEnv&);
+ jni::jobject* createJavaPeer(jni::JNIEnv&);
+
+}; // class HeatmapLayer
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/layers/hillshade_layer.cpp b/platform/android/src/style/layers/hillshade_layer.cpp
new file mode 100644
index 0000000000..d98e3ee074
--- /dev/null
+++ b/platform/android/src/style/layers/hillshade_layer.cpp
@@ -0,0 +1,163 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`.
+
+#include "hillshade_layer.hpp"
+
+#include <string>
+
+#include "../conversion/property_value.hpp"
+#include "../conversion/transition_options.hpp"
+
+namespace mbgl {
+namespace android {
+
+ /**
+ * Creates an owning peer object (for layers not attached to the map) from the JVM side
+ */
+ HillshadeLayer::HillshadeLayer(jni::JNIEnv& env, jni::String layerId, jni::String sourceId)
+ : Layer(env, std::make_unique<mbgl::style::HillshadeLayer>(jni::Make<std::string>(env, layerId), jni::Make<std::string>(env, sourceId))) {
+ }
+
+ /**
+ * Creates a non-owning peer object (for layers currently attached to the map)
+ */
+ HillshadeLayer::HillshadeLayer(mbgl::Map& map, mbgl::style::HillshadeLayer& coreLayer)
+ : Layer(map, coreLayer) {
+ }
+
+ /**
+ * Creates an owning peer object (for layers not attached to the map)
+ */
+ HillshadeLayer::HillshadeLayer(mbgl::Map& map, std::unique_ptr<mbgl::style::HillshadeLayer> coreLayer)
+ : Layer(map, std::move(coreLayer)) {
+ }
+
+ HillshadeLayer::~HillshadeLayer() = default;
+
+ // Property getters
+
+ jni::Object<jni::ObjectTag> HillshadeLayer::getHillshadeIlluminationDirection(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::HillshadeLayer>()->HillshadeLayer::getHillshadeIlluminationDirection());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> HillshadeLayer::getHillshadeIlluminationAnchor(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::HillshadeLayer>()->HillshadeLayer::getHillshadeIlluminationAnchor());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> HillshadeLayer::getHillshadeExaggeration(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::HillshadeLayer>()->HillshadeLayer::getHillshadeExaggeration());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<TransitionOptions> HillshadeLayer::getHillshadeExaggerationTransition(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ mbgl::style::TransitionOptions options = layer.as<mbgl::style::HillshadeLayer>()->HillshadeLayer::getHillshadeExaggerationTransition();
+ return *convert<jni::Object<TransitionOptions>>(env, options);
+ }
+
+ void HillshadeLayer::setHillshadeExaggerationTransition(jni::JNIEnv&, jlong duration, jlong delay) {
+ mbgl::style::TransitionOptions options;
+ options.duration.emplace(mbgl::Milliseconds(duration));
+ options.delay.emplace(mbgl::Milliseconds(delay));
+ layer.as<mbgl::style::HillshadeLayer>()->HillshadeLayer::setHillshadeExaggerationTransition(options);
+ }
+
+ jni::Object<jni::ObjectTag> HillshadeLayer::getHillshadeShadowColor(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::HillshadeLayer>()->HillshadeLayer::getHillshadeShadowColor());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<TransitionOptions> HillshadeLayer::getHillshadeShadowColorTransition(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ mbgl::style::TransitionOptions options = layer.as<mbgl::style::HillshadeLayer>()->HillshadeLayer::getHillshadeShadowColorTransition();
+ return *convert<jni::Object<TransitionOptions>>(env, options);
+ }
+
+ void HillshadeLayer::setHillshadeShadowColorTransition(jni::JNIEnv&, jlong duration, jlong delay) {
+ mbgl::style::TransitionOptions options;
+ options.duration.emplace(mbgl::Milliseconds(duration));
+ options.delay.emplace(mbgl::Milliseconds(delay));
+ layer.as<mbgl::style::HillshadeLayer>()->HillshadeLayer::setHillshadeShadowColorTransition(options);
+ }
+
+ jni::Object<jni::ObjectTag> HillshadeLayer::getHillshadeHighlightColor(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::HillshadeLayer>()->HillshadeLayer::getHillshadeHighlightColor());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<TransitionOptions> HillshadeLayer::getHillshadeHighlightColorTransition(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ mbgl::style::TransitionOptions options = layer.as<mbgl::style::HillshadeLayer>()->HillshadeLayer::getHillshadeHighlightColorTransition();
+ return *convert<jni::Object<TransitionOptions>>(env, options);
+ }
+
+ void HillshadeLayer::setHillshadeHighlightColorTransition(jni::JNIEnv&, jlong duration, jlong delay) {
+ mbgl::style::TransitionOptions options;
+ options.duration.emplace(mbgl::Milliseconds(duration));
+ options.delay.emplace(mbgl::Milliseconds(delay));
+ layer.as<mbgl::style::HillshadeLayer>()->HillshadeLayer::setHillshadeHighlightColorTransition(options);
+ }
+
+ jni::Object<jni::ObjectTag> HillshadeLayer::getHillshadeAccentColor(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::HillshadeLayer>()->HillshadeLayer::getHillshadeAccentColor());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<TransitionOptions> HillshadeLayer::getHillshadeAccentColorTransition(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ mbgl::style::TransitionOptions options = layer.as<mbgl::style::HillshadeLayer>()->HillshadeLayer::getHillshadeAccentColorTransition();
+ return *convert<jni::Object<TransitionOptions>>(env, options);
+ }
+
+ void HillshadeLayer::setHillshadeAccentColorTransition(jni::JNIEnv&, jlong duration, jlong delay) {
+ mbgl::style::TransitionOptions options;
+ options.duration.emplace(mbgl::Milliseconds(duration));
+ options.delay.emplace(mbgl::Milliseconds(delay));
+ layer.as<mbgl::style::HillshadeLayer>()->HillshadeLayer::setHillshadeAccentColorTransition(options);
+ }
+
+
+ jni::Class<HillshadeLayer> HillshadeLayer::javaClass;
+
+ jni::jobject* HillshadeLayer::createJavaPeer(jni::JNIEnv& env) {
+ static auto constructor = HillshadeLayer::javaClass.template GetConstructor<jni::jlong>(env);
+ return HillshadeLayer::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this));
+ }
+
+ void HillshadeLayer::registerNative(jni::JNIEnv& env) {
+ // Lookup the class
+ HillshadeLayer::javaClass = *jni::Class<HillshadeLayer>::Find(env).NewGlobalRef(env).release();
+
+ #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
+
+ // Register the peer
+ jni::RegisterNativePeer<HillshadeLayer>(
+ env, HillshadeLayer::javaClass, "nativePtr",
+ std::make_unique<HillshadeLayer, JNIEnv&, jni::String, jni::String>,
+ "initialize",
+ "finalize",
+ METHOD(&HillshadeLayer::getHillshadeIlluminationDirection, "nativeGetHillshadeIlluminationDirection"),
+ METHOD(&HillshadeLayer::getHillshadeIlluminationAnchor, "nativeGetHillshadeIlluminationAnchor"),
+ METHOD(&HillshadeLayer::getHillshadeExaggerationTransition, "nativeGetHillshadeExaggerationTransition"),
+ METHOD(&HillshadeLayer::setHillshadeExaggerationTransition, "nativeSetHillshadeExaggerationTransition"),
+ METHOD(&HillshadeLayer::getHillshadeExaggeration, "nativeGetHillshadeExaggeration"),
+ METHOD(&HillshadeLayer::getHillshadeShadowColorTransition, "nativeGetHillshadeShadowColorTransition"),
+ METHOD(&HillshadeLayer::setHillshadeShadowColorTransition, "nativeSetHillshadeShadowColorTransition"),
+ METHOD(&HillshadeLayer::getHillshadeShadowColor, "nativeGetHillshadeShadowColor"),
+ METHOD(&HillshadeLayer::getHillshadeHighlightColorTransition, "nativeGetHillshadeHighlightColorTransition"),
+ METHOD(&HillshadeLayer::setHillshadeHighlightColorTransition, "nativeSetHillshadeHighlightColorTransition"),
+ METHOD(&HillshadeLayer::getHillshadeHighlightColor, "nativeGetHillshadeHighlightColor"),
+ METHOD(&HillshadeLayer::getHillshadeAccentColorTransition, "nativeGetHillshadeAccentColorTransition"),
+ METHOD(&HillshadeLayer::setHillshadeAccentColorTransition, "nativeSetHillshadeAccentColorTransition"),
+ METHOD(&HillshadeLayer::getHillshadeAccentColor, "nativeGetHillshadeAccentColor"));
+ }
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/layers/hillshade_layer.hpp b/platform/android/src/style/layers/hillshade_layer.hpp
new file mode 100644
index 0000000000..4b68251775
--- /dev/null
+++ b/platform/android/src/style/layers/hillshade_layer.hpp
@@ -0,0 +1,56 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`.
+
+#pragma once
+
+#include "layer.hpp"
+#include "../transition_options.hpp"
+#include <mbgl/style/layers/hillshade_layer.hpp>
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace android {
+
+class HillshadeLayer : public Layer {
+public:
+
+ static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/layers/HillshadeLayer"; };
+
+ static jni::Class<HillshadeLayer> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+
+ HillshadeLayer(jni::JNIEnv&, jni::String, jni::String);
+
+ HillshadeLayer(mbgl::Map&, mbgl::style::HillshadeLayer&);
+
+ HillshadeLayer(mbgl::Map&, std::unique_ptr<mbgl::style::HillshadeLayer>);
+
+ ~HillshadeLayer();
+
+ // Properties
+
+ jni::Object<jni::ObjectTag> getHillshadeIlluminationDirection(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getHillshadeIlluminationAnchor(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getHillshadeExaggeration(jni::JNIEnv&);
+ void setHillshadeExaggerationTransition(jni::JNIEnv&, jlong duration, jlong delay);
+ jni::Object<TransitionOptions> getHillshadeExaggerationTransition(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getHillshadeShadowColor(jni::JNIEnv&);
+ void setHillshadeShadowColorTransition(jni::JNIEnv&, jlong duration, jlong delay);
+ jni::Object<TransitionOptions> getHillshadeShadowColorTransition(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getHillshadeHighlightColor(jni::JNIEnv&);
+ void setHillshadeHighlightColorTransition(jni::JNIEnv&, jlong duration, jlong delay);
+ jni::Object<TransitionOptions> getHillshadeHighlightColorTransition(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getHillshadeAccentColor(jni::JNIEnv&);
+ void setHillshadeAccentColorTransition(jni::JNIEnv&, jlong duration, jlong delay);
+ jni::Object<TransitionOptions> getHillshadeAccentColorTransition(jni::JNIEnv&);
+ jni::jobject* createJavaPeer(jni::JNIEnv&);
+
+}; // class HillshadeLayer
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/layers/layer.cpp b/platform/android/src/style/layers/layer.cpp
index 02a1f0be82..29530879a5 100644
--- a/platform/android/src/style/layers/layer.cpp
+++ b/platform/android/src/style/layers/layer.cpp
@@ -4,11 +4,22 @@
#include <jni/jni.hpp>
#include <mbgl/style/style.hpp>
+#include <mbgl/style/filter.hpp>
#include <mbgl/style/transition_options.hpp>
+#include <mbgl/style/layers/background_layer.hpp>
+#include <mbgl/style/layers/circle_layer.hpp>
+#include <mbgl/style/layers/fill_layer.hpp>
+#include <mbgl/style/layers/fill_extrusion_layer.hpp>
+#include <mbgl/style/layers/heatmap_layer.hpp>
+#include <mbgl/style/layers/hillshade_layer.hpp>
+#include <mbgl/style/layers/line_layer.hpp>
+#include <mbgl/style/layers/raster_layer.hpp>
+#include <mbgl/style/layers/symbol_layer.hpp>
#include <mbgl/util/logging.hpp>
// Java -> C++ conversion
#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/filter.hpp>
#include <mbgl/style/conversion/layer.hpp>
#include <mbgl/style/conversion/source.hpp>
@@ -78,10 +89,8 @@ namespace android {
}
void Layer::setLayoutProperty(jni::JNIEnv& env, jni::String jname, jni::Object<> jvalue) {
- Value value(env, jvalue);
-
// Convert and set property
- optional<mbgl::style::conversion::Error> error = mbgl::style::conversion::setLayoutProperty(layer, jni::Make<std::string>(env, jname), value);
+ optional<mbgl::style::conversion::Error> error = mbgl::style::conversion::setLayoutProperty(layer, jni::Make<std::string>(env, jname), Value(env, jvalue));
if (error) {
mbgl::Log::Error(mbgl::Event::JNI, "Error setting property: " + jni::Make<std::string>(env, jname) + " " + error->message);
return;
@@ -89,10 +98,8 @@ namespace android {
}
void Layer::setPaintProperty(jni::JNIEnv& env, jni::String jname, jni::Object<> jvalue) {
- Value value(env, jvalue);
-
// Convert and set property
- optional<mbgl::style::conversion::Error> error = mbgl::style::conversion::setPaintProperty(layer, jni::Make<std::string>(env, jname), value);
+ optional<mbgl::style::conversion::Error> error = mbgl::style::conversion::setPaintProperty(layer, jni::Make<std::string>(env, jname), Value(env, jvalue));
if (error) {
mbgl::Log::Error(mbgl::Event::JNI, "Error setting property: " + jni::Make<std::string>(env, jname) + " " + error->message);
return;
@@ -105,6 +112,7 @@ namespace android {
void operator()(style::BackgroundLayer&) { Log::Warning(mbgl::Event::JNI, "BackgroundLayer doesn't support filters"); }
void operator()(style::CustomLayer&) { Log::Warning(mbgl::Event::JNI, "CustomLayer doesn't support filters"); }
void operator()(style::RasterLayer&) { Log::Warning(mbgl::Event::JNI, "RasterLayer doesn't support filters"); }
+ void operator()(style::HillshadeLayer&) { Log::Warning(mbgl::Event::JNI, "HillshadeLayer doesn't support filters"); }
template <class LayerType>
void operator()(LayerType& layer) {
@@ -116,10 +124,8 @@ namespace android {
using namespace mbgl::style;
using namespace mbgl::style::conversion;
- Value wrapped(env, jfilter);
-
Error error;
- optional<Filter> converted = convert<Filter>(wrapped, error);
+ optional<Filter> converted = convert<Filter>(Value(env, jfilter), error);
if (!converted) {
mbgl::Log::Error(mbgl::Event::JNI, "Error setting filter: " + error.message);
return;
@@ -134,6 +140,7 @@ namespace android {
void operator()(style::BackgroundLayer&) { Log::Warning(mbgl::Event::JNI, "BackgroundLayer doesn't support source layer"); }
void operator()(style::CustomLayer&) { Log::Warning(mbgl::Event::JNI, "CustomLayer doesn't support source layer"); }
void operator()(style::RasterLayer&) { Log::Warning(mbgl::Event::JNI, "RasterLayer doesn't support source layer"); }
+ void operator()(style::HillshadeLayer&) { Log::Warning(mbgl::Event::JNI, "HillshadeLayer doesn't support source layer"); }
template <class LayerType>
void operator()(LayerType& layer) {
@@ -154,6 +161,7 @@ namespace android {
std::string operator()(style::BackgroundLayer&) { return noop("BackgroundLayer"); }
std::string operator()(style::CustomLayer&) { return noop("CustomLayer"); }
std::string operator()(style::RasterLayer&) { return noop("RasterLayer"); }
+ std::string operator()(style::HillshadeLayer&) { return noop("HillshadeLayer"); }
template <class LayerType>
std::string operator()(LayerType& layer) {
diff --git a/platform/android/src/style/layers/layers.cpp b/platform/android/src/style/layers/layers.cpp
index 9803b6f841..5df689b45d 100644
--- a/platform/android/src/style/layers/layers.cpp
+++ b/platform/android/src/style/layers/layers.cpp
@@ -5,6 +5,8 @@
#include <mbgl/style/layers/circle_layer.hpp>
#include <mbgl/style/layers/fill_extrusion_layer.hpp>
#include <mbgl/style/layers/fill_layer.hpp>
+#include <mbgl/style/layers/heatmap_layer.hpp>
+#include <mbgl/style/layers/hillshade_layer.hpp>
#include <mbgl/style/layers/line_layer.hpp>
#include <mbgl/style/layers/raster_layer.hpp>
#include <mbgl/style/layers/symbol_layer.hpp>
@@ -15,6 +17,8 @@
#include "custom_layer.hpp"
#include "fill_extrusion_layer.hpp"
#include "fill_layer.hpp"
+#include "heatmap_layer.hpp"
+#include "hillshade_layer.hpp"
#include "line_layer.hpp"
#include "raster_layer.hpp"
#include "symbol_layer.hpp"
@@ -30,6 +34,8 @@ template <> struct PeerType<style::BackgroundLayer> { using Type = android::Back
template <> struct PeerType<style::CircleLayer> { using Type = android::CircleLayer; };
template <> struct PeerType<style::FillExtrusionLayer> { using Type = android::FillExtrusionLayer; };
template <> struct PeerType<style::FillLayer> { using Type = android::FillLayer; };
+template <> struct PeerType<style::HeatmapLayer> { using Type = android::HeatmapLayer; };
+template <> struct PeerType<style::HillshadeLayer> { using Type = android::HillshadeLayer; };
template <> struct PeerType<style::LineLayer> { using Type = android::LineLayer; };
template <> struct PeerType<style::RasterLayer> { using Type = android::RasterLayer; };
template <> struct PeerType<style::SymbolLayer> { using Type = android::SymbolLayer; };
@@ -92,6 +98,8 @@ void registerNativeLayers(jni::JNIEnv& env) {
CustomLayer::registerNative(env);
FillExtrusionLayer::registerNative(env);
FillLayer::registerNative(env);
+ HeatmapLayer::registerNative(env);
+ HillshadeLayer::registerNative(env);
LineLayer::registerNative(env);
RasterLayer::registerNative(env);
SymbolLayer::registerNative(env);
diff --git a/platform/android/src/style/layers/raster_layer.cpp b/platform/android/src/style/layers/raster_layer.cpp
index 98923d5129..6d36298bb1 100644
--- a/platform/android/src/style/layers/raster_layer.cpp
+++ b/platform/android/src/style/layers/raster_layer.cpp
@@ -155,19 +155,6 @@ namespace android {
return jni::Object<jni::ObjectTag>(*converted);
}
- jni::Object<TransitionOptions> RasterLayer::getRasterFadeDurationTransition(jni::JNIEnv& env) {
- using namespace mbgl::android::conversion;
- mbgl::style::TransitionOptions options = layer.as<mbgl::style::RasterLayer>()->RasterLayer::getRasterFadeDurationTransition();
- return *convert<jni::Object<TransitionOptions>>(env, options);
- }
-
- void RasterLayer::setRasterFadeDurationTransition(jni::JNIEnv&, jlong duration, jlong delay) {
- mbgl::style::TransitionOptions options;
- options.duration.emplace(mbgl::Milliseconds(duration));
- options.delay.emplace(mbgl::Milliseconds(delay));
- layer.as<mbgl::style::RasterLayer>()->RasterLayer::setRasterFadeDurationTransition(options);
- }
-
jni::Class<RasterLayer> RasterLayer::javaClass;
@@ -206,8 +193,6 @@ namespace android {
METHOD(&RasterLayer::getRasterContrastTransition, "nativeGetRasterContrastTransition"),
METHOD(&RasterLayer::setRasterContrastTransition, "nativeSetRasterContrastTransition"),
METHOD(&RasterLayer::getRasterContrast, "nativeGetRasterContrast"),
- METHOD(&RasterLayer::getRasterFadeDurationTransition, "nativeGetRasterFadeDurationTransition"),
- METHOD(&RasterLayer::setRasterFadeDurationTransition, "nativeSetRasterFadeDurationTransition"),
METHOD(&RasterLayer::getRasterFadeDuration, "nativeGetRasterFadeDuration"));
}
diff --git a/platform/android/src/style/layers/raster_layer.hpp b/platform/android/src/style/layers/raster_layer.hpp
index 3b119ee0d2..ed6fc3e1b2 100644
--- a/platform/android/src/style/layers/raster_layer.hpp
+++ b/platform/android/src/style/layers/raster_layer.hpp
@@ -54,8 +54,6 @@ public:
jni::Object<TransitionOptions> getRasterContrastTransition(jni::JNIEnv&);
jni::Object<jni::ObjectTag> getRasterFadeDuration(jni::JNIEnv&);
- void setRasterFadeDurationTransition(jni::JNIEnv&, jlong duration, jlong delay);
- jni::Object<TransitionOptions> getRasterFadeDurationTransition(jni::JNIEnv&);
jni::jobject* createJavaPeer(jni::JNIEnv&);
}; // class RasterLayer
diff --git a/platform/android/src/style/sources/custom_geometry_source.cpp b/platform/android/src/style/sources/custom_geometry_source.cpp
new file mode 100644
index 0000000000..b38405a3b1
--- /dev/null
+++ b/platform/android/src/style/sources/custom_geometry_source.cpp
@@ -0,0 +1,143 @@
+#include "custom_geometry_source.hpp"
+
+#include <mbgl/renderer/query.hpp>
+
+// Java -> C++ conversion
+#include "../android_conversion.hpp"
+#include "../conversion/filter.hpp"
+
+// C++ -> Java conversion
+#include "../../conversion/conversion.hpp"
+#include "../../conversion/collection.hpp"
+#include "../../geojson/conversion/feature.hpp"
+#include <mbgl/style/conversion/custom_geometry_source_options.hpp>
+
+#include <string>
+
+namespace mbgl {
+namespace android {
+
+ // This conversion is expected not to fail because it's used only in contexts where
+ // the value was originally a CustomGeometrySourceOptions object on the Java side. If it fails
+ // to convert, it's a bug in our serialization or Java-side static typing.
+ static style::CustomGeometrySource::Options convertCustomGeometrySourceOptions(jni::JNIEnv& env,
+ jni::Object<> options,
+ style::TileFunction fetchFn,
+ style::TileFunction cancelFn) {
+ using namespace mbgl::style::conversion;
+ if (!options) {
+ return style::CustomGeometrySource::Options();
+ }
+ Error error;
+ optional<style::CustomGeometrySource::Options> result = convert<style::CustomGeometrySource::Options>(Value(env, options), error);
+ if (!result) {
+ throw std::logic_error(error.message);
+ }
+ result->fetchTileFunction = fetchFn;
+ result->cancelTileFunction = cancelFn;
+ return *result;
+ }
+
+ CustomGeometrySource::CustomGeometrySource(jni::JNIEnv& env,
+ jni::String sourceId,
+ jni::Object<> options)
+ : Source(env, std::make_unique<mbgl::style::CustomGeometrySource>(
+ jni::Make<std::string>(env, sourceId),
+ convertCustomGeometrySourceOptions(env, options,
+ std::bind(&CustomGeometrySource::fetchTile, this, std::placeholders::_1),
+ std::bind(&CustomGeometrySource::cancelTile, this, std::placeholders::_1)))) {
+ }
+
+ CustomGeometrySource::CustomGeometrySource(jni::JNIEnv& env,
+ mbgl::style::Source& coreSource,
+ AndroidRendererFrontend& frontend)
+ : Source(env, coreSource, createJavaPeer(env), frontend) {
+ }
+
+ CustomGeometrySource::~CustomGeometrySource() = default;
+
+ void CustomGeometrySource::fetchTile (const mbgl::CanonicalTileID& tileID) {
+ android::UniqueEnv _env = android::AttachEnv();
+
+ static auto fetchTile = javaClass.GetMethod<void (jni::jint, jni::jint, jni::jint)>(*_env, "fetchTile");
+
+ assert(javaPeer);
+
+ auto peer = jni::Cast(*_env, *javaPeer, javaClass);
+ peer.Call(*_env, fetchTile, (int)tileID.z, (int)tileID.x, (int)tileID.y);
+ };
+
+ void CustomGeometrySource::cancelTile(const mbgl::CanonicalTileID& tileID) {
+ android::UniqueEnv _env = android::AttachEnv();
+
+ static auto cancelTile = javaClass.GetMethod<void (jni::jint, jni::jint, jni::jint)>(*_env, "cancelTile");
+
+ assert(javaPeer);
+
+ auto peer = jni::Cast(*_env, *javaPeer, javaClass);
+ peer.Call(*_env, cancelTile, (int)tileID.z, (int)tileID.x, (int)tileID.y);
+ };
+
+ void CustomGeometrySource::setTileData(jni::JNIEnv& env,
+ jni::jint z,
+ jni::jint x,
+ jni::jint y,
+ jni::Object<geojson::FeatureCollection> jFeatures) {
+ using namespace mbgl::android::geojson;
+
+ // Convert the jni object
+ auto geometry = geojson::FeatureCollection::convert(env, jFeatures);
+
+ // Update the core source
+ source.as<mbgl::style::CustomGeometrySource>()->CustomGeometrySource::setTileData(CanonicalTileID(z, x, y), GeoJSON(geometry));
+ }
+
+ void CustomGeometrySource::invalidateTile(jni::JNIEnv&, jni::jint z, jni::jint x, jni::jint y) {
+ source.as<mbgl::style::CustomGeometrySource>()->CustomGeometrySource::invalidateTile(CanonicalTileID(z, x, y));
+ }
+
+ void CustomGeometrySource::invalidateBounds(jni::JNIEnv& env, jni::Object<LatLngBounds> jBounds) {
+ auto bounds = LatLngBounds::getLatLngBounds(env, jBounds);
+ source.as<mbgl::style::CustomGeometrySource>()->CustomGeometrySource::invalidateRegion(bounds);
+ }
+
+ jni::Array<jni::Object<geojson::Feature>> CustomGeometrySource::querySourceFeatures(jni::JNIEnv& env,
+ jni::Array<jni::Object<>> jfilter) {
+ using namespace mbgl::android::conversion;
+ using namespace mbgl::android::geojson;
+
+ std::vector<mbgl::Feature> features;
+ if (rendererFrontend) {
+ features = rendererFrontend->querySourceFeatures(source.getID(), { {}, toFilter(env, jfilter) });
+ }
+ return *convert<jni::Array<jni::Object<Feature>>, std::vector<mbgl::Feature>>(env, features);
+ }
+
+ jni::Class<CustomGeometrySource> CustomGeometrySource::javaClass;
+
+ jni::Object<Source> CustomGeometrySource::createJavaPeer(jni::JNIEnv& env) {
+ static auto constructor = CustomGeometrySource::javaClass.template GetConstructor<jni::jlong>(env);
+ return jni::Object<Source>(CustomGeometrySource::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this)).Get());
+ }
+
+ void CustomGeometrySource::registerNative(jni::JNIEnv& env) {
+ // Lookup the class
+ CustomGeometrySource::javaClass = *jni::Class<CustomGeometrySource>::Find(env).NewGlobalRef(env).release();
+
+ #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
+
+ // Register the peer
+ jni::RegisterNativePeer<CustomGeometrySource>(
+ env, CustomGeometrySource::javaClass, "nativePtr",
+ std::make_unique<CustomGeometrySource, JNIEnv&, jni::String, jni::Object<>>,
+ "initialize",
+ "finalize",
+ METHOD(&CustomGeometrySource::querySourceFeatures, "querySourceFeatures"),
+ METHOD(&CustomGeometrySource::setTileData, "nativeSetTileData"),
+ METHOD(&CustomGeometrySource::invalidateTile, "nativeInvalidateTile"),
+ METHOD(&CustomGeometrySource::invalidateBounds, "nativeInvalidateBounds")
+ );
+ }
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/sources/custom_geometry_source.hpp b/platform/android/src/style/sources/custom_geometry_source.hpp
new file mode 100644
index 0000000000..1dc1c07b4f
--- /dev/null
+++ b/platform/android/src/style/sources/custom_geometry_source.hpp
@@ -0,0 +1,47 @@
+#pragma once
+
+#include "source.hpp"
+#include <mbgl/style/sources/custom_geometry_source.hpp>
+#include <mbgl/util/geojson.hpp>
+#include <mbgl/tile/tile_id.hpp>
+#include "../../geojson/geometry.hpp"
+#include "../../geojson/feature.hpp"
+#include "../../geojson/feature_collection.hpp"
+#include "../../geometry/lat_lng_bounds.hpp"
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace android {
+
+class CustomGeometrySource : public Source {
+public:
+
+ static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/sources/CustomGeometrySource"; };
+
+ static jni::Class<CustomGeometrySource> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+
+ CustomGeometrySource(jni::JNIEnv&, jni::String, jni::Object<>);
+
+ CustomGeometrySource(jni::JNIEnv&, mbgl::style::Source&, AndroidRendererFrontend&);
+
+ ~CustomGeometrySource();
+
+ void fetchTile(const mbgl::CanonicalTileID& tileID);
+ void cancelTile(const mbgl::CanonicalTileID& tileID);
+ void setTileData(jni::JNIEnv& env, jni::jint z, jni::jint x, jni::jint y, jni::Object<geojson::FeatureCollection> jf);
+
+ void invalidateTile(jni::JNIEnv& env, jni::jint z, jni::jint x, jni::jint y);
+ void invalidateBounds(jni::JNIEnv& env, jni::Object<LatLngBounds> bounds);
+
+ jni::Array<jni::Object<geojson::Feature>> querySourceFeatures(jni::JNIEnv&,
+ jni::Array<jni::Object<>> );
+
+private:
+ jni::Object<Source> createJavaPeer(jni::JNIEnv&);
+
+}; // class CustomGeometrySource
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/sources/geojson_source.cpp b/platform/android/src/style/sources/geojson_source.cpp
index 90ef851eba..6d9ab9e22c 100644
--- a/platform/android/src/style/sources/geojson_source.cpp
+++ b/platform/android/src/style/sources/geojson_source.cpp
@@ -5,15 +5,15 @@
// Java -> C++ conversion
#include "../android_conversion.hpp"
#include "../conversion/filter.hpp"
-#include "../conversion/geojson.hpp"
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/geojson.hpp>
+#include <mbgl/style/conversion/geojson_options.hpp>
// C++ -> Java conversion
#include "../../conversion/conversion.hpp"
#include "../../conversion/collection.hpp"
#include "../../geojson/conversion/feature.hpp"
#include "../conversion/url_or_tileset.hpp"
-#include <mbgl/style/conversion.hpp>
-#include <mbgl/style/conversion/geojson_options.hpp>
#include <string>
@@ -29,7 +29,7 @@ namespace android {
return style::GeoJSONOptions();
}
Error error;
- optional<style::GeoJSONOptions> result = convert<style::GeoJSONOptions>(Value(env, options), error);
+ optional<style::GeoJSONOptions> result = convert<style::GeoJSONOptions>(mbgl::android::Value(env, options), error);
if (!result) {
throw std::logic_error(error.message);
}
@@ -43,8 +43,10 @@ namespace android {
) {
}
- GeoJSONSource::GeoJSONSource(mbgl::style::GeoJSONSource& coreSource)
- : Source(coreSource) {
+ GeoJSONSource::GeoJSONSource(jni::JNIEnv& env,
+ mbgl::style::Source& coreSource,
+ AndroidRendererFrontend& frontend)
+ : Source(env, coreSource, createJavaPeer(env), frontend) {
}
GeoJSONSource::~GeoJSONSource() = default;
@@ -54,7 +56,7 @@ namespace android {
// Convert the jni object
Error error;
- optional<GeoJSON> converted = convert<GeoJSON>(Value(env, json), error);
+ optional<GeoJSON> converted = convert<GeoJSON>(mbgl::android::Value(env, json), error);
if(!converted) {
mbgl::Log::Error(mbgl::Event::JNI, "Error setting geo json: " + error.message);
return;
@@ -118,9 +120,9 @@ namespace android {
jni::Class<GeoJSONSource> GeoJSONSource::javaClass;
- jni::jobject* GeoJSONSource::createJavaPeer(jni::JNIEnv& env) {
+ jni::Object<Source> GeoJSONSource::createJavaPeer(jni::JNIEnv& env) {
static auto constructor = GeoJSONSource::javaClass.template GetConstructor<jni::jlong>(env);
- return GeoJSONSource::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this));
+ return jni::Object<Source>(GeoJSONSource::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this)).Get());
}
void GeoJSONSource::registerNative(jni::JNIEnv& env) {
diff --git a/platform/android/src/style/sources/geojson_source.hpp b/platform/android/src/style/sources/geojson_source.hpp
index 52dd632bfa..c46519b04a 100644
--- a/platform/android/src/style/sources/geojson_source.hpp
+++ b/platform/android/src/style/sources/geojson_source.hpp
@@ -21,7 +21,7 @@ public:
GeoJSONSource(jni::JNIEnv&, jni::String, jni::Object<>);
- GeoJSONSource(mbgl::style::GeoJSONSource&);
+ GeoJSONSource(jni::JNIEnv&, mbgl::style::Source&, AndroidRendererFrontend&);
~GeoJSONSource();
@@ -40,7 +40,8 @@ public:
jni::String getURL(jni::JNIEnv&);
- jni::jobject* createJavaPeer(jni::JNIEnv&);
+private:
+ jni::Object<Source> createJavaPeer(jni::JNIEnv&);
}; // class GeoJSONSource
diff --git a/platform/android/src/style/sources/image_source.cpp b/platform/android/src/style/sources/image_source.cpp
index d46b367c53..0cd6995969 100644
--- a/platform/android/src/style/sources/image_source.cpp
+++ b/platform/android/src/style/sources/image_source.cpp
@@ -23,8 +23,10 @@ namespace android {
) {
}
- ImageSource::ImageSource(mbgl::style::ImageSource& coreSource)
- : Source(coreSource) {
+ ImageSource::ImageSource(jni::JNIEnv& env,
+ mbgl::style::Source& coreSource,
+ AndroidRendererFrontend& frontend)
+ : Source(env, coreSource, createJavaPeer(env), frontend) {
}
ImageSource::~ImageSource() = default;
@@ -45,9 +47,9 @@ namespace android {
jni::Class<ImageSource> ImageSource::javaClass;
- jni::jobject* ImageSource::createJavaPeer(jni::JNIEnv& env) {
+ jni::Object<Source> ImageSource::createJavaPeer(jni::JNIEnv& env) {
static auto constructor = ImageSource::javaClass.template GetConstructor<jni::jlong>(env);
- return ImageSource::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this));
+ return jni::Object<Source>(ImageSource::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this)).Get());
}
void ImageSource::registerNative(jni::JNIEnv& env) {
diff --git a/platform/android/src/style/sources/image_source.hpp b/platform/android/src/style/sources/image_source.hpp
index 9787a7294f..f0af28d357 100644
--- a/platform/android/src/style/sources/image_source.hpp
+++ b/platform/android/src/style/sources/image_source.hpp
@@ -21,7 +21,7 @@ public:
ImageSource(jni::JNIEnv&, jni::String, jni::Object<LatLngQuad>);
- ImageSource(mbgl::style::ImageSource&);
+ ImageSource(jni::JNIEnv&, mbgl::style::Source&, AndroidRendererFrontend&);
~ImageSource();
@@ -30,7 +30,8 @@ public:
void setImage(jni::JNIEnv&, jni::Object<Bitmap>);
- jni::jobject* createJavaPeer(jni::JNIEnv&);
+private:
+ jni::Object<Source> createJavaPeer(jni::JNIEnv&);
}; // class ImageSource
diff --git a/platform/android/src/style/sources/raster_dem_source.cpp b/platform/android/src/style/sources/raster_dem_source.cpp
new file mode 100644
index 0000000000..75e0159d7c
--- /dev/null
+++ b/platform/android/src/style/sources/raster_dem_source.cpp
@@ -0,0 +1,63 @@
+#include "raster_dem_source.hpp"
+
+#include "../android_conversion.hpp"
+#include "../value.hpp"
+#include "../conversion/url_or_tileset.hpp"
+#include "source.hpp"
+
+#include <mbgl/util/variant.hpp>
+
+#include <string>
+
+namespace mbgl {
+namespace android {
+
+ RasterDEMSource::RasterDEMSource(jni::JNIEnv& env, jni::String sourceId, jni::Object<> urlOrTileSet, jni::jint tileSize)
+ : Source(
+ env,
+ std::make_unique<mbgl::style::RasterDEMSource>(
+ jni::Make<std::string>(env, sourceId),
+ convertURLOrTileset(Value(env, urlOrTileSet)),
+ tileSize
+ )
+ ) {
+ }
+
+ RasterDEMSource::RasterDEMSource(jni::JNIEnv& env,
+ mbgl::style::Source& coreSource,
+ AndroidRendererFrontend& frontend)
+ : Source(env, coreSource, createJavaPeer(env), frontend) {
+ }
+
+ RasterDEMSource::~RasterDEMSource() = default;
+
+ jni::String RasterDEMSource::getURL(jni::JNIEnv& env) {
+ optional<std::string> url = source.as<mbgl::style::RasterDEMSource>()->RasterDEMSource::getURL();
+ return url ? jni::Make<jni::String>(env, *url) : jni::String();
+ }
+
+ jni::Class<RasterDEMSource> RasterDEMSource::javaClass;
+
+ jni::Object<Source> RasterDEMSource::createJavaPeer(jni::JNIEnv& env) {
+ static auto constructor = RasterDEMSource::javaClass.template GetConstructor<jni::jlong>(env);
+ return jni::Object<Source>(RasterDEMSource::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this)).Get());
+ }
+
+ void RasterDEMSource::registerNative(jni::JNIEnv& env) {
+ // Lookup the class
+ RasterDEMSource::javaClass = *jni::Class<RasterDEMSource>::Find(env).NewGlobalRef(env).release();
+
+ #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
+
+ // Register the peer
+ jni::RegisterNativePeer<RasterDEMSource>(
+ env, RasterDEMSource::javaClass, "nativePtr",
+ std::make_unique<RasterDEMSource, JNIEnv&, jni::String, jni::Object<>, jni::jint>,
+ "initialize",
+ "finalize",
+ METHOD(&RasterDEMSource::getURL, "nativeGetUrl")
+ );
+ }
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/sources/raster_dem_source.hpp b/platform/android/src/style/sources/raster_dem_source.hpp
new file mode 100644
index 0000000000..56924c1f8d
--- /dev/null
+++ b/platform/android/src/style/sources/raster_dem_source.hpp
@@ -0,0 +1,33 @@
+#pragma once
+
+#include "source.hpp"
+#include <mbgl/style/sources/raster_dem_source.hpp>
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace android {
+
+class RasterDEMSource : public Source {
+public:
+
+ static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/sources/RasterDemSource"; };
+
+ static jni::Class<RasterDEMSource> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+
+ RasterDEMSource(jni::JNIEnv&, jni::String, jni::Object<>, jni::jint);
+
+ RasterDEMSource(jni::JNIEnv&, mbgl::style::Source&, AndroidRendererFrontend&);
+
+ ~RasterDEMSource();
+
+ jni::String getURL(jni::JNIEnv&);
+
+private:
+ jni::Object<Source> createJavaPeer(jni::JNIEnv&);
+
+}; // class RasterDEMSource
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/sources/raster_source.cpp b/platform/android/src/style/sources/raster_source.cpp
index d45342a1ad..33223a5b69 100644
--- a/platform/android/src/style/sources/raster_source.cpp
+++ b/platform/android/src/style/sources/raster_source.cpp
@@ -22,8 +22,10 @@ namespace android {
) {
}
- RasterSource::RasterSource(mbgl::style::RasterSource& coreSource)
- : Source(coreSource) {
+ RasterSource::RasterSource(jni::JNIEnv& env,
+ mbgl::style::Source& coreSource,
+ AndroidRendererFrontend& frontend)
+ : Source(env, coreSource, createJavaPeer(env), frontend) {
}
RasterSource::~RasterSource() = default;
@@ -35,9 +37,9 @@ namespace android {
jni::Class<RasterSource> RasterSource::javaClass;
- jni::jobject* RasterSource::createJavaPeer(jni::JNIEnv& env) {
+ jni::Object<Source> RasterSource::createJavaPeer(jni::JNIEnv& env) {
static auto constructor = RasterSource::javaClass.template GetConstructor<jni::jlong>(env);
- return RasterSource::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this));
+ return jni::Object<Source>(RasterSource::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this)).Get());
}
void RasterSource::registerNative(jni::JNIEnv& env) {
diff --git a/platform/android/src/style/sources/raster_source.hpp b/platform/android/src/style/sources/raster_source.hpp
index 84c49d7381..a1da22f40d 100644
--- a/platform/android/src/style/sources/raster_source.hpp
+++ b/platform/android/src/style/sources/raster_source.hpp
@@ -18,13 +18,14 @@ public:
RasterSource(jni::JNIEnv&, jni::String, jni::Object<>, jni::jint);
- RasterSource(mbgl::style::RasterSource&);
+ RasterSource(jni::JNIEnv&, mbgl::style::Source&, AndroidRendererFrontend&);
~RasterSource();
jni::String getURL(jni::JNIEnv&);
- jni::jobject* createJavaPeer(jni::JNIEnv&);
+private:
+ jni::Object<Source> createJavaPeer(jni::JNIEnv&);
}; // class RasterSource
diff --git a/platform/android/src/style/sources/source.cpp b/platform/android/src/style/sources/source.cpp
index 447b13019d..413530a5ec 100644
--- a/platform/android/src/style/sources/source.cpp
+++ b/platform/android/src/style/sources/source.cpp
@@ -15,29 +15,71 @@
#include <string>
+// Core Sources
+#include <mbgl/style/sources/geojson_source.hpp>
+#include <mbgl/style/sources/image_source.hpp>
+#include <mbgl/style/sources/raster_source.hpp>
+#include <mbgl/style/sources/vector_source.hpp>
+
+// Android Source peers
+#include "geojson_source.hpp"
+#include "image_source.hpp"
+#include "raster_source.hpp"
+#include "unknown_source.hpp"
+#include "vector_source.hpp"
+#include "custom_geometry_source.hpp"
+#include "raster_dem_source.hpp"
+
namespace mbgl {
namespace android {
- /**
- * Invoked when the construction is initiated from the jvm through a subclass
- */
- Source::Source(jni::JNIEnv&, std::unique_ptr<mbgl::style::Source> coreSource)
- : ownedSource(std::move(coreSource))
- , source(*ownedSource) {
+ static std::unique_ptr<Source> createSourcePeer(jni::JNIEnv& env, mbgl::style::Source& coreSource, AndroidRendererFrontend& frontend) {
+ if (coreSource.is<mbgl::style::VectorSource>()) {
+ return std::make_unique<VectorSource>(env, *coreSource.as<mbgl::style::VectorSource>(), frontend);
+ } else if (coreSource.is<mbgl::style::RasterSource>()) {
+ return std::make_unique<RasterSource>(env, *coreSource.as<mbgl::style::RasterSource>(), frontend);
+ } else if (coreSource.is<mbgl::style::GeoJSONSource>()) {
+ return std::make_unique<GeoJSONSource>(env, *coreSource.as<mbgl::style::GeoJSONSource>(), frontend);
+ } else if (coreSource.is<mbgl::style::ImageSource>()) {
+ return std::make_unique<ImageSource>(env, *coreSource.as<mbgl::style::ImageSource>(), frontend);
+ } else {
+ return std::make_unique<UnknownSource>(env, coreSource, frontend);
+ }
}
- Source::Source(mbgl::style::Source& coreSource)
- : source(coreSource) {
+ jni::Object<Source> Source::peerForCoreSource(jni::JNIEnv& env, mbgl::style::Source& coreSource, AndroidRendererFrontend& frontend) {
+ if (!coreSource.peer.has_value()) {
+ coreSource.peer = createSourcePeer(env, coreSource, frontend);
+ }
+ return *mbgl::util::any_cast<std::unique_ptr<Source>>(&coreSource.peer)->get()->javaPeer;
}
- Source::~Source() = default;
+ Source::Source(jni::JNIEnv& env, mbgl::style::Source& coreSource, jni::Object<Source> obj, AndroidRendererFrontend& frontend)
+ : source(coreSource)
+ , javaPeer(obj.NewGlobalRef(env))
+ , rendererFrontend(&frontend) {
+ }
- style::Source& Source::get() {
- return source;
+ Source::Source(jni::JNIEnv&, std::unique_ptr<mbgl::style::Source> coreSource)
+ : ownedSource(std::move(coreSource))
+ , source(*ownedSource) {
}
- void Source::setSource(std::unique_ptr<style::Source> coreSource) {
- this->ownedSource = std::move(coreSource);
+ Source::~Source() {
+ // Before being added to a map, the Java peer owns this C++ peer and cleans
+ // up after itself correctly through the jni native peer bindings.
+ // After being added to the map, the ownership is flipped and the C++ peer has a strong reference
+ // to it's Java peer, preventing the Java peer from being GC'ed.
+ // In this case, the core source initiates the destruction, which requires releasing the Java peer,
+ // while also resetting it's nativePtr to 0 to prevent the subsequent GC of the Java peer from
+ // re-entering this dtor.
+ if (ownedSource.get() == nullptr && javaPeer.get() != nullptr) {
+ // Manually clear the java peer
+ android::UniqueEnv env = android::AttachEnv();
+ static auto nativePtrField = javaClass.GetField<jlong>(*env, "nativePtr");
+ javaPeer->Set(*env, nativePtrField, (jlong) 0);
+ javaPeer.reset();
+ }
}
jni::String Source::getId(jni::JNIEnv& env) {
@@ -49,23 +91,48 @@ namespace android {
return attribution ? jni::Make<jni::String>(env, attribution.value()) : jni::Make<jni::String>(env,"");
}
- void Source::addToMap(mbgl::Map& _map) {
+ void Source::addToMap(JNIEnv& env, jni::Object<Source> obj, mbgl::Map& map, AndroidRendererFrontend& frontend) {
// Check to see if we own the source first
if (!ownedSource) {
throw std::runtime_error("Cannot add source twice");
}
- // Add source to map
- _map.getStyle().addSource(releaseCoreSource());
- }
+ // Add source to map and release ownership
+ map.getStyle().addSource(std::move(ownedSource));
+
+ // Add peer to core source
+ source.peer = std::unique_ptr<Source>(this);
+
+ // Add strong reference to java source
+ javaPeer = obj.NewGlobalRef(env);
- void Source::setRendererFrontend(AndroidRendererFrontend& frontend_) {
- rendererFrontend = &frontend_;
+ rendererFrontend = &frontend;
}
- std::unique_ptr<mbgl::style::Source> Source::releaseCoreSource() {
- assert(ownedSource != nullptr);
- return std::move(ownedSource);
+ void Source::removeFromMap(JNIEnv&, jni::Object<Source>, mbgl::Map& map) {
+ // Cannot remove if not attached yet
+ if (ownedSource) {
+ throw std::runtime_error("Cannot remove detached source");
+ }
+
+ // Remove the source from the map and take ownership
+ ownedSource = map.getStyle().removeSource(source.getID());
+
+ // The source may not be removed if any layers still reference it
+ if (!ownedSource) {
+ return;
+ }
+
+ // Release the peer relationships. These will be re-established when the source is added to a map
+ assert(ownedSource->peer.has_value());
+ util::any_cast<std::unique_ptr<Source>>(&(ownedSource->peer))->release();
+ ownedSource->peer.reset();
+
+ // Release the strong reference to the java peer
+ assert(javaPeer);
+ javaPeer.release();
+
+ rendererFrontend = nullptr;
}
jni::Class<Source> Source::javaClass;
@@ -82,6 +149,14 @@ namespace android {
METHOD(&Source::getAttribution, "nativeGetAttribution")
);
+ // Register subclasses
+ GeoJSONSource::registerNative(env);
+ ImageSource::registerNative(env);
+ RasterSource::registerNative(env);
+ UnknownSource::registerNative(env);
+ VectorSource::registerNative(env);
+ CustomGeometrySource::registerNative(env);
+ RasterDEMSource::registerNative(env);
}
} // namespace android
diff --git a/platform/android/src/style/sources/source.hpp b/platform/android/src/style/sources/source.hpp
index 383017b66f..718f60b381 100644
--- a/platform/android/src/style/sources/source.hpp
+++ b/platform/android/src/style/sources/source.hpp
@@ -21,48 +21,40 @@ public:
static void registerNative(jni::JNIEnv&);
+ static jni::Object<Source> peerForCoreSource(jni::JNIEnv&, mbgl::style::Source&, AndroidRendererFrontend&);
+
/*
- * Called when a Java object is created on the c++ side
+ * Called when a Java object is created for a core source that belongs to a map.
*/
- Source(mbgl::style::Source&);
+ Source(jni::JNIEnv&, mbgl::style::Source&, jni::Object<Source>, AndroidRendererFrontend&);
/*
- * Called when a Java object was created from the jvm side
+ * Called when a Java object is created for a new core source that does not belong to a map.
*/
Source(jni::JNIEnv&, std::unique_ptr<mbgl::style::Source>);
virtual ~Source();
- /**
- * Set core source (ie return ownership after remove)
- */
- void setSource(std::unique_ptr<style::Source>);
-
- style::Source& get();
-
- void addToMap(mbgl::Map&);
+ void addToMap(JNIEnv&, jni::Object<Source>, mbgl::Map&, AndroidRendererFrontend&);
- void setRendererFrontend(AndroidRendererFrontend&);
-
- virtual jni::jobject* createJavaPeer(jni::JNIEnv&) = 0;
+ void removeFromMap(JNIEnv&, jni::Object<Source>, mbgl::Map&);
jni::String getId(jni::JNIEnv&);
jni::String getAttribution(jni::JNIEnv&);
protected:
- // Release the owned view and return it
- std::unique_ptr<mbgl::style::Source> releaseCoreSource();
-
- // Set on newly created sources until added to the map
+ // Set on newly created sources until added to the map.
std::unique_ptr<mbgl::style::Source> ownedSource;
- // Raw pointer that is valid until the source is removed from the map
+ // Raw pointer that is valid at all times.
mbgl::style::Source& source;
- // RendererFrontend pointer is valid only when
- // added to the map
- AndroidRendererFrontend* rendererFrontend;
+ // Set when the source is added to a map.
+ jni::UniqueObject<Source> javaPeer;
+
+ // RendererFrontend pointer is valid only when added to the map.
+ AndroidRendererFrontend* rendererFrontend { nullptr };
};
} // namespace android
diff --git a/platform/android/src/style/sources/sources.cpp b/platform/android/src/style/sources/sources.cpp
deleted file mode 100644
index 9ab3ca8e84..0000000000
--- a/platform/android/src/style/sources/sources.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-#include "sources.hpp"
-
-#include <mbgl/style/source.hpp>
-#include <mbgl/style/sources/geojson_source.hpp>
-#include <mbgl/style/sources/image_source.hpp>
-#include <mbgl/style/sources/raster_source.hpp>
-#include <mbgl/style/sources/vector_source.hpp>
-
-#include "source.hpp"
-#include "geojson_source.hpp"
-#include "image_source.hpp"
-#include "raster_source.hpp"
-#include "unknown_source.hpp"
-#include "vector_source.hpp"
-
-namespace {
-
- using namespace mbgl::android;
-
- Source* initializeSourcePeer(mbgl::style::Source& coreSource) {
- Source* source;
- if (coreSource.is<mbgl::style::VectorSource>()) {
- source = new VectorSource(*coreSource.as<mbgl::style::VectorSource>());
- } else if (coreSource.is<mbgl::style::RasterSource>()) {
- source = new RasterSource(*coreSource.as<mbgl::style::RasterSource>());
- } else if (coreSource.is<mbgl::style::GeoJSONSource>()) {
- source = new GeoJSONSource(*coreSource.as<mbgl::style::GeoJSONSource>());
- } else if (coreSource.is<mbgl::style::ImageSource>()) {
- source = new ImageSource(*coreSource.as<mbgl::style::ImageSource>());
- } else {
- source = new UnknownSource(coreSource);
- }
-
- return source;
- }
-} // namespace
-
-namespace mbgl {
-namespace android {
-
-
-jni::jobject* createJavaSourcePeer(jni::JNIEnv& env, AndroidRendererFrontend& frontend, mbgl::style::Source& coreSource) {
- std::unique_ptr<Source> peerSource = std::unique_ptr<Source>(initializeSourcePeer(coreSource));
- peerSource->setRendererFrontend(frontend);
- jni::jobject* result = peerSource->createJavaPeer(env);
- peerSource.release();
- return result;
-}
-
-void registerNativeSources(jni::JNIEnv& env) {
- Source::registerNative(env);
- GeoJSONSource::registerNative(env);
- ImageSource::registerNative(env);
- RasterSource::registerNative(env);
- UnknownSource::registerNative(env);
- VectorSource::registerNative(env);
-}
-
-}
-}
diff --git a/platform/android/src/style/sources/sources.hpp b/platform/android/src/style/sources/sources.hpp
deleted file mode 100644
index c7b36818b2..0000000000
--- a/platform/android/src/style/sources/sources.hpp
+++ /dev/null
@@ -1,18 +0,0 @@
-#pragma once
-
-#include <mbgl/style/source.hpp>
-
-#include "source.hpp"
-#include "../../android_renderer_frontend.hpp"
-
-#include <jni/jni.hpp>
-
-namespace mbgl {
-namespace android {
-
- jni::jobject* createJavaSourcePeer(jni::JNIEnv&, AndroidRendererFrontend&, mbgl::style::Source&);
-
- void registerNativeSources(jni::JNIEnv&);
-
-} // namespace android
-} // namespace mbgl
diff --git a/platform/android/src/style/sources/unknown_source.cpp b/platform/android/src/style/sources/unknown_source.cpp
index 79f27bdfbf..4b5510c1db 100644
--- a/platform/android/src/style/sources/unknown_source.cpp
+++ b/platform/android/src/style/sources/unknown_source.cpp
@@ -12,15 +12,17 @@ namespace {
namespace mbgl {
namespace android {
- UnknownSource::UnknownSource(mbgl::style::Source& coreSource)
- : Source(coreSource) {
+ UnknownSource::UnknownSource(jni::JNIEnv& env,
+ mbgl::style::Source& coreSource,
+ AndroidRendererFrontend& frontend)
+ : Source(env, coreSource, createJavaPeer(env), frontend) {
}
jni::Class<UnknownSource> UnknownSource::javaClass;
- jni::jobject* UnknownSource::createJavaPeer(jni::JNIEnv& env) {
+ jni::Object<Source> UnknownSource::createJavaPeer(jni::JNIEnv& env) {
static auto constructor = UnknownSource::javaClass.template GetConstructor<jni::jlong>(env);
- return UnknownSource::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this));
+ return jni::Object<Source>(UnknownSource::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this)).Get());
}
void UnknownSource::registerNative(jni::JNIEnv& env) {
diff --git a/platform/android/src/style/sources/unknown_source.hpp b/platform/android/src/style/sources/unknown_source.hpp
index 4a003c9a7f..414d420c61 100644
--- a/platform/android/src/style/sources/unknown_source.hpp
+++ b/platform/android/src/style/sources/unknown_source.hpp
@@ -16,11 +16,12 @@ public:
static void registerNative(jni::JNIEnv&);
- UnknownSource(mbgl::style::Source&);
+ UnknownSource(jni::JNIEnv&, mbgl::style::Source&, AndroidRendererFrontend&);
~UnknownSource() = default;
- jni::jobject* createJavaPeer(jni::JNIEnv&);
+private:
+ jni::Object<Source> createJavaPeer(jni::JNIEnv&);
}; // class UnknownSource
diff --git a/platform/android/src/style/sources/vector_source.cpp b/platform/android/src/style/sources/vector_source.cpp
index 7fe45441bd..9a9548d283 100644
--- a/platform/android/src/style/sources/vector_source.cpp
+++ b/platform/android/src/style/sources/vector_source.cpp
@@ -30,8 +30,10 @@ namespace android {
) {
}
- VectorSource::VectorSource(mbgl::style::VectorSource& coreSource)
- : Source(coreSource) {
+ VectorSource::VectorSource(jni::JNIEnv& env,
+ mbgl::style::Source& coreSource,
+ AndroidRendererFrontend& frontend)
+ : Source(env, coreSource, createJavaPeer(env), frontend) {
}
VectorSource::~VectorSource() = default;
@@ -56,9 +58,9 @@ namespace android {
jni::Class<VectorSource> VectorSource::javaClass;
- jni::jobject* VectorSource::createJavaPeer(jni::JNIEnv& env) {
+ jni::Object<Source> VectorSource::createJavaPeer(jni::JNIEnv& env) {
static auto constructor = VectorSource::javaClass.template GetConstructor<jni::jlong>(env);
- return VectorSource::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this));
+ return jni::Object<Source>(VectorSource::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this)).Get());
}
void VectorSource::registerNative(jni::JNIEnv& env) {
diff --git a/platform/android/src/style/sources/vector_source.hpp b/platform/android/src/style/sources/vector_source.hpp
index 509fe068d1..16049f5c77 100644
--- a/platform/android/src/style/sources/vector_source.hpp
+++ b/platform/android/src/style/sources/vector_source.hpp
@@ -19,7 +19,7 @@ public:
VectorSource(jni::JNIEnv&, jni::String, jni::Object<>);
- VectorSource(mbgl::style::VectorSource&);
+ VectorSource(jni::JNIEnv&, mbgl::style::Source&, AndroidRendererFrontend&);
~VectorSource();
@@ -28,7 +28,8 @@ public:
jni::String getURL(jni::JNIEnv&);
- jni::jobject* createJavaPeer(jni::JNIEnv&);
+private:
+ jni::Object<Source> createJavaPeer(jni::JNIEnv&);
}; // class VectorSource
diff --git a/platform/android/src/style/value.cpp b/platform/android/src/style/value.cpp
index e1cd81d7fd..70bdea6677 100644
--- a/platform/android/src/style/value.cpp
+++ b/platform/android/src/style/value.cpp
@@ -24,8 +24,6 @@ namespace android {
Value::Value(jni::JNIEnv& _env, jni::jobject* _value) : env(_env), value(_value, ObjectDeleter(env)) {}
- Value::~Value() = default;
-
bool Value::isNull() const {
return value == nullptr;
}
diff --git a/platform/android/src/style/value.hpp b/platform/android/src/style/value.hpp
index 7464bae832..2057b93454 100644
--- a/platform/android/src/style/value.hpp
+++ b/platform/android/src/style/value.hpp
@@ -9,9 +9,13 @@ namespace android {
class Value {
public:
-
Value(jni::JNIEnv&, jni::jobject*);
- virtual ~Value();
+
+ Value(Value&&) = default;
+ Value& operator=(Value&&) = default;
+
+ Value(const Value&) = delete;
+ Value& operator=(const Value&) = delete;
bool isNull() const;
bool isArray() const;
diff --git a/platform/darwin/.gitignore b/platform/darwin/.gitignore
new file mode 100644
index 0000000000..f4635b2c19
--- /dev/null
+++ b/platform/darwin/.gitignore
@@ -0,0 +1,3 @@
+# Generated list files from code generation
+/scripts/generate-style-code.list
+/scripts/update-examples.list
diff --git a/platform/darwin/docs/guides/For Style Authors.md.ejs b/platform/darwin/docs/guides/For Style Authors.md.ejs
index ba6f14647c..c2eaf337e0 100644
--- a/platform/darwin/docs/guides/For Style Authors.md.ejs
+++ b/platform/darwin/docs/guides/For Style Authors.md.ejs
@@ -265,12 +265,16 @@ In style JSON | In Objective-C | In Swift
## Setting attribute values
Each property representing a layout or paint attribute is set to an
-`MGLStyleValue` object, which is either an `MGLConstantStyleValue` object (for
-constant values) or an `MGLStyleFunction` object (for style functions). The
-style value object is a container for the raw value or function parameters that
-you want the attribute to be set to.
+`NSExpression` object. `NSExpression` objects play the same role as
+[expressions in the Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions),
+but you create the former using a very different syntax. `NSExpression`’s format
+string syntax is reminiscent of a spreadsheet formula or an expression in a
+database query. See the
+“[Predicates and Expressions](Predicates and Expressions.md)” guide for an
+overview of the expression support in this SDK. This SDK no longer supports
+style functions; use expressions instead.
-### Constant style values
+### Constant values in expressions
In contrast to the JSON type that the style specification defines for each
layout or paint property, the style value object often contains a more specific
@@ -281,10 +285,10 @@ or set.
In style JSON | In Objective-C | In Swift
--------------|-----------------------|---------
Color | `<%- cocoaPrefix %>Color` | `<%- cocoaPrefix %>Color`
-Enum | `NSValue` (see `NSValue(MGLAdditions)`) | `NSValue` (see `NSValue(MGLAdditions)`)
+Enum | `NSString` | `String`
String | `NSString` | `String`
-Boolean | `NSNumber.boolValue` | `Bool`
-Number | `NSNumber.floatValue` | `Float`
+Boolean | `NSNumber.boolValue` | `NSNumber.boolValue`
+Number | `NSNumber.floatValue` | `NSNumber.floatValue`
Array (`-dasharray`) | `NSArray<NSNumber>` | `[Float]`
Array (`-font`) | `NSArray<NSString>` | `[String]`
<% if (iOS) { -%>
@@ -314,38 +318,77 @@ translation downward. This is the reverse of how `CGVector` is interpreted on
iOS.
<% } -%>
-### Style functions
-
-A _style function_ allows you to vary the value of a layout or paint attribute
-based on the zoom level, data provided by content sources, or both. For more
-information about style functions, see “[Using Style Functions at Runtime](using-style-functions-at-runtime.html)”.
-
-Each kind of style function is represented by a distinct class, but you
-typically create style functions as you create any other style value, using
-class methods on `MGLStyleValue`:
-
-In style specification | SDK class | SDK factory method
----------------------------|-----------------------------|-------------------
-zoom function | `MGLCameraStyleFunction` | `+[MGLStyleValue valueWithInterpolationMode:cameraStops:options:]`
-property function | `MGLSourceStyleFunction` | `+[MGLStyleValue valueWithInterpolationMode:sourceStops:attributeName:options:]`
-zoom-and-property function | `MGLCompositeStyleFunction` | `+[MGLStyleValue valueWithInterpolationMode:compositeStops:attributeName:options:]`
-
-The documentation for each individual style layer property indicates the kinds
-of style functions that are enabled for that property.
-
-When you create a style function, you specify an _interpolation mode_ and a
-series of _stops_. Each stop determines the effective value displayed at a
-particular zoom level (for camera functions) or the effective value on features
-with a particular attribute value in the content source (for source functions).
-The interpolation mode tells the SDK how to calculate the effective value
-between any two stops:
-
-In style specification | In the SDK
------------------------------|-----------
-`exponential` | `MGLInterpolationModeExponential`
-`interval` | `MGLInterpolationModeInterval`
-`categorical` | `MGLInterpolationModeCategorical`
-`identity` | `MGLInterpolationModeIdentity`
+### Expression operators
+
+In style specification | Method, function, or predicate type | Format string syntax
+-----------------------|-------------------------------------|---------------------
+`array` | |
+`boolean` | |
+`literal` | `+[NSExpression expressionForConstantValue:]` | `%@` representing `NSArray` or `NSDictionary`
+`number` | |
+`string` | |
+`to-boolean` | `boolValue` |
+`to-color` | |
+`to-number` | `mgl_numberWithFallbackValues:` |
+`to-string` | `stringValue` |
+`typeof` | |
+`geometry-type` | |
+`id` | |
+`properties` | |
+`at` | |
+`get` | `+[NSExpression expressionForKeyPath:]` | Key path
+`has` | |
+`length` | `count:` | `count({1, 2, 2, 3, 4, 7, 9})`
+`!` | `NSNotPredicateType` | `NOT (p0 OR … OR pn)`
+`!=` | `NSNotEqualToPredicateOperatorType` | `key != value`
+`<` | `NSLessThanPredicateOperatorType` | `key < value`
+`<=` | `NSLessThanOrEqualToPredicateOperatorType` | `key <= value`
+`==` | `NSEqualToPredicateOperatorType` | `key == value`
+`>` | `NSGreaterThanPredicateOperatorType` | `key > value`
+`>=` | `NSGreaterThanOrEqualToPredicateOperatorType` | `key >= value`
+`all` | `NSAndPredicateType` | `p0 AND … AND pn`
+`any` | `NSOrPredicateType` | `p0 OR … OR pn`
+`case` | `+[NSExpression expressionForConditional:trueExpression:falseExpression:]` | `TERNARY(condition, trueExpression, falseExpression)`
+`coalesce` | |
+`match` | |
+`interpolate` | `mgl_interpolateWithCurveType:parameters:stops:` |
+`step` | `mgl_stepWithMinimum:stops:` |
+`let` | `mgl_expressionWithContext:` |
+`var` | `+[NSExpression expressionForVariable:]` | `$variable`
+`concat` | `stringByAppendingString:` |
+`downcase` | `lowercase:` | `lowercase('DOWNTOWN')`
+`upcase` | `uppercase:` | `uppercase('Elysian Fields')`
+<% if (macOS) { -%>
+`rgb` | `+[NSColor colorWithCalibratedRed:green:blue:alpha:]` |
+`rgba` | `+[NSColor colorWithCalibratedRed:green:blue:alpha:]` |
+<% } else { %>
+`rgb` | `+[UIColor colorWithRed:green:blue:alpha:]` |
+`rgba` | `+[UIColor colorWithRed:green:blue:alpha:]` |
+<% } -%>
+`to-rgba` | |
+`-` | `from:subtract:` | `2 - 1`
+`*` | `multiply:by:` | `1 * 2`
+`/` | `divide:by:` | `1 / 2`
+`%` | `modulus:by:` |
+`^` | `raise:toPower:` | `2 ** 2`
+`+` | `add:to:` | `1 + 2`
+`acos` | |
+`asin` | |
+`atan` | |
+`cos` | |
+`e` | | `%@` representing `NSNumber` containing `M_E`
+`ln` | `ln:` | `ln(2)`
+`ln2` | | `%@` representing `NSNumber` containing `M_LN2`
+`log10` | `log:` | `log(1)`
+`log2` | |
+`max` | `max:` | `max({1, 2, 2, 3, 4, 7, 9})`
+`min` | `min:` | `min({1, 2, 2, 3, 4, 7, 9})`
+`pi` | | `%@` representing `NSNumber` containing `M_PI`
+`sin` | |
+`sqrt` | `sqrt:` | `sqrt(2)`
+`tan` | |
+`zoom` | | `$zoom`
+`heatmap-density` | | `$heatmapDensity`
## Filtering sources
@@ -370,5 +413,5 @@ In style JSON | In the format string
`["any", f0, …, fn]` | `p0 OR … OR pn`
`["none", f0, …, fn]` | `NOT (p0 OR … OR pn)`
-See the `MGLVectorStyleLayer.predicate` documentation for a full description of
-the supported operators and operand types.
+See the “[Predicates and Expressions](Predicates and Expressions.md)” guide for
+a full description of the supported operators and operand types.
diff --git a/platform/darwin/docs/guides/Predicates and Expressions.md b/platform/darwin/docs/guides/Predicates and Expressions.md
new file mode 100644
index 0000000000..19d98fd4c1
--- /dev/null
+++ b/platform/darwin/docs/guides/Predicates and Expressions.md
@@ -0,0 +1,414 @@
+# Predicates and expressions
+
+Style layers use predicates and expressions to determine what to display and how
+to format it. _Predicates_ are represented by the same `NSPredicate` class that
+filters results from Core Data or items in an `NSArray` in Objective-C.
+Predicates are based on _expressions_, represented by the `NSExpression` class.
+Somewhat unusually, style layers also use expressions on their own.
+
+This document discusses the specific subset of the predicate and expression
+syntax supported by this SDK. For a more general introduction to predicates and
+expressions, consult the
+_[Predicate Programming Guide](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Predicates/AdditionalChapters/Introduction.html)_
+in Apple developer documentation.
+
+## Using predicates to filter vector data
+
+Most style layer classes display `MGLFeature` objects that you can show or hide
+based on the feature’s attributes. Use the `MGLVectorStyleLayer.predicate`
+property to include only the features in the source layer that satisfy a
+condition that you define.
+
+The following comparison operators are supported:
+
+`NSPredicateOperatorType` | Format string syntax
+----------------------------------------------|---------------------
+`NSEqualToPredicateOperatorType` | `key = value`<br />`key == value`
+`NSGreaterThanOrEqualToPredicateOperatorType` | `key >= value`<br />`key => value`
+`NSLessThanOrEqualToPredicateOperatorType` | `key <= value`<br />`key =< value`
+`NSGreaterThanPredicateOperatorType` | `key > value`
+`NSLessThanPredicateOperatorType` | `key < value`
+`NSNotEqualToPredicateOperatorType` | `key != value`<br />`key <> value`
+`NSBetweenPredicateOperatorType` | `key BETWEEN { 32, 212 }`
+
+The following compound operators are supported:
+
+`NSCompoundPredicateType` | Format string syntax
+--------------------------|---------------------
+`NSAndPredicateType` | `predicate1 AND predicate2`<br />`predicate1 && predicate2`
+`NSOrPredicateType` | `predicate1 OR predicate2`<br />`predicate1 || predicate2`
+`NSNotPredicateType` | `NOT predicate`<br />`!predicate`
+
+The following aggregate operators are supported:
+
+`NSPredicateOperatorType` | Format string syntax
+----------------------------------|---------------------
+`NSInPredicateOperatorType` | `key IN { 'iOS', 'macOS', 'tvOS', 'watchOS' }`
+`NSContainsPredicateOperatorType` | `{ 'iOS', 'macOS', 'tvOS', 'watchOS' } CONTAINS key`
+
+To test whether a feature has or lacks a specific attribute, compare the
+attribute to `NULL` or `NIL`. Predicates created using the
+`+[NSPredicate predicateWithValue:]` method are also supported. String
+operators and custom operators are not supported.
+
+For details about the predicate format string syntax, consult the “Predicate
+Format String Syntax” chapter of the
+_[Predicate Programming Guide](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Predicates/AdditionalChapters/Introduction.html)_
+in Apple developer documentation.
+
+The predicate's left-hand expression must be a string that identifies a feature
+attribute or, alternatively, one of the following special attributes:
+
+<table>
+<thead>
+<tr><th>Attribute</th><th>Meaning</th></tr>
+</thead>
+<tbody>
+<tr>
+ <td><code>$id</code></td>
+ <td>
+ A value that uniquely identifies the feature in the containing source.
+ For details on the types of values that may be associated with this key,
+ consult the documentation for the <code>MGLFeature</code> protocol’s
+ <code>identifier</code> property.
+ </td>
+</tr>
+<tr>
+ <td><code>$type</code></td>
+ <td>
+ The type of geometry represented by the feature. A feature’s type is
+ guaranteed to be one of the following strings:
+ <ul>
+ <li>
+ <code>Point</code> for point features, corresponding to the
+ <code>MGLPointAnnotation</code> class
+ </li>
+ <li>
+ <code>LineString</code> for polyline features, corresponding to
+ the <code>MGLPolyline</code> class
+ </li>
+ <li>
+ <code>Polygon</code> for polygon features, corresponding to the
+ <code>MGLPolygon</code> class
+ </li>
+ </ul>
+ </td>
+</tr>
+<tr>
+ <td><code>point_count</code></td>
+ <td>The number of point features in a given cluster.</td>
+</tr>
+</tbody>
+</table>
+
+The predicate’s right-hand expression must be an `NSString` (to match strings)
+or `NSNumber` (to match numbers, including Boolean values) or an array of
+`NSString`s or `NSNumber`s, depending on the operator and the type of values
+expected for the attribute being tested. For floating-point values, use
+`-[NSNumber numberWithDouble:]` instead of `-[NSNumber numberWithFloat:]`
+to avoid precision issues.
+
+Automatic type casting is not performed. Therefore, a feature only matches this
+predicate if its value for the attribute in question is of the same type as the
+value specified in the predicate. Also, operator modifiers such as `c` (for
+case insensitivity), `d` (for diacritic insensitivity), and `l` (for locale
+sensitivity) are unsupported for comparison and aggregate operators that are
+used in the predicate.
+
+It is possible to create expressions that contain special characters in the
+predicate format syntax. This includes the `$` in the `$id` and `$type` special
+style attributes and also `hyphen-minus` and `tag:subtag`. However, you must use
+`%K` in the format string to represent these variables:
+`@"%K == 'LineString'", @"$type"`.
+
+## Using expressions to configure layout and paint attributes
+
+### Functions
+
+#### Key paths
+
+A key path expression refers to an attribute of the `MGLFeature` object being
+evaluated for display. For example, if a polygon’s `MGLFeature.attributes`
+dictionary contains the `floorCount` key, then the key path `floorCount` refers
+to the value of the `floorCount` attribute when evaluating that particular
+polygon.
+
+#### Predefined functions
+
+Of the
+[functions predefined by the `+[NSExpression expressionForFunction:arguments:]` method](https://developer.apple.com/documentation/foundation/nsexpression/1413747-init#discussion),
+the following subset is supported in layer attribute values:
+
+Initializer parameter | Format string syntax
+----------------------|---------------------
+`average:` | `average({1, 2, 2, 3, 4, 7, 9})`
+`sum:` | `sum({1, 2, 2, 3, 4, 7, 9})`
+`count:` | `count({1, 2, 2, 3, 4, 7, 9})`
+`min:` | `min({1, 2, 2, 3, 4, 7, 9})`
+`max:` | `max({1, 2, 2, 3, 4, 7, 9})`
+`add:to:` | `1 + 2`
+`from:subtract:` | `2 - 1`
+`multiply:by:` | `1 * 2`
+`divide:by:` | `1 / 2`
+`modulus:by:` | `modulus:by:(1, 2)`
+`sqrt:` | `sqrt(2)`
+`log:` | `log(10)`
+`ln:` | `ln(2)`
+`raise:toPower:` | `2 ** 2`
+`exp:` | `exp(0)`
+`ceiling:` | `ceiling(0.99999)`
+`abs:` | `abs(-1)`
+`trunc:` | `trunc(6378.1370)`
+`floor:` | `floor(-0.99999)`
+`uppercase:` | `uppercase('Elysian Fields')`
+`lowercase:` | `lowercase('DOWNTOWN')`
+`noindex:` | `noindex(0 + 2 + c)`
+
+The following predefined functions are not supported:
+
+Initializer parameter | Format string syntax
+----------------------|---------------------
+`median:` | `median({1, 2, 2, 3, 4, 7, 9})`
+`mode:` | `mode({1, 2, 2, 3, 4, 7, 9})`
+`stddev:` | `stddev({1, 2, 2, 3, 4, 7, 9})`
+`random` | `random()`
+`randomn:` | `randomn(10)`
+`now` | `now()`
+`bitwiseAnd:with:` | `bitwiseAnd:with:(5, 3)`
+`bitwiseOr:with:` | `bitwiseOr:with:(5, 3)`
+`bitwiseXor:with:` | `bitwiseXor:with:(5, 3)`
+`leftshift:by:` | `leftshift:by:(23, 1)`
+`rightshift:by:` | `rightshift:by:(23, 1)`
+`onesComplement:` | `onesComplement(255)`
+`distanceToLocation:fromLocation:` | `distanceToLocation:fromLocation:(there, here)`
+
+#### Mapbox-specific functions
+
+For compatibility with the Mapbox Style Specification, the following functions
+are defined by this SDK for use with style layers. Because these functions are
+not predefined by `NSExpression`, you must use the
+`+[NSExpression expressionForFunction:selectorName:arguments:]` method or the
+`FUNCTION()` format string syntax instead.
+
+<table>
+<thead>
+<tr><th>Selector name</th><th>Target</th><th>Arguments</th><th>Returns</th></tr>
+</thead>
+<tbody>
+<tr>
+ <td><code>boolValue</code></td>
+ <td>
+ An `NSExpression` that evaluates to a number or string.
+ </td>
+ <td></td>
+ <td>
+ A Boolean representation of the target: `FALSE` when then input is an
+ empty string, 0, `FALSE`, `NIL`, or NaN, otherwise `TRUE`.
+ </td>
+</tr>
+<tr>
+ <td><code>mgl_expressionWithContext:</code></td>
+ <td>
+ An `NSExpression` that may contain references to the variables defined in
+ the context dictionary.
+ </td>
+ <td>
+ An `NSDictionary` with `NSString`s as keys and `NSExpression`s as values.
+ Each key is a variable name and each value is the variable’s value within
+ the target expression.
+ </td>
+ <td>
+ The target expression with variable subexpressions replaced with the
+ values defined in the context dictionary.
+ </td>
+</tr>
+<tr>
+ <td><code>mgl_interpolateWithCurveType:parameters:stops:</code></td>
+ <td>
+ An `NSExpression` that evaluates to a number and contains a variable or
+ key path expression.
+ </td>
+ <td>
+ The first argument is one of the following strings denoting curve types:
+ `linear`, `exponential`, or `cubic-bezier`.
+
+ The second argument is an expression providing parameters for the curve:
+
+ <ul>
+ <li>If the curve type is `linear`, the argument is `NIL`.</li>
+ <li>
+ If the curve type is `exponential`, the argument is an expression
+ that evaluates to a number, specifying the base of the exponential
+ interpolation.
+ </li>
+ <li>
+ If the curve type is `cubic-bezier`, the argument is an array or
+ aggregate expression containing four expressions, each evaluating to
+ a number. The four numbers are control points for the cubic Bézier
+ curve.
+ </li>
+ </ul>
+
+ The third argument is an `NSDictionary` object representing the
+ interpolation’s stops, with numeric zoom levels as keys and expressions as
+ values.
+ </td>
+ <td>
+ A value interpolated along the continuous mathematical function defined by
+ the arguments, with the target as the input to the function.
+ </td>
+</tr>
+<tr>
+ <td>
+ <code>mgl_numberWithFallbackValues:</code>,
+ <code>doubleValue</code>,
+ <code>floatValue</code>,
+ <code>decimalValue</code>
+ </td>
+ <td>
+ An `NSExpression` that evaluates to a Boolean value, number, or string.
+ </td>
+ <td>
+ Zero or more `NSExpression`s, each evaluating to a Boolean value or
+ string.
+ </td>
+ <td>
+ A numeric representation of the target:
+ <ul>
+ <li>If the target is `NIL` or `FALSE`, the result is 0.</li>
+ <li>If the target is true, the result is 1.</li>
+ <li>
+ If the target is a string, it is converted to a number as specified
+ by the
+ “<a href="https://tc39.github.io/ecma262/#sec-tonumber-applied-to-the-string-type">ToNumber Applied to the String Type</a>”
+ algorithm of the ECMAScript Language Specification.
+ </li>
+ <li>
+ If multiple values are provided, each one is evaluated in order
+ until the first successful conversion is obtained.
+ </li>
+ </ul>
+ </td>
+</tr>
+<tr>
+ <td><code>mgl_stepWithMinimum:stops:</code></td>
+ <td>
+ An `NSExpression` that evaluates to a number and contains a variable or
+ key path expression.
+ </td>
+ <td>
+ The first argument is an expression that evaluates to a number, specifying
+ the minimum value in case the target is less than any of the stops in the
+ second argument.
+
+ The second argument is an `NSDictionary` object representing the
+ interpolation’s stops, with numeric zoom levels as keys and expressions as
+ values.
+ </td>
+ <td>
+ The output value of the stop whose key is just less than the evaluated
+ target, or the minimum value if the target is less than the least of the
+ stops’ keys.
+ </td>
+</tr>
+<tr>
+ <td><code>stringByAppendingString:</code></td>
+ <td>
+ An `NSExpression` that evaluates to a string.
+ </td>
+ <td>
+ One or more `NSExpression`s, each evaluating to a string.
+ </td>
+ <td>
+ The target string with each of the argument strings appended in order.
+ </td>
+</tr>
+<tr>
+ <td><code>stringValue</code></td>
+ <td>
+ An `NSExpression` that evaluates to a Boolean value, number, or string.
+ </td>
+ <td></td>
+ <td>
+ A string representation of the target:
+ <ul>
+ <li>If the target is `NIL`, the result is the string `null`.</li>
+ <li>
+ If the target is a Boolean value, the result is the string `true` or
+ `false`.
+ </li>
+ <li>
+ If the target is a number, it is converted to a string as specified
+ by the
+ “<a href="https://tc39.github.io/ecma262/#sec-tostring-applied-to-the-number-type">NumberToString</a>”
+ algorithm of the ECMAScript Language Specification.
+ </li>
+ <li>
+ If the target is a color, it is converted to a string of the form
+ `rgba(r,g,b,a)`, where <var>r</var>, <var>g</var>, and <var>b</var>
+ are numerals ranging from 0 to 255 and <var>a</var> ranges from 0 to
+ 1.
+ </li>
+ <li>
+ Otherwise, the target is converted to a string in the format
+ specified by the
+ [`JSON.stringify()`](https://tc39.github.io/ecma262/#sec-json.stringify)
+ function of the ECMAScript Language Specification.
+ </li>
+ </ul>
+ </td>
+</tr>
+</tbody>
+</table>
+
+Some of these functions are defined as methods on their respective target
+classes, but you should not call them directly outside the context of an
+expression, because the result may differ from the evaluated expression’s result
+or may result in undefined behavior.
+
+### Variables
+
+The following variables are defined by this SDK for use with style layers:
+
+<table>
+<thead>
+<tr><th>Variable</th><th>Type</th><th>Meaning</th></tr>
+</thead>
+<tbody>
+<tr>
+ <td><code>$heatmapDensity</code></td>
+ <td>Number</td>
+ <td>
+ The
+ <a href="https://en.wikipedia.org/wiki/Kernel_density_estimation">kernel density estimation</a>
+ of a screen point in a heatmap layer; in other words, a relative measure
+ of how many data points are crowded around a particular pixel. This
+ variable can only be used with the `heatmapColor` property.
+ </td>
+</tr>
+<tr>
+ <td><code>$zoomLevel</code></td>
+ <td>Number</td>
+ <td>
+ The current zoom level. In style layout and paint properties, this
+ variable may only appear as the target of a top-level interpolation or
+ step expression.
+ </td>
+</tr>
+</tbody>
+</table>
+
+In addition to these variables, you can define your own variables and refer to
+them elsewhere in the expression. The syntax for defining a variable makes use
+of a [Mapbox-specific function](#mapbox-specific-functions) that takes an
+`NSDictionary` as an argument:
+
+```objc
+[NSExpression expressionWithFormat:@"FUNCTION($floorCount + 1, 'mgl_expressionWithContext:', %@)",
+ {@"floorCount": @2}];
+```
+
+```swift
+NSExpression(format: "FUNCTION($floorCount + 1, 'mgl_expressionWithContext:', %@)",
+ ["floorCount": 2])
+```
diff --git a/platform/darwin/docs/guides/Using Style Functions at Runtime.md.ejs b/platform/darwin/docs/guides/Using Style Functions at Runtime.md.ejs
index e41ac1c832..61034a674f 100644
--- a/platform/darwin/docs/guides/Using Style Functions at Runtime.md.ejs
+++ b/platform/darwin/docs/guides/Using Style Functions at Runtime.md.ejs
@@ -34,7 +34,7 @@ The documentation for each individual style layer property notes which style fun
## Stops
-Stops are key-value pairs that that determine a style value. With a `MGLCameraSourceFunction` stop, you can use a dictionary with a zoom level for a key and a `MGLStyleValue` for the value. For example, you can use a stops dictionary with zoom levels 0, 10, and 20 as keys, and yellow, orange, and red as the values. A `MGLSourceStyleFunction` uses the relevant attribute value as the key.
+Stops are dictionary keys that are associated with layer attribute values. With feature attribute values as stops, you can use a dictionary with a zoom level for a key and an expression or constant value for the value. For example, you can use a stop dictionary with the zoom levels 0, 10, and 20 as keys and the colors yellow, orange, and red as the values. Alternatively, attribute values can be the keys.
<%- guideExample(guide, 'Stops', os) %>
diff --git a/platform/darwin/docs/theme/assets/css/jazzy.css.scss b/platform/darwin/docs/theme/assets/css/jazzy.css.scss
index ad0a3b7082..103ba601dc 100644
--- a/platform/darwin/docs/theme/assets/css/jazzy.css.scss
+++ b/platform/darwin/docs/theme/assets/css/jazzy.css.scss
@@ -386,6 +386,7 @@ pre code {
.nav-group-task[data-name="MGLStyleFunction"],
.nav-group-task[data-name="MGLStyleLayer"],
.nav-group-task[data-name="MGLTileSource"],
+.nav-group-task[data-name="MGLAbstractShapeSource"],
.nav-group-task[data-name="MGLVectorStyleLayer"] {
.nav-group-task-link::after {
@extend %nav-group-task-gloss;
diff --git a/platform/darwin/mbgl/gl/gl_impl.hpp b/platform/darwin/mbgl/gl/gl_impl.hpp
new file mode 100644
index 0000000000..b4c062a474
--- /dev/null
+++ b/platform/darwin/mbgl/gl/gl_impl.hpp
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "TargetConditionals.h"
+#if TARGET_OS_IPHONE
+ #include <OpenGLES/ES2/gl.h>
+ #include <OpenGLES/ES2/glext.h>
+#elif TARGET_IPHONE_SIMULATOR
+ #include <OpenGLES/ES2/gl.h>
+ #include <OpenGLES/ES2/glext.h>
+#elif TARGET_OS_MAC
+ #include <OpenGL/OpenGL.h>
+ #include <OpenGL/gl.h>
+ #include <OpenGL/glext.h>
+#else
+ #error Unsupported Apple platform
+#endif
diff --git a/platform/darwin/mbgl/util/image+MGLAdditions.hpp b/platform/darwin/mbgl/util/image+MGLAdditions.hpp
index c738b4523d..c5343af4de 100644
--- a/platform/darwin/mbgl/util/image+MGLAdditions.hpp
+++ b/platform/darwin/mbgl/util/image+MGLAdditions.hpp
@@ -5,7 +5,7 @@
#include <CoreGraphics/CGImage.h>
// Creates a CGImage from a PremultipliedImage, taking over the memory ownership.
-CGImageRef CGImageFromMGLPremultipliedImage(mbgl::PremultipliedImage&&);
+CGImageRef CGImageCreateWithMGLPremultipliedImage(mbgl::PremultipliedImage&&);
// Creates a PremultipliedImage by copying the pixels of the CGImage.
// Does not alter the retain count of the supplied CGImage.
diff --git a/platform/darwin/resources/ca.lproj/Foundation.strings b/platform/darwin/resources/ca.lproj/Foundation.strings
index e60546d3c2..06bd2e8261 100644
--- a/platform/darwin/resources/ca.lproj/Foundation.strings
+++ b/platform/darwin/resources/ca.lproj/Foundation.strings
Binary files differ
diff --git a/platform/darwin/resources/de.lproj/Foundation.strings b/platform/darwin/resources/de.lproj/Foundation.strings
index fb4b1e874a..9c7242bb72 100644
--- a/platform/darwin/resources/de.lproj/Foundation.strings
+++ b/platform/darwin/resources/de.lproj/Foundation.strings
Binary files differ
diff --git a/platform/darwin/resources/fr.lproj/Foundation.strings b/platform/darwin/resources/fr.lproj/Foundation.strings
index ed38a264cf..6ad3fc554b 100644
--- a/platform/darwin/resources/fr.lproj/Foundation.strings
+++ b/platform/darwin/resources/fr.lproj/Foundation.strings
Binary files differ
diff --git a/platform/darwin/resources/he.lproj/Foundation.strings b/platform/darwin/resources/he.lproj/Foundation.strings
new file mode 100644
index 0000000000..2f6e24d6e6
--- /dev/null
+++ b/platform/darwin/resources/he.lproj/Foundation.strings
@@ -0,0 +1,297 @@
+/* Clock position format, long: {hours} o’clock */
+"CLOCK_FMT_LONG" = "שעה %@";
+
+/* Clock position format, medium: {hours} o’clock */
+"CLOCK_FMT_MEDIUM" = "שעה %@";
+
+/* Clock position format, short: {hours}:00 */
+"CLOCK_FMT_SHORT" = "%@:00";
+
+/* East, long */
+"COMPASS_E_LONG" = "מזרח";
+
+/* East, short */
+"COMPASS_E_SHORT" = "מז";
+
+/* East by north, long */
+"COMPASS_EbN_LONG" = "east by north";
+
+/* East by north, short */
+"COMPASS_EbN_SHORT" = "EbN";
+
+/* East by south, long */
+"COMPASS_EbS_LONG" = "east by south";
+
+/* East by south, short */
+"COMPASS_EbS_SHORT" = "EbS";
+
+/* East-northeast, long */
+"COMPASS_ENE_LONG" = "מזרח-צפון מזרח";
+
+/* East-northeast, short */
+"COMPASS_ENE_SHORT" = "מז-צ-מז";
+
+/* East-southeast, long */
+"COMPASS_ESE_LONG" = "מזרח-דרום מזרח";
+
+/* East-southeast, short */
+"COMPASS_ESE_SHORT" = "מז-ד-מז";
+
+/* North, long */
+"COMPASS_N_LONG" = "צפון";
+
+/* North, short */
+"COMPASS_N_SHORT" = "צ";
+
+/* North by east, long */
+"COMPASS_NbE_LONG" = "north by east";
+
+/* North by east, short */
+"COMPASS_NbE_SHORT" = "NbE";
+
+/* North by west, long */
+"COMPASS_NbW_LONG" = "north by west";
+
+/* North by west, short */
+"COMPASS_NbW_SHORT" = "NbW";
+
+/* Northeast, long */
+"COMPASS_NE_LONG" = "צפון מזרח";
+
+/* Northeast, short */
+"COMPASS_NE_SHORT" = "צ-מז";
+
+/* Northeast by east, long */
+"COMPASS_NEbE_LONG" = "northeast by east";
+
+/* Northeast by east, short */
+"COMPASS_NEbE_SHORT" = "NEbE";
+
+/* Northeast by north, long */
+"COMPASS_NEbN_LONG" = "northeast by north";
+
+/* Northeast by north, short */
+"COMPASS_NEbN_SHORT" = "NEbN";
+
+/* North-northeast, long */
+"COMPASS_NNE_LONG" = "צפון-צפון מזרח";
+
+/* North-northeast, short */
+"COMPASS_NNE_SHORT" = "צ-צ-מז";
+
+/* North-northwest, long */
+"COMPASS_NNW_LONG" = "צפון-צפון מערב";
+
+/* North-northwest, short */
+"COMPASS_NNW_SHORT" = "צ-צ-מע";
+
+/* Northwest, long */
+"COMPASS_NW_LONG" = "צפון מערב";
+
+/* Northwest, short */
+"COMPASS_NW_SHORT" = "צ-מע";
+
+/* Northwest by north, long */
+"COMPASS_NWbN_LONG" = "northwest by north";
+
+/* Northwest by north, short */
+"COMPASS_NWbN_SHORT" = "NWbN";
+
+/* Northwest by west, long */
+"COMPASS_NWbW_LONG" = "northwest by west";
+
+/* Northwest by west, short */
+"COMPASS_NWbW_SHORT" = "NWbW";
+
+/* South, long */
+"COMPASS_S_LONG" = "דרום";
+
+/* South, short */
+"COMPASS_S_SHORT" = "ד";
+
+/* South by east, long */
+"COMPASS_SbE_LONG" = "south by east";
+
+/* South by east, short */
+"COMPASS_SbE_SHORT" = "SbE";
+
+/* South by west, long */
+"COMPASS_SbW_LONG" = "south by west";
+
+/* South by west, short */
+"COMPASS_SbW_SHORT" = "SbW";
+
+/* Southeast, long */
+"COMPASS_SE_LONG" = "דרום מזרח";
+
+/* Southeast, short */
+"COMPASS_SE_SHORT" = "ד-מז";
+
+/* Southeast by east, long */
+"COMPASS_SEbE_LONG" = "southeast by east";
+
+/* Southeast by east, short */
+"COMPASS_SEbE_SHORT" = "SEbE";
+
+/* Southeast by south, long */
+"COMPASS_SEbS_LONG" = "southeast by south";
+
+/* Southeast by south, short */
+"COMPASS_SEbS_SHORT" = "SEbS";
+
+/* South-southeast, long */
+"COMPASS_SSE_LONG" = "דרום-דרום מזרח";
+
+/* South-southeast, short */
+"COMPASS_SSE_SHORT" = "ד-ד-מז";
+
+/* South-southwest, long */
+"COMPASS_SSW_LONG" = "דרום-דרום מערב";
+
+/* South-southwest, short */
+"COMPASS_SSW_SHORT" = "ד-ד-מע";
+
+/* Southwest, long */
+"COMPASS_SW_LONG" = "דרום מערב";
+
+/* Southwest, short */
+"COMPASS_SW_SHORT" = "ד-מע";
+
+/* Southwest by south, long */
+"COMPASS_SWbS_LONG" = "southwest by south";
+
+/* Southwest by south, short */
+"COMPASS_SWbS_SHORT" = "SWbS";
+
+/* Southwest by west, long */
+"COMPASS_SWbW_LONG" = "southwest by west";
+
+/* Southwest by west, short */
+"COMPASS_SWbW_SHORT" = "SWbW";
+
+/* West, long */
+"COMPASS_W_LONG" = "מערב";
+
+/* West, short */
+"COMPASS_W_SHORT" = "מע";
+
+/* West by north, long */
+"COMPASS_WbN_LONG" = "west by north";
+
+/* West by north, short */
+"COMPASS_WbN_SHORT" = "WbN";
+
+/* West by south, long */
+"COMPASS_WbS_LONG" = "west by south";
+
+/* West by south, short */
+"COMPASS_WbS_SHORT" = "WbS";
+
+/* West-northwest, long */
+"COMPASS_WNW_LONG" = "מערב-צפון מערב";
+
+/* West-northwest, short */
+"COMPASS_WNW_SHORT" = "מע-צ-מע";
+
+/* West-southwest, long */
+"COMPASS_WSW_LONG" = "מערב-דרום מערב";
+
+/* West-southwest, short */
+"COMPASS_WSW_SHORT" = "מע-ד-מע";
+
+/* Degrees format, long */
+"COORD_DEG_LONG" = "%d מעלה(ות)";
+
+/* Degrees format, medium: {degrees} */
+"COORD_DEG_MEDIUM" = "%d°";
+
+/* Degrees format, short: {degrees} */
+"COORD_DEG_SHORT" = "%d°";
+
+/* Coordinate format, long: {degrees}{minutes} */
+"COORD_DM_LONG" = "%1$@ ו %2$@";
+
+/* Coordinate format, medium: {degrees}{minutes} */
+"COORD_DM_MEDIUM" = "%1$@%2$@";
+
+/* Coordinate format, short: {degrees}{minutes} */
+"COORD_DM_SHORT" = "%1$@%2$@";
+
+/* Coordinate format, long: {degrees}{minutes}{seconds} */
+"COORD_DMS_LONG" = "%1$@, %2$@, ו %3$@";
+
+/* Coordinate format, medium: {degrees}{minutes}{seconds} */
+"COORD_DMS_MEDIUM" = "%1$@%2$@%3$@";
+
+/* Coordinate format, short: {degrees}{minutes}{seconds} */
+"COORD_DMS_SHORT" = "%1$@%2$@%3$@";
+
+/* East longitude format, long: {longitude} */
+"COORD_E_LONG" = "%@ מזרחה";
+
+/* East longitude format, medium: {longitude} */
+"COORD_E_MEDIUM" = "%@ מזרחה";
+
+/* East longitude format, short: {longitude} */
+"COORD_E_SHORT" = "%@מז";
+
+/* Coordinate pair format, long: {latitude}, {longitude} */
+"COORD_FMT_LONG" = "%1$@ על %2$@";
+
+/* Coordinate pair format, medium: {latitude}, {longitude} */
+"COORD_FMT_MEDIUM" = "%1$@, %2$@";
+
+/* Coordinate pair format, short: {latitude}, {longitude} */
+"COORD_FMT_SHORT" = "%1$@, %2$@";
+
+/* Minutes format, long */
+"COORD_MIN_LONG" = "%d דקה(ות)";
+
+/* Minutes format, medium: {minutes} */
+"COORD_MIN_MEDIUM" = "%d′";
+
+/* Minutes format, short: {minutes} */
+"COORD_MIN_SHORT" = "%d′";
+
+/* North latitude format, long: {latitude} */
+"COORD_N_LONG" = "%@ צפונה";
+
+/* North latitude format, medium: {latitude} */
+"COORD_N_MEDIUM" = "%@ צפונה";
+
+/* North latitude format, short: {latitude} */
+"COORD_N_SHORT" = "%@צ";
+
+/* South latitude format, long: {latitude} */
+"COORD_S_LONG" = "%@ דרומה";
+
+/* South latitude format, medium: {latitude} */
+"COORD_S_MEDIUM" = "%@ דרומה";
+
+/* South latitude format, short: {latitude} */
+"COORD_S_SHORT" = "%@ד";
+
+/* Seconds format, long */
+"COORD_SEC_LONG" = "%d שניה(ות)";
+
+/* Seconds format, medium: {seconds} */
+"COORD_SEC_MEDIUM" = "%d″";
+
+/* Seconds format, short: {seconds} */
+"COORD_SEC_SHORT" = "%d″";
+
+/* West longitude format, long: {longitude} */
+"COORD_W_LONG" = "%@ מערב";
+
+/* West longitude format, medium: {longitude} */
+"COORD_W_MEDIUM" = "%@ מערב";
+
+/* West longitude format, short: {longitude} */
+"COORD_W_SHORT" = "%@מע";
+
+/* OpenStreetMap full name attribution */
+"OSM_FULL_NAME" = "OpenStreetMap";
+
+/* OpenStreetMap short name attribution */
+"OSM_SHORT_NAME" = "OSM";
+
diff --git a/platform/darwin/resources/ja.lproj/Foundation.strings b/platform/darwin/resources/ja.lproj/Foundation.strings
index 52dda1557f..3588f38e48 100644
--- a/platform/darwin/resources/ja.lproj/Foundation.strings
+++ b/platform/darwin/resources/ja.lproj/Foundation.strings
Binary files differ
diff --git a/platform/darwin/resources/lt.lproj/Foundation.strings b/platform/darwin/resources/lt.lproj/Foundation.strings
index 1151a559bd..2bd0baa210 100644
--- a/platform/darwin/resources/lt.lproj/Foundation.strings
+++ b/platform/darwin/resources/lt.lproj/Foundation.strings
Binary files differ
diff --git a/platform/darwin/resources/pt-BR.lproj/Foundation.strings b/platform/darwin/resources/pt-BR.lproj/Foundation.strings
index bf0e126d79..331b61dd77 100644
--- a/platform/darwin/resources/pt-BR.lproj/Foundation.strings
+++ b/platform/darwin/resources/pt-BR.lproj/Foundation.strings
Binary files differ
diff --git a/platform/darwin/resources/ru.lproj/Foundation.strings b/platform/darwin/resources/ru.lproj/Foundation.strings
index f029952595..4842496377 100644
--- a/platform/darwin/resources/ru.lproj/Foundation.strings
+++ b/platform/darwin/resources/ru.lproj/Foundation.strings
Binary files differ
diff --git a/platform/darwin/resources/sv.lproj/Foundation.strings b/platform/darwin/resources/sv.lproj/Foundation.strings
index 4391ec7f95..e3e7f931a8 100644
--- a/platform/darwin/resources/sv.lproj/Foundation.strings
+++ b/platform/darwin/resources/sv.lproj/Foundation.strings
Binary files differ
diff --git a/platform/darwin/resources/uk.lproj/Foundation.strings b/platform/darwin/resources/uk.lproj/Foundation.strings
index d9d9ad32e1..ded065c564 100644
--- a/platform/darwin/resources/uk.lproj/Foundation.strings
+++ b/platform/darwin/resources/uk.lproj/Foundation.strings
Binary files differ
diff --git a/platform/darwin/resources/zh-Hans.lproj/Foundation.strings b/platform/darwin/resources/zh-Hans.lproj/Foundation.strings
index e69b65f2ac..0c57a70d8a 100644
--- a/platform/darwin/resources/zh-Hans.lproj/Foundation.strings
+++ b/platform/darwin/resources/zh-Hans.lproj/Foundation.strings
Binary files differ
diff --git a/platform/darwin/resources/zh-Hant.lproj/Foundation.strings b/platform/darwin/resources/zh-Hant.lproj/Foundation.strings
index 2fa92e1c6e..7946dcf7cd 100644
--- a/platform/darwin/resources/zh-Hant.lproj/Foundation.strings
+++ b/platform/darwin/resources/zh-Hant.lproj/Foundation.strings
Binary files differ
diff --git a/platform/darwin/scripts/generate-style-code.js b/platform/darwin/scripts/generate-style-code.js
index 4ee86e65da..53a668d10b 100644..100755
--- a/platform/darwin/scripts/generate-style-code.js
+++ b/platform/darwin/scripts/generate-style-code.js
@@ -1,3 +1,4 @@
+#!/usr/bin/env node
'use strict';
const fs = require('fs');
@@ -11,7 +12,7 @@ const cocoaConventions = require('./style-spec-cocoa-conventions-v8.json');
const prefix = 'MGL';
const suffix = 'StyleLayer';
-let spec = _.merge(require('../../../mapbox-gl-js/src/style-spec/reference/v8'), require('./style-spec-overrides-v8.json'));
+let spec = _.merge(require('../../../scripts/style-spec'), require('./style-spec-overrides-v8.json'));
// Rename properties and keep `original` for use with setters and getters
_.forOwn(cocoaConventions, function (properties, kind) {
@@ -27,8 +28,10 @@ _.forOwn(cocoaConventions, function (properties, kind) {
delete spec[kind][oldName];
spec[kind][newName] = property;
- // Update requirements in other properties.
- let updateRequirements = function (property, name) {
+ // Update cross-references to this property in other properties'
+ // documentation and requirements.
+ let renameCrossReferences = function (property, name) {
+ property.doc = property.doc.replace(new RegExp('`' + oldName + '`', 'g'), '`' + newName + '`');
let requires = property.requires || [];
for (let i = 0; i < requires.length; i++) {
if (requires[i] === oldName) {
@@ -45,8 +48,8 @@ _.forOwn(cocoaConventions, function (properties, kind) {
}
}
};
- _.forOwn(spec[kind.replace(/^layout_/, 'paint_')], updateRequirements);
- _.forOwn(spec[kind.replace(/^paint_/, 'layout_')], updateRequirements);
+ _.forOwn(spec[kind.replace(/^layout_/, 'paint_')], renameCrossReferences);
+ _.forOwn(spec[kind.replace(/^paint_/, 'layout_')], renameCrossReferences);
})
});
@@ -92,37 +95,42 @@ global.testImplementation = function (property, layerType, isFunction) {
return `layer.${objCName(property)} = [MGLRuntimeStylingHelper ${helperMsg}];`;
};
-global.objCTestValue = function (property, layerType, indent) {
+global.objCTestValue = function (property, layerType, arraysAsStructs, indent) {
let propertyName = originalPropertyName(property);
switch (property.type) {
case 'boolean':
- return property.default ? '@NO' : '@YES';
+ return property.default ? '@"false"' : '@"true"';
case 'number':
- return '@0xff';
+ return '@"0xff"';
case 'string':
- return `@"${_.startCase(propertyName)}"`;
+ return `@"'${_.startCase(propertyName)}'"`;
case 'enum':
- let type = objCType(layerType, property.name);
- let value = `${type}${camelize(_.last(_.keys(property.values)))}`;
- return `[NSValue valueWith${type}:${value}]`;
+ return `@"'${_.last(_.keys(property.values))}'"`;
case 'color':
- return '[MGLColor redColor]';
+ return '@"%@", [MGLColor redColor]';
case 'array':
switch (arrayType(property)) {
case 'dasharray':
- return '@[@1, @2]';
+ return '@"{1, 2}"';
case 'font':
- return `@[@"${_.startCase(propertyName)}", @"${_.startCase(_.reverse(propertyName.split('')).join(''))}"]`;
+ return `@"{'${_.startCase(propertyName)}', '${_.startCase(_.reverse(propertyName.split('')).join(''))}'}"`;
case 'padding': {
- let iosValue = '[NSValue valueWithUIEdgeInsets:UIEdgeInsetsMake(1, 1, 1, 1)]'.indent(indent * 4);
- let macosValue = '[NSValue valueWithEdgeInsets:NSEdgeInsetsMake(1, 1, 1, 1)]'.indent(indent * 4);
- return `\n#if TARGET_OS_IPHONE\n${iosValue}\n#else\n${macosValue}\n#endif\n${''.indent((indent - 1) * 4)}`;
+ if (arraysAsStructs) {
+ let iosValue = '[NSValue valueWithUIEdgeInsets:UIEdgeInsetsMake(1, 1, 1, 1)]'.indent(indent * 4);
+ let macosValue = '[NSValue valueWithEdgeInsets:NSEdgeInsetsMake(1, 1, 1, 1)]'.indent(indent * 4);
+ return `@"%@",\n#if TARGET_OS_IPHONE\n${iosValue}\n#else\n${macosValue}\n#endif\n${''.indent((indent - 1) * 4)}`;
+ }
+ return '@"{1, 1, 1, 1}"';
}
case 'offset':
- case 'translate':
- let iosValue = '[NSValue valueWithCGVector:CGVectorMake(1, 1)]'.indent(indent * 4);
- let macosValue = '[NSValue valueWithMGLVector:CGVectorMake(1, -1)]'.indent(indent * 4);
- return `\n#if TARGET_OS_IPHONE\n${iosValue}\n#else\n${macosValue}\n#endif\n${''.indent((indent - 1) * 4)}`;
+ case 'translate': {
+ if (arraysAsStructs) {
+ let iosValue = '[NSValue valueWithCGVector:CGVectorMake(1, 1)]'.indent(indent * 4);
+ let macosValue = '[NSValue valueWithMGLVector:CGVectorMake(1, -1)]'.indent(indent * 4);
+ return `@"%@",\n#if TARGET_OS_IPHONE\n${iosValue}\n#else\n${macosValue}\n#endif\n${''.indent((indent - 1) * 4)}`;
+ }
+ return '@"{1, 1}"';
+ }
default:
throw new Error(`unknown array type for ${property.name}`);
}
@@ -245,6 +253,13 @@ global.propertyDoc = function (propertyName, property, layerType, kind) {
// Format everything else: our property name & its possible values.
// Requires symbols to be surrounded by backticks.
doc = doc.replace(/`(.+?)`/g, function (m, symbol, offset, str) {
+ if (kind === 'enum') {
+ let layoutProperties = spec[`layout_${layerType}`] || [];
+ let paintProperties = spec[`paint_${layerType}`] || [];
+ if (symbol in layoutProperties || symbol in paintProperties) {
+ return '`MGL' + camelize(layerType) + 'StyleLayer.' + camelizeWithLeadingLowercase(symbol) + '`';
+ }
+ }
if ('values' in property && Object.keys(property.values).indexOf(symbol) !== -1) {
let objCType = global.objCType(layerType, property.name);
return '`' + `${objCType}${camelize(symbol)}` + '`';
@@ -284,29 +299,24 @@ global.propertyDoc = function (propertyName, property, layerType, kind) {
}
doc += `\n\nThis attribute corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#${anchor}"><code>${property.original}</code></a> layout property in the Mapbox Style Specification.`;
}
- doc += '\n\nYou can set this property to an instance of:\n\n' +
- '* `MGLConstantStyleValue`\n';
+ doc += '\n\nYou can set this property to an expression containing any of the following:\n\n';
+ doc += `* Constant ${describeType(property)} values\n`;
+ if (property.type === 'enum') {
+ doc += '* Any of the following constant string values:\n';
+ doc += Object.keys(property.values).map(value => ' * `' + value + '`: ' + property.values[value].doc).join('\n') + '\n';
+ }
+ doc += '* Predefined functions, including mathematical and string operators\n' +
+ '* Conditional expressions\n' +
+ '* Variable assignments and references to assigned variables\n';
+ const inputVariable = property.name === 'heatmap-color' ? '$heatmapDensity' : '$zoomLevel';
if (property["property-function"]) {
- doc += '* `MGLCameraStyleFunction` with an interpolation mode of:\n' +
- ' * `MGLInterpolationModeExponential`\n' +
- ' * `MGLInterpolationModeInterval`\n' +
- '* `MGLSourceStyleFunction` with an interpolation mode of:\n' +
- ' * `MGLInterpolationModeExponential`\n' +
- ' * `MGLInterpolationModeInterval`\n' +
- ' * `MGLInterpolationModeCategorical`\n' +
- ' * `MGLInterpolationModeIdentity`\n' +
- '* `MGLCompositeStyleFunction` with an interpolation mode of:\n' +
- ' * `MGLInterpolationModeExponential`\n' +
- ' * `MGLInterpolationModeInterval`\n' +
- ' * `MGLInterpolationModeCategorical`\n';
+ doc += `* Interpolation and step functions applied to the \`${inputVariable}\` variable and/or feature attributes\n`;
+ } else if (property.function === "interpolated") {
+ doc += `* Interpolation and step functions applied to the \`${inputVariable}\` variable\n\n` +
+ 'This property does not support applying interpolation or step functions to feature attributes.';
} else {
- if (property.function === "interpolated") {
- doc += '* `MGLCameraStyleFunction` with an interpolation mode of:\n' +
- ' * `MGLInterpolationModeExponential`\n' +
- ' * `MGLInterpolationModeInterval`\n';
- } else {
- doc += '* `MGLCameraStyleFunction` with an interpolation mode of `MGLInterpolationModeInterval`\n';
- }
+ doc += `* Step functions applied to the \`${inputVariable}\` variable\n\n` +
+ `This property does not support applying interpolation functions to the \`${inputVariable}\` variable or applying interpolation or step functions to feature attributes.`;
}
}
return doc;
@@ -320,7 +330,7 @@ global.propertyReqs = function (property, propertiesByName, type) {
return '`' + camelizeWithLeadingLowercase(req['!']) + '` is set to `nil`';
} else {
let name = Object.keys(req)[0];
- return '`' + camelizeWithLeadingLowercase(name) + '` is set to an `MGLStyleValue` object containing ' + describeValue(req[name], propertiesByName[name], type);
+ return '`' + camelizeWithLeadingLowercase(name) + '` is set to an expression that evaluates to ' + describeValue(req[name], propertiesByName[name], type);
}
}).join(', and ') + '. Otherwise, it is ignored.';
};
@@ -335,12 +345,55 @@ global.parseColor = function (str) {
};
};
+global.describeType = function (property) {
+ switch (property.type) {
+ case 'boolean':
+ return 'Boolean';
+ case 'number':
+ return 'numeric';
+ case 'string':
+ return 'string';
+ case 'enum':
+ return '`MGL' + camelize(property.name) + '`';
+ case 'color':
+ return '`UIColor`';
+ case 'array':
+ switch (arrayType(property)) {
+ case 'padding':
+ return '`UIEdgeInsets`';
+ case 'offset':
+ case 'translate':
+ return '`CGVector`';
+ case 'position':
+ return '`MGLSphericalPosition`';
+ default:
+ return 'array';
+ }
+ break;
+ default:
+ throw new Error(`unknown type for ${property.name}`);
+ }
+}
+
global.describeValue = function (value, property, layerType) {
+ if (Array.isArray(value) && property.type !== 'array' && property.type !== 'enum') {
+ switch (value[0]) {
+ case 'interpolate': {
+ let curveType = value[1][0];
+ let minimum = describeValue(value[3 + value.length % 2], property, layerType);
+ let maximum = describeValue(_.last(value), property, layerType);
+ return `${curveType.match(/^[aeiou]/i) ? 'an' : 'a'} ${curveType} interpolation expression ranging from ${minimum} to ${maximum}`;
+ }
+ default:
+ throw new Error(`No description available for ${value[0]} expression in ${property.name} of ${layerType}.`);
+ }
+ }
+
switch (property.type) {
case 'boolean':
- return 'an `NSNumber` object containing ' + (value ? '`YES`' : '`NO`');
+ return value ? '`YES`' : '`NO`';
case 'number':
- return 'an `NSNumber` object containing the float `' + value + '`';
+ return 'the float `' + value + '`';
case 'string':
if (value === '') {
return 'the empty string';
@@ -349,21 +402,18 @@ global.describeValue = function (value, property, layerType) {
case 'enum':
let displayValue;
if (Array.isArray(value)) {
- let separator = (value.length === 2) ? ' ' : ', ';
- displayValue = value.map((possibleValue, i) => {
- let conjunction = '';
- if (value.length === 2 && i === 0) conjunction = 'either ';
- if (i === value.length - 1) conjunction = 'or ';
- let objCType = global.objCType(layerType, property.name);
- return `${conjunction}\`${objCType}${camelize(possibleValue)}\``;
- }).join(separator);
- } else if (property['light-property']) {
- displayValue = `\`${prefix}Light${camelize(property.name)}${camelize(value)}\``;
+ let separator = (value.length === 2) ? ' ' : ', ';
+ displayValue = value.map((possibleValue, i) => {
+ let conjunction = '';
+ if (value.length === 2 && i === 0) conjunction = 'either ';
+ if (i === value.length - 1) conjunction = 'or ';
+ let objCType = global.objCType(layerType, property.name);
+ return `${conjunction}\`${objCType}${camelize(possibleValue)}\``;
+ }).join(separator);
} else {
- let objCType = global.objCType(layerType, property.name);
- displayValue = `\`${objCType}${camelize(value)}\``;
+ displayValue = `\`${value}\``;
}
- return `an \`NSValue\` object containing ${displayValue}`;
+ return displayValue;
case 'color':
let color = parseColor(value);
if (!color) {
@@ -404,7 +454,11 @@ global.describeValue = function (value, property, layerType) {
};
global.propertyDefault = function (property, layerType) {
- return 'an `MGLStyleValue` object containing ' + describeValue(property.default, property, layerType);
+ if (property.name === 'heatmap-color') {
+ return 'an expression that evaluates to a rainbow color scale from blue to red';
+ } else {
+ return 'an expression that evaluates to ' + describeValue(property.default, property, layerType);
+ }
};
global.originalPropertyName = function (property) {
@@ -564,9 +618,9 @@ const templatesMD = ejs.compile(fs.readFileSync('platform/darwin/docs/guides/Til
const lightH = ejs.compile(fs.readFileSync('platform/darwin/src/MGLLight.h.ejs', 'utf8'), {strict: true});
const lightM = ejs.compile(fs.readFileSync('platform/darwin/src/MGLLight.mm.ejs', 'utf8'), {strict: true});
const testLight = ejs.compile(fs.readFileSync('platform/darwin/test/MGLLightTest.mm.ejs', 'utf8'), { strict: true});
-fs.writeFileSync(`platform/darwin/src/MGLLight.h`, duplicatePlatformDecls(lightH({ properties: lightProperties, doc: lightDoc, type: lightType })));
-fs.writeFileSync(`platform/darwin/src/MGLLight.mm`, lightM({ properties: lightProperties, doc: lightDoc, type: lightType }));
-fs.writeFileSync(`platform/darwin/test/MGLLightTest.mm`, testLight({ properties: lightProperties, doc: lightDoc, type: lightType }));
+writeIfModified(`platform/darwin/src/MGLLight.h`, duplicatePlatformDecls(lightH({ properties: lightProperties, doc: lightDoc, type: lightType })));
+writeIfModified(`platform/darwin/src/MGLLight.mm`, lightM({ properties: lightProperties, doc: lightDoc, type: lightType }));
+writeIfModified(`platform/darwin/test/MGLLightTest.mm`, testLight({ properties: lightProperties, doc: lightDoc, type: lightType }));
const layers = _(spec.layer.type.values).map((value, layerType) => {
@@ -639,9 +693,9 @@ for (var layer of layers) {
renamedPropertiesByLayerType[layer.type] = renamedProperties;
}
- fs.writeFileSync(`platform/darwin/src/${prefix}${camelize(layer.type)}${suffix}.h`, duplicatePlatformDecls(layerH(layer)));
- fs.writeFileSync(`platform/darwin/src/${prefix}${camelize(layer.type)}${suffix}.mm`, layerM(layer));
- fs.writeFileSync(`platform/darwin/test/${prefix}${camelize(layer.type)}${suffix}Tests.mm`, testLayers(layer));
+ writeIfModified(`platform/darwin/src/${prefix}${camelize(layer.type)}${suffix}.h`, duplicatePlatformDecls(layerH(layer)));
+ writeIfModified(`platform/darwin/src/${prefix}${camelize(layer.type)}${suffix}.mm`, layerM(layer));
+ writeIfModified(`platform/darwin/test/${prefix}${camelize(layer.type)}${suffix}Tests.mm`, testLayers(layer));
}
// Extract examples for guides from unit tests.
@@ -679,25 +733,25 @@ global.guideExample = function (guide, exampleId, os) {
return '```swift\n' + example + '\n```';
};
-fs.writeFileSync(`platform/ios/docs/guides/For Style Authors.md`, forStyleAuthorsMD({
+writeIfModified(`platform/ios/docs/guides/For Style Authors.md`, forStyleAuthorsMD({
os: 'iOS',
renamedProperties: renamedPropertiesByLayerType,
layers: layers,
}));
-fs.writeFileSync(`platform/macos/docs/guides/For Style Authors.md`, forStyleAuthorsMD({
+writeIfModified(`platform/macos/docs/guides/For Style Authors.md`, forStyleAuthorsMD({
os: 'macOS',
renamedProperties: renamedPropertiesByLayerType,
layers: layers,
}));
-fs.writeFileSync(`platform/ios/docs/guides/Using Style Functions at Runtime.md`, ddsGuideMD({
+writeIfModified(`platform/ios/docs/guides/Using Style Functions at Runtime.md`, ddsGuideMD({
os: 'iOS',
}));
-fs.writeFileSync(`platform/macos/docs/guides/Using Style Functions at Runtime.md`, ddsGuideMD({
+writeIfModified(`platform/macos/docs/guides/Using Style Functions at Runtime.md`, ddsGuideMD({
os: 'macOS',
}));
-fs.writeFileSync(`platform/ios/docs/guides/Tile URL Templates.md`, templatesMD({
+writeIfModified(`platform/ios/docs/guides/Tile URL Templates.md`, templatesMD({
os: 'iOS',
}));
-fs.writeFileSync(`platform/macos/docs/guides/Tile URL Templates.md`, templatesMD({
+writeIfModified(`platform/macos/docs/guides/Tile URL Templates.md`, templatesMD({
os: 'macOS',
}));
diff --git a/platform/darwin/scripts/style-spec-overrides-v8.json b/platform/darwin/scripts/style-spec-overrides-v8.json
index 67a6641fe7..b0c50a06f8 100644
--- a/platform/darwin/scripts/style-spec-overrides-v8.json
+++ b/platform/darwin/scripts/style-spec-overrides-v8.json
@@ -23,8 +23,14 @@
"circle": {
"doc": "An `MGLCircleStyleLayer` is a style layer that renders one or more filled circles on the map.\n\nUse a circle style layer to configure the visual appearance of point or point collection features in vector tiles loaded by an `MGLVectorSource` object or `MGLPointAnnotation`, `MGLPointFeature`, `MGLPointCollection`, or `MGLPointCollectionFeature` instances in an `MGLShapeSource` object.\n\nA circle style layer renders circles whose radii are measured in screen units. To display circles on the map whose radii correspond to real-world distances, use many-sided regular polygons and configure their appearance using an `MGLFillStyleLayer` object."
},
+ "heatmap": {
+ "doc": "An `MGLHeatmapStyleLayer` is a style layer that renders a <a href=\"https://en.wikipedia.org/wiki/Heat_map\">heatmap</a>.\n\nA heatmap visualizes the spatial distribution of a large, dense set of point data, using color to avoid cluttering the map with individual points at low zoom levels. The points are weighted by an attribute you specify. Use a heatmap style layer in conjunction with point or point collection features in vector tiles loaded by an `MGLVectorSource` object or `MGLPointAnnotation`, `MGLPointFeature`, `MGLPointCollection`, or `MGLPointCollectionFeature` instances in an `MGLShapeSource` object.\n\nConsider accompanying a heatmap style layer with an `MGLCircleStyleLayer` or `MGLSymbolStyleLayer` at high zoom levels. If you are unsure whether the point data in an `MGLShapeSource` is dense enough to warrant a heatmap, you can alternatively cluster the source using the `MGLShapeSourceOptionClustered` option and render the data using an `MGLCircleStyleLayer` or `MGLSymbolStyleLayer`."
+ },
"raster": {
- "doc": "An `MGLRasterStyleLayer` is a style layer that renders raster tiles on the map.\n\nUse a raster style layer to configure the color parameters of raster tiles loaded by an `MGLRasterSource` object. For example, you could use a raster style layer to render <a href=\"https://www.mapbox.com/satellite/\">Mapbox Satellite</a> imagery, a <a href=\"https://www.mapbox.com/help/define-tileset/#raster-tilesets\">raster tile set</a> uploaded to Mapbox Studio, or a raster map authored in <a href=\"https://tilemill-project.github.io/tilemill/\">TileMill</a>, the classic Mapbox Editor, or Mapbox Studio Classic."
+ "doc": "An `MGLRasterStyleLayer` is a style layer that renders georeferenced raster imagery on the map, especially raster tiles.\n\nUse a raster style layer to configure the color parameters of raster tiles loaded by an `MGLRasterSource` object or raster images loaded by an `MGLImageSource` object. For example, you could use a raster style layer to render <a href=\"https://www.mapbox.com/satellite/\">Mapbox Satellite</a> imagery, a <a href=\"https://www.mapbox.com/help/define-tileset/#raster-tilesets\">raster tile set</a> uploaded to Mapbox Studio, or a raster map authored in <a href=\"https://tilemill-project.github.io/tilemill/\">TileMill</a>, the classic Mapbox Editor, or Mapbox Studio Classic.\n\nRaster images may also be used as icons or patterns in a style layer. To register an image for use as an icon or pattern, use the `-[MGLStyle setImage:forName:]` method. To configure a point annotation’s image, use the `MGLAnnotationImage` class."
+ },
+ "hillshade": {
+ "doc": "An `MGLHillshadeStyleLayer` is a style layer that renders raster <a href=\"https://en.wikipedia.org/wiki/Digital_elevation_model\">digital elevation model</a> (DEM) tiles on the map.\n\nUse a hillshade style layer to configure the color parameters of raster tiles loaded by an `MGLRasterDEMSource` object. For example, you could use a hillshade style layer to render <a href=\"https://www.mapbox.com/help/access-elevation-data/#mapbox-terrain-rgb\">Mapbox Terrain-RGB</a> data.\n\nTo display posterized hillshading based on vector shapes, as with the <a href=\"https://www.mapbox.com/vector-tiles/mapbox-terrain/\">Mapbox Terrain</a> source, use an `MGLVectorSource` object in conjunction with several `MGLFillStyleLayer` objects."
},
"background": {
"doc": "An `MGLBackgroundStyleLayer` is a style layer that covers the entire map. Use a background style layer to configure a color or pattern to show below all other map content. If the style’s other layers use the Mapbox Streets source, the background style layer is responsible for drawing land, whereas the oceans and other bodies of water are drawn by `MGLFillStyleLayer` objects.\n\nA background style layer is typically the bottommost layer in a style, because it covers the entire map and can occlude any layers below it. You can therefore access it by getting the last item in the `MGLStyle.layers` array.\n\nIf the background style layer is transparent or omitted from the style, any portion of the map view that does not show another style layer is transparent."
@@ -39,6 +45,12 @@
"icon-offset": {
"doc": "Offset distance of icon from its anchor."
},
+ "icon-image": {
+ "doc": "Name of image in sprite to use for drawing an image background. Within literal values, attribute names enclosed in curly brackets (e.g. `{token}`) are replaced with the value of the named attribute. Expressions do not support this syntax; for equivalent functionality in expressions, use `stringByAppendingString:` and key path expressions."
+ },
+ "text-field": {
+ "doc": "Value to use for a text label. Within literal values, attribute names enclosed in curly brackets (e.g. `{token}`) are replaced with the value of the named attribute. Expressions do not support this syntax; for equivalent functionality in expressions, use `stringByAppendingString:` and key path expressions."
+ },
"text-font": {
"doc": "An array of font face names used to display the text.\n\nEach font name must be included in the `{fontstack}` portion of the JSON stylesheet’s <a href=\"https://www.mapbox.com/mapbox-gl-style-spec/#glyphs\"><code>glyphs</code></a> property. You can register a custom font when designing the style in Mapbox Studio. Fonts installed on the system are not used.\n\nThe first font named in the array is applied to the text. For each character in the text, if the first font lacks a glyph for the character, the next font is applied as a fallback, and so on."
},
@@ -70,6 +82,11 @@
"doc": "The base color of this layer. The extrusion's surfaces will be shaded differently based on this color in combination with the `light` settings. If this color is specified with an alpha component, the alpha component will be ignored; use `fill-extrusion-opacity` to set layer opacityco."
}
},
+ "paint_heatmap": {
+ "heatmap-color": {
+ "doc": "Defines the color of each pixel based on its density value in a heatmap. Should be an expression that uses `$heatmapDensity` as input."
+ }
+ },
"paint_line": {
"line-pattern": {
"doc": "Name of image in style images to use for drawing image lines. For seamless patterns, image width must be a factor of two (2, 4, 8, ..., 512)."
@@ -105,4 +122,4 @@
"doc": "Distance that the text's anchor is moved from its original placement."
}
}
-} \ No newline at end of file
+}
diff --git a/platform/darwin/scripts/update-examples.js b/platform/darwin/scripts/update-examples.js
index d453c0e4ba..f87ed07288 100644..100755
--- a/platform/darwin/scripts/update-examples.js
+++ b/platform/darwin/scripts/update-examples.js
@@ -1,9 +1,12 @@
+#!/usr/bin/env node
'use strict';
const fs = require('fs');
const execFileSync = require('child_process').execFileSync;
const _ = require('lodash');
+require('../../../scripts/style-code');
+
const examplesSrc = fs.readFileSync('platform/darwin/test/MGLDocumentationExampleTests.swift', 'utf8');
// Regex extracts the following block
@@ -124,14 +127,7 @@ function completeExamples(os) {
}
// Write out the modified file contents.
- if (src === newSrc) {
- console.log('Skipping', path);
- } else {
- console.log('Updating', path);
- if (['0', 'false'].indexOf(process.env.DRY_RUN || '0') !== -1) {
- fs.writeFileSync(path, newSrc);
- }
- }
+ writeIfModified(path, newSrc)
});
});
}
diff --git a/platform/darwin/src/MGLAbstractShapeSource.h b/platform/darwin/src/MGLAbstractShapeSource.h
new file mode 100644
index 0000000000..d61f41fbf6
--- /dev/null
+++ b/platform/darwin/src/MGLAbstractShapeSource.h
@@ -0,0 +1,124 @@
+#import "MGLSource.h"
+
+/**
+ Options for `MGLShapeSource` objects.
+ */
+typedef NSString *MGLShapeSourceOption NS_STRING_ENUM;
+
+/**
+ An `NSNumber` object containing a Boolean enabling or disabling clustering.
+ If the `shape` property contains point shapes, setting this option to
+ `YES` clusters the points by radius into groups. The default value is `NO`.
+
+ This attribute corresponds to the
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-cluster"><code>cluster</code></a>
+ source property in the Mapbox Style Specification.
+
+ This option only affects point features within a shape source.
+ */
+extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionClustered;
+
+/**
+ An `NSNumber` object containing an integer; specifies the radius of each
+ cluster if clustering is enabled. A value of 512 produces a radius equal to
+ the width of a tile. The default value is 50.
+ */
+extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionClusterRadius;
+
+/**
+ An `NSNumber` object containing an integer; specifies the maximum zoom level at
+ which to cluster points if clustering is enabled. Defaults to one zoom level
+ less than the value of `MGLShapeSourceOptionMaximumZoomLevel` so that, at the
+ maximum zoom level, the shapes are not clustered.
+
+ This attribute corresponds to the
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-clusterMaxZoom"><code>clusterMaxZoom</code></a>
+ source property in the Mapbox Style Specification.
+ */
+extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevelForClustering;
+
+/**
+ An `NSNumber` object containing an integer; specifies the minimum zoom level at
+ which to create vector tiles. The default value is 0.
+
+ This attribute corresponds to the
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-minzoom"><code>minzoom</code></a>
+ source property in the Mapbox Style Specification.
+ */
+extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionMinimumZoomLevel;
+
+/**
+ An `NSNumber` object containing an integer; specifies the maximum zoom level at
+ which to create vector tiles. A greater value produces greater detail at high
+ zoom levels. The default value is 18.
+
+ This attribute corresponds to the
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-maxzoom"><code>maxzoom</code></a>
+ source property in the Mapbox Style Specification.
+ */
+extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevel;
+
+/**
+ An `NSNumber` object containing an integer; specifies the size of the tile
+ buffer on each side. A value of 0 produces no buffer. A value of 512 produces a
+ buffer as wide as the tile itself. Larger values produce fewer rendering
+ artifacts near tile edges and slower performance. The default value is 128.
+
+ This attribute corresponds to the
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-buffer"><code>buffer</code></a>
+ source property in the Mapbox Style Specification.
+ */
+extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionBuffer;
+
+/**
+ An `NSNumber` object containing a double; specifies the Douglas-Peucker
+ simplification tolerance. A greater value produces simpler geometries and
+ improves performance. The default value is 0.375.
+
+ This attribute corresponds to the
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-tolerance"><code>tolerance</code></a>
+ source property in the Mapbox Style Specification.
+ */
+extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance;
+
+/**
+ An `NSNumber` object containing a Boolean value; specifies whether the shape of
+ an `MGLComputedShapeSource` should be wrapped to accomodate coordinates with
+ longitudes beyond −180 and 180. The default value is `NO`.
+
+ Setting this option to `YES` affects rendering performance.
+
+ This option is ignored when creating an instance of a class besides
+ `MGLComputedShapeSource`.
+ */
+extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionWrapsCoordinates;
+
+/**
+ An `NSNumber` object containing a Boolean value; specifies whether the shape of
+ an `MGLComputedShapeSource` should be clipped at the edge of each tile. The
+ default value is `NO`.
+
+ Setting this option to `YES` affects rendering performance. Use this option to
+ clip `MGLPolyline`s and `MGLPolygon`s at tile boundaries without artifacts.
+
+ This option is ignored when creating an instance of a class besides
+ `MGLComputedShapeSource`.
+ */
+extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionClipsCoordinates;
+
+/**
+ `MGLAbstractShapeSource` is an abstract base class for map content sources that
+ supply vector shapes to be shown on the map. A shape source is added to an
+ `MGLStyle` object along with an `MGLVectorStyleLayer` object. The vector style
+ layer defines the appearance of any content supplied by the shape source.
+
+
+ Do not create instances of this class directly, and do not create your own
+ subclasses of this class. Instead, create instances of `MGLShapeSource` or
+ `MGLComputedShapeSource`.
+ */
+MGL_EXPORT
+@interface MGLAbstractShapeSource : MGLSource
+
+
+@end
diff --git a/platform/darwin/src/MGLAbstractShapeSource.mm b/platform/darwin/src/MGLAbstractShapeSource.mm
new file mode 100644
index 0000000000..eb0807cee0
--- /dev/null
+++ b/platform/darwin/src/MGLAbstractShapeSource.mm
@@ -0,0 +1,136 @@
+#import "MGLAbstractShapeSource.h"
+#import "MGLAbstractShapeSource_Private.h"
+
+const MGLShapeSourceOption MGLShapeSourceOptionBuffer = @"MGLShapeSourceOptionBuffer";
+const MGLShapeSourceOption MGLShapeSourceOptionClusterRadius = @"MGLShapeSourceOptionClusterRadius";
+const MGLShapeSourceOption MGLShapeSourceOptionClustered = @"MGLShapeSourceOptionClustered";
+const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevel = @"MGLShapeSourceOptionMaximumZoomLevel";
+const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevelForClustering = @"MGLShapeSourceOptionMaximumZoomLevelForClustering";
+const MGLShapeSourceOption MGLShapeSourceOptionMinimumZoomLevel = @"MGLShapeSourceOptionMinimumZoomLevel";
+const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance = @"MGLShapeSourceOptionSimplificationTolerance";
+const MGLShapeSourceOption MGLShapeSourceOptionWrapsCoordinates = @"MGLShapeSourceOptionWrapsCoordinates";
+const MGLShapeSourceOption MGLShapeSourceOptionClipsCoordinates = @"MGLShapeSourceOptionClipsCoordinates";
+
+@interface MGLAbstractShapeSource ()
+
+@end
+
+@implementation MGLAbstractShapeSource
+
+@end
+
+mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *options) {
+ auto geoJSONOptions = mbgl::style::GeoJSONOptions();
+
+ if (NSNumber *value = options[MGLShapeSourceOptionMinimumZoomLevel]) {
+ if (![value isKindOfClass:[NSNumber class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionMaximumZoomLevel must be an NSNumber."];
+ }
+ geoJSONOptions.minzoom = value.integerValue;
+ }
+
+ if (NSNumber *value = options[MGLShapeSourceOptionMaximumZoomLevel]) {
+ if (![value isKindOfClass:[NSNumber class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionMaximumZoomLevel must be an NSNumber."];
+ }
+ geoJSONOptions.maxzoom = value.integerValue;
+ }
+
+ if (NSNumber *value = options[MGLShapeSourceOptionBuffer]) {
+ if (![value isKindOfClass:[NSNumber class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionBuffer must be an NSNumber."];
+ }
+ geoJSONOptions.buffer = value.integerValue;
+ }
+
+ if (NSNumber *value = options[MGLShapeSourceOptionSimplificationTolerance]) {
+ if (![value isKindOfClass:[NSNumber class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionSimplificationTolerance must be an NSNumber."];
+ }
+ geoJSONOptions.tolerance = value.doubleValue;
+ }
+
+ if (NSNumber *value = options[MGLShapeSourceOptionClusterRadius]) {
+ if (![value isKindOfClass:[NSNumber class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionClusterRadius must be an NSNumber."];
+ }
+ geoJSONOptions.clusterRadius = value.integerValue;
+ }
+
+ if (NSNumber *value = options[MGLShapeSourceOptionMaximumZoomLevelForClustering]) {
+ if (![value isKindOfClass:[NSNumber class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionMaximumZoomLevelForClustering must be an NSNumber."];
+ }
+ geoJSONOptions.clusterMaxZoom = value.integerValue;
+ }
+
+ if (NSNumber *value = options[MGLShapeSourceOptionClustered]) {
+ if (![value isKindOfClass:[NSNumber class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionClustered must be an NSNumber."];
+ }
+ geoJSONOptions.cluster = value.boolValue;
+ }
+
+ return geoJSONOptions;
+}
+
+mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDictionary(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *options) {
+ mbgl::style::CustomGeometrySource::Options sourceOptions;
+
+ if (NSNumber *value = options[MGLShapeSourceOptionMinimumZoomLevel]) {
+ if (![value isKindOfClass:[NSNumber class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionMaximumZoomLevelForClustering must be an NSNumber."];
+ }
+ sourceOptions.zoomRange.min = value.integerValue;
+ }
+
+ if (NSNumber *value = options[MGLShapeSourceOptionMaximumZoomLevel]) {
+ if (![value isKindOfClass:[NSNumber class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionMaximumZoomLevel must be an NSNumber."];
+ }
+ sourceOptions.zoomRange.max = value.integerValue;
+ }
+
+ if (NSNumber *value = options[MGLShapeSourceOptionBuffer]) {
+ if (![value isKindOfClass:[NSNumber class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionBuffer must be an NSNumber."];
+ }
+ sourceOptions.tileOptions.buffer = value.integerValue;
+ }
+
+ if (NSNumber *value = options[MGLShapeSourceOptionSimplificationTolerance]) {
+ if (![value isKindOfClass:[NSNumber class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionSimplificationTolerance must be an NSNumber."];
+ }
+ sourceOptions.tileOptions.tolerance = value.doubleValue;
+ }
+
+ if (NSNumber *value = options[MGLShapeSourceOptionWrapsCoordinates]) {
+ if (![value isKindOfClass:[NSNumber class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionWrapsCoordinates must be an NSNumber."];
+ }
+ sourceOptions.tileOptions.wrap = value.boolValue;
+ }
+
+ if (NSNumber *value = options[MGLShapeSourceOptionClipsCoordinates]) {
+ if (![value isKindOfClass:[NSNumber class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionClipsCoordinates must be an NSNumber."];
+ }
+ sourceOptions.tileOptions.clip = value.boolValue;
+ }
+
+ return sourceOptions;
+}
diff --git a/platform/darwin/src/MGLAbstractShapeSource_Private.h b/platform/darwin/src/MGLAbstractShapeSource_Private.h
new file mode 100644
index 0000000000..ddde55b149
--- /dev/null
+++ b/platform/darwin/src/MGLAbstractShapeSource_Private.h
@@ -0,0 +1,22 @@
+#import "MGLAbstractShapeSource.h"
+
+#import "MGLFoundation.h"
+#import "MGLTypes.h"
+#import "MGLShape.h"
+
+#include <mbgl/style/sources/geojson_source.hpp>
+#include <mbgl/style/sources/custom_geometry_source.hpp>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface MGLAbstractShapeSource (Private)
+
+MGL_EXPORT
+
+mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *options);
+
+MGL_EXPORT
+mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDictionary(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *options);
+
+@end
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLAccountManager.m b/platform/darwin/src/MGLAccountManager.m
index 7e55779d04..729caeaa31 100644
--- a/platform/darwin/src/MGLAccountManager.m
+++ b/platform/darwin/src/MGLAccountManager.m
@@ -39,7 +39,7 @@
}
static dispatch_once_t onceToken;
static MGLAccountManager *_sharedManager;
- void (^setupBlock)() = ^{
+ void (^setupBlock)(void) = ^{
dispatch_once(&onceToken, ^{
_sharedManager = [[self alloc] init];
});
diff --git a/platform/darwin/src/MGLAttributionInfo.mm b/platform/darwin/src/MGLAttributionInfo.mm
index 52a83fd18e..73147270c1 100644
--- a/platform/darwin/src/MGLAttributionInfo.mm
+++ b/platform/darwin/src/MGLAttributionInfo.mm
@@ -160,6 +160,7 @@
[NSURLQueryItem queryItemWithName:@"owner" value:stylePathComponents[1]],
[NSURLQueryItem queryItemWithName:@"id" value:stylePathComponents[2]],
[NSURLQueryItem queryItemWithName:@"access_token" value:[MGLAccountManager accessToken]],
+ [NSURLQueryItem queryItemWithName:@"map_sdk_version" value:[NSBundle mgl_frameworkInfoDictionary][@"MGLSemanticVersionString"]],
]];
}
}
diff --git a/platform/darwin/src/MGLBackgroundStyleLayer.h b/platform/darwin/src/MGLBackgroundStyleLayer.h
index 659903914c..9d2673c859 100644
--- a/platform/darwin/src/MGLBackgroundStyleLayer.h
+++ b/platform/darwin/src/MGLBackgroundStyleLayer.h
@@ -2,7 +2,6 @@
// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
#import "MGLFoundation.h"
-#import "MGLStyleValue.h"
#import "MGLStyleLayer.h"
NS_ASSUME_NONNULL_BEGIN
@@ -38,45 +37,28 @@ which it is added.
#pragma mark - Accessing the Paint Attributes
-#if TARGET_OS_IPHONE
/**
The color with which the background will be drawn.
- The default value of this property is an `MGLStyleValue` object containing
+ The default value of this property is an expression that evaluates to
`UIColor.blackColor`. Set this property to `nil` to reset it to the default
value.
This property is only applied to the style if `backgroundPattern` is set to
`nil`. Otherwise, it is ignored.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- */
-@property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *backgroundColor;
-#else
-/**
- The color with which the background will be drawn.
+ * Constant `UIColor` values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
- The default value of this property is an `MGLStyleValue` object containing
- `NSColor.blackColor`. Set this property to `nil` to reset it to the default
- value.
-
- This property is only applied to the style if `backgroundPattern` is set to
- `nil`. Otherwise, it is ignored.
-
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
+ This property does not support applying interpolation or step functions to
+ feature attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *backgroundColor;
-#endif
+@property (nonatomic, null_resettable) NSExpression *backgroundColor;
/**
The transition affecting any changes to this layer’s `backgroundColor` property.
@@ -88,18 +70,21 @@ which it is added.
/**
The opacity at which the background will be drawn.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `1`. Set this property to `nil` to reset
- it to the default value.
+ The default value of this property is an expression that evaluates to the float
+ `1`. Set this property to `nil` to reset it to the default value.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
+
+ This property does not support applying interpolation or step functions to
+ feature attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *backgroundOpacity;
+@property (nonatomic, null_resettable) NSExpression *backgroundOpacity;
/**
The transition affecting any changes to this layer’s `backgroundOpacity` property.
@@ -113,13 +98,19 @@ which it is added.
seamless patterns, image width and height must be a factor of two (2, 4, 8,
..., 512).
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
+
+ * Constant string values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Step functions applied to the `$zoomLevel` variable
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of
- `MGLInterpolationModeInterval`
+ This property does not support applying interpolation functions to the
+ `$zoomLevel` variable or applying interpolation or step functions to feature
+ attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSString *> *backgroundPattern;
+@property (nonatomic, null_resettable) NSExpression *backgroundPattern;
/**
The transition affecting any changes to this layer’s `backgroundPattern` property.
diff --git a/platform/darwin/src/MGLBackgroundStyleLayer.mm b/platform/darwin/src/MGLBackgroundStyleLayer.mm
index 47b491fc65..7c4cf79709 100644
--- a/platform/darwin/src/MGLBackgroundStyleLayer.mm
+++ b/platform/darwin/src/MGLBackgroundStyleLayer.mm
@@ -32,21 +32,21 @@
#pragma mark - Accessing the Paint Attributes
-- (void)setBackgroundColor:(MGLStyleValue<MGLColor *> *)backgroundColor {
+- (void)setBackgroundColor:(NSExpression *)backgroundColor {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toInterpolatablePropertyValue(backgroundColor);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toPropertyValue<mbgl::style::PropertyValue<mbgl::Color>>(backgroundColor);
self.rawLayer->setBackgroundColor(mbglValue);
}
-- (MGLStyleValue<MGLColor *> *)backgroundColor {
+- (NSExpression *)backgroundColor {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getBackgroundColor();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toStyleValue(self.rawLayer->getDefaultBackgroundColor());
+ propertyValue = self.rawLayer->getDefaultBackgroundColor();
}
- return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toExpression(propertyValue);
}
- (void)setBackgroundColorTransition:(MGLTransition )transition {
@@ -67,21 +67,21 @@
return transition;
}
-- (void)setBackgroundOpacity:(MGLStyleValue<NSNumber *> *)backgroundOpacity {
+- (void)setBackgroundOpacity:(NSExpression *)backgroundOpacity {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(backgroundOpacity);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(backgroundOpacity);
self.rawLayer->setBackgroundOpacity(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)backgroundOpacity {
+- (NSExpression *)backgroundOpacity {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getBackgroundOpacity();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultBackgroundOpacity());
+ propertyValue = self.rawLayer->getDefaultBackgroundOpacity();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
- (void)setBackgroundOpacityTransition:(MGLTransition )transition {
@@ -102,21 +102,21 @@
return transition;
}
-- (void)setBackgroundPattern:(MGLStyleValue<NSString *> *)backgroundPattern {
+- (void)setBackgroundPattern:(NSExpression *)backgroundPattern {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<std::string, NSString *>().toPropertyValue(backgroundPattern);
+ auto mbglValue = MGLStyleValueTransformer<std::string, NSString *>().toPropertyValue<mbgl::style::PropertyValue<std::string>>(backgroundPattern);
self.rawLayer->setBackgroundPattern(mbglValue);
}
-- (MGLStyleValue<NSString *> *)backgroundPattern {
+- (NSExpression *)backgroundPattern {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getBackgroundPattern();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<std::string, NSString *>().toStyleValue(self.rawLayer->getDefaultBackgroundPattern());
+ propertyValue = self.rawLayer->getDefaultBackgroundPattern();
}
- return MGLStyleValueTransformer<std::string, NSString *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
}
- (void)setBackgroundPatternTransition:(MGLTransition )transition {
diff --git a/platform/darwin/src/MGLCircleStyleLayer.h b/platform/darwin/src/MGLCircleStyleLayer.h
index ea95f6beef..caa6d2f6cb 100644
--- a/platform/darwin/src/MGLCircleStyleLayer.h
+++ b/platform/darwin/src/MGLCircleStyleLayer.h
@@ -2,7 +2,6 @@
// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
#import "MGLFoundation.h"
-#import "MGLStyleValue.h"
#import "MGLVectorStyleLayer.h"
NS_ASSUME_NONNULL_BEGIN
@@ -42,7 +41,7 @@ typedef NS_ENUM(NSUInteger, MGLCircleScaleAlignment) {
};
/**
- Controls the translation reference point.
+ Controls the frame of reference for `MGLCircleStyleLayer.circleTranslation`.
Values of this type are used in the `MGLCircleStyleLayer.circleTranslationAnchor`
property.
@@ -83,12 +82,11 @@ typedef NS_ENUM(NSUInteger, MGLCircleTranslationAnchor) {
```swift
let layer = MGLCircleStyleLayer(identifier: "circles", source: population)
layer.sourceLayerIdentifier = "population"
- layer.circleColor = MGLStyleValue(rawValue: .green)
- layer.circleRadius = MGLStyleValue(interpolationMode: .exponential,
- cameraStops: [12: MGLStyleValue(rawValue: 2),
- 22: MGLStyleValue(rawValue: 180)],
- options: [.interpolationBase: 1.75])
- layer.circleOpacity = MGLStyleValue(rawValue: 0.7)
+ layer.circleColor = NSExpression(forConstantValue: UIColor.green)
+ layer.circleRadius = NSExpression(format: "FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'exponential', 1.75, %@)",
+ [12: 2,
+ 22: 180])
+ layer.circleOpacity = NSExpression(forConstantValue: 0.7)
layer.predicate = NSPredicate(format: "%K == %@", "marital-status", "married")
mapView.style?.addLayer(layer)
```
@@ -117,27 +115,19 @@ MGL_EXPORT
Amount to blur the circle. 1 blurs the circle such that only the centerpoint is
full opacity.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `0`. Set this property to `nil` to reset
- it to the default value.
-
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
+ The default value of this property is an expression that evaluates to the float
+ `0`. Set this property to `nil` to reset it to the default value.
+
+ You can set this property to an expression containing any of the following:
+
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *circleBlur;
+@property (nonatomic, null_resettable) NSExpression *circleBlur;
/**
The transition affecting any changes to this layer’s `circleBlur` property.
@@ -146,57 +136,23 @@ MGL_EXPORT
*/
@property (nonatomic) MGLTransition circleBlurTransition;
-#if TARGET_OS_IPHONE
/**
The fill color of the circle.
- The default value of this property is an `MGLStyleValue` object containing
+ The default value of this property is an expression that evaluates to
`UIColor.blackColor`. Set this property to `nil` to reset it to the default
value.
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- */
-@property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *circleColor;
-#else
-/**
- The fill color of the circle.
-
- The default value of this property is an `MGLStyleValue` object containing
- `NSColor.blackColor`. Set this property to `nil` to reset it to the default
- value.
+ You can set this property to an expression containing any of the following:
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
+ * Constant `UIColor` values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *circleColor;
-#endif
+@property (nonatomic, null_resettable) NSExpression *circleColor;
/**
The transition affecting any changes to this layer’s `circleColor` property.
@@ -208,27 +164,19 @@ MGL_EXPORT
/**
The opacity at which the circle will be drawn.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `1`. Set this property to `nil` to reset
- it to the default value.
-
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
+ The default value of this property is an expression that evaluates to the float
+ `1`. Set this property to `nil` to reset it to the default value.
+
+ You can set this property to an expression containing any of the following:
+
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *circleOpacity;
+@property (nonatomic, null_resettable) NSExpression *circleOpacity;
/**
The transition affecting any changes to this layer’s `circleOpacity` property.
@@ -240,44 +188,44 @@ MGL_EXPORT
/**
Orientation of circle when map is pitched.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSValue` object containing `MGLCirclePitchAlignmentViewport`. Set this
- property to `nil` to reset it to the default value.
+ The default value of this property is an expression that evaluates to
+ `viewport`. Set this property to `nil` to reset it to the default value.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of
- `MGLInterpolationModeInterval`
+ * Constant `MGLCirclePitchAlignment` values
+ * Any of the following constant string values:
+ * `map`: The circle is aligned to the plane of the map.
+ * `viewport`: The circle is aligned to the plane of the viewport.
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Step functions applied to the `$zoomLevel` variable
+
+ This property does not support applying interpolation functions to the
+ `$zoomLevel` variable or applying interpolation or step functions to feature
+ attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *circlePitchAlignment;
+@property (nonatomic, null_resettable) NSExpression *circlePitchAlignment;
/**
Circle radius.
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `5`. Set this property to `nil` to reset
- it to the default value.
-
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
+ The default value of this property is an expression that evaluates to the float
+ `5`. Set this property to `nil` to reset it to the default value.
+
+ You can set this property to an expression containing any of the following:
+
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *circleRadius;
+@property (nonatomic, null_resettable) NSExpression *circleRadius;
/**
The transition affecting any changes to this layer’s `circleRadius` property.
@@ -289,75 +237,50 @@ MGL_EXPORT
/**
Controls the scaling behavior of the circle when the map is pitched.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSValue` object containing `MGLCircleScaleAlignmentMap`. Set this property to
- `nil` to reset it to the default value.
+ The default value of this property is an expression that evaluates to `map`.
+ Set this property to `nil` to reset it to the default value.
This attribute corresponds to the <a
href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-circle-pitch-scale"><code>circle-pitch-scale</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of
- `MGLInterpolationModeInterval`
+ You can set this property to an expression containing any of the following:
+
+ * Constant `MGLCircleScaleAlignment` values
+ * Any of the following constant string values:
+ * `map`: Circles are scaled according to their apparent distance to the
+ camera.
+ * `viewport`: Circles are not scaled.
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Step functions applied to the `$zoomLevel` variable
+
+ This property does not support applying interpolation functions to the
+ `$zoomLevel` variable or applying interpolation or step functions to feature
+ attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *circleScaleAlignment;
+@property (nonatomic, null_resettable) NSExpression *circleScaleAlignment;
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *circlePitchScale __attribute__((unavailable("Use circleScaleAlignment instead.")));
+@property (nonatomic, null_resettable) NSExpression *circlePitchScale __attribute__((unavailable("Use circleScaleAlignment instead.")));
-#if TARGET_OS_IPHONE
/**
The stroke color of the circle.
- The default value of this property is an `MGLStyleValue` object containing
+ The default value of this property is an expression that evaluates to
`UIColor.blackColor`. Set this property to `nil` to reset it to the default
value.
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- */
-@property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *circleStrokeColor;
-#else
-/**
- The stroke color of the circle.
-
- The default value of this property is an `MGLStyleValue` object containing
- `NSColor.blackColor`. Set this property to `nil` to reset it to the default
- value.
+ You can set this property to an expression containing any of the following:
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
+ * Constant `UIColor` values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *circleStrokeColor;
-#endif
+@property (nonatomic, null_resettable) NSExpression *circleStrokeColor;
/**
The transition affecting any changes to this layer’s `circleStrokeColor` property.
@@ -369,27 +292,19 @@ MGL_EXPORT
/**
The opacity of the circle's stroke.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `1`. Set this property to `nil` to reset
- it to the default value.
-
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
+ The default value of this property is an expression that evaluates to the float
+ `1`. Set this property to `nil` to reset it to the default value.
+
+ You can set this property to an expression containing any of the following:
+
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *circleStrokeOpacity;
+@property (nonatomic, null_resettable) NSExpression *circleStrokeOpacity;
/**
The transition affecting any changes to this layer’s `circleStrokeOpacity` property.
@@ -404,27 +319,19 @@ MGL_EXPORT
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `0`. Set this property to `nil` to reset
- it to the default value.
-
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
+ The default value of this property is an expression that evaluates to the float
+ `0`. Set this property to `nil` to reset it to the default value.
+
+ You can set this property to an expression containing any of the following:
+
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *circleStrokeWidth;
+@property (nonatomic, null_resettable) NSExpression *circleStrokeWidth;
/**
The transition affecting any changes to this layer’s `circleStrokeWidth` property.
@@ -439,7 +346,7 @@ MGL_EXPORT
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an
+ The default value of this property is an expression that evaluates to an
`NSValue` object containing a `CGVector` struct set to 0 points rightward and 0
points downward. Set this property to `nil` to reset it to the default value.
@@ -447,21 +354,25 @@ MGL_EXPORT
href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-circle-translate"><code>circle-translate</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
+ * Constant `CGVector` values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
+
+ This property does not support applying interpolation or step functions to
+ feature attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *circleTranslation;
+@property (nonatomic, null_resettable) NSExpression *circleTranslation;
#else
/**
The geometry's offset.
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an
+ The default value of this property is an expression that evaluates to an
`NSValue` object containing a `CGVector` struct set to 0 points rightward and 0
points upward. Set this property to `nil` to reset it to the default value.
@@ -469,14 +380,18 @@ MGL_EXPORT
href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-circle-translate"><code>circle-translate</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
+
+ * Constant `CGVector` values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
+ This property does not support applying interpolation or step functions to
+ feature attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *circleTranslation;
+@property (nonatomic, null_resettable) NSExpression *circleTranslation;
#endif
/**
@@ -486,14 +401,13 @@ MGL_EXPORT
*/
@property (nonatomic) MGLTransition circleTranslationTransition;
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *circleTranslate __attribute__((unavailable("Use circleTranslation instead.")));
+@property (nonatomic, null_resettable) NSExpression *circleTranslate __attribute__((unavailable("Use circleTranslation instead.")));
/**
- Controls the translation reference point.
+ Controls the frame of reference for `circleTranslation`.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSValue` object containing `MGLCircleTranslationAnchorMap`. Set this property
- to `nil` to reset it to the default value.
+ The default value of this property is an expression that evaluates to `map`.
+ Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `circleTranslation` is non-`nil`.
Otherwise, it is ignored.
@@ -502,15 +416,24 @@ MGL_EXPORT
href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-circle-translate-anchor"><code>circle-translate-anchor</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
+
+ * Constant `MGLCircleTranslationAnchor` values
+ * Any of the following constant string values:
+ * `map`: The circle is translated relative to the map.
+ * `viewport`: The circle is translated relative to the viewport.
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Step functions applied to the `$zoomLevel` variable
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of
- `MGLInterpolationModeInterval`
+ This property does not support applying interpolation functions to the
+ `$zoomLevel` variable or applying interpolation or step functions to feature
+ attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *circleTranslationAnchor;
+@property (nonatomic, null_resettable) NSExpression *circleTranslationAnchor;
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *circleTranslateAnchor __attribute__((unavailable("Use circleTranslationAnchor instead.")));
+@property (nonatomic, null_resettable) NSExpression *circleTranslateAnchor __attribute__((unavailable("Use circleTranslationAnchor instead.")));
@end
diff --git a/platform/darwin/src/MGLCircleStyleLayer.mm b/platform/darwin/src/MGLCircleStyleLayer.mm
index 71ae37035e..0be3920987 100644
--- a/platform/darwin/src/MGLCircleStyleLayer.mm
+++ b/platform/darwin/src/MGLCircleStyleLayer.mm
@@ -87,21 +87,21 @@ namespace mbgl {
#pragma mark - Accessing the Paint Attributes
-- (void)setCircleBlur:(MGLStyleValue<NSNumber *> *)circleBlur {
+- (void)setCircleBlur:(NSExpression *)circleBlur {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(circleBlur);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(circleBlur);
self.rawLayer->setCircleBlur(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)circleBlur {
+- (NSExpression *)circleBlur {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getCircleBlur();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultCircleBlur());
+ propertyValue = self.rawLayer->getDefaultCircleBlur();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
- (void)setCircleBlurTransition:(MGLTransition )transition {
@@ -122,21 +122,21 @@ namespace mbgl {
return transition;
}
-- (void)setCircleColor:(MGLStyleValue<MGLColor *> *)circleColor {
+- (void)setCircleColor:(NSExpression *)circleColor {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenPropertyValue(circleColor);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<mbgl::Color>>(circleColor);
self.rawLayer->setCircleColor(mbglValue);
}
-- (MGLStyleValue<MGLColor *> *)circleColor {
+- (NSExpression *)circleColor {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getCircleColor();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(self.rawLayer->getDefaultCircleColor());
+ propertyValue = self.rawLayer->getDefaultCircleColor();
}
- return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toExpression(propertyValue);
}
- (void)setCircleColorTransition:(MGLTransition )transition {
@@ -157,21 +157,21 @@ namespace mbgl {
return transition;
}
-- (void)setCircleOpacity:(MGLStyleValue<NSNumber *> *)circleOpacity {
+- (void)setCircleOpacity:(NSExpression *)circleOpacity {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(circleOpacity);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(circleOpacity);
self.rawLayer->setCircleOpacity(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)circleOpacity {
+- (NSExpression *)circleOpacity {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getCircleOpacity();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultCircleOpacity());
+ propertyValue = self.rawLayer->getDefaultCircleOpacity();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
- (void)setCircleOpacityTransition:(MGLTransition )transition {
@@ -192,38 +192,38 @@ namespace mbgl {
return transition;
}
-- (void)setCirclePitchAlignment:(MGLStyleValue<NSValue *> *)circlePitchAlignment {
+- (void)setCirclePitchAlignment:(NSExpression *)circlePitchAlignment {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLCirclePitchAlignment>().toEnumPropertyValue(circlePitchAlignment);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLCirclePitchAlignment>().toPropertyValue<mbgl::style::PropertyValue<mbgl::style::AlignmentType>>(circlePitchAlignment);
self.rawLayer->setCirclePitchAlignment(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)circlePitchAlignment {
+- (NSExpression *)circlePitchAlignment {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getCirclePitchAlignment();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLCirclePitchAlignment>().toEnumStyleValue(self.rawLayer->getDefaultCirclePitchAlignment());
+ propertyValue = self.rawLayer->getDefaultCirclePitchAlignment();
}
- return MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLCirclePitchAlignment>().toEnumStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLCirclePitchAlignment>().toExpression(propertyValue);
}
-- (void)setCircleRadius:(MGLStyleValue<NSNumber *> *)circleRadius {
+- (void)setCircleRadius:(NSExpression *)circleRadius {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(circleRadius);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(circleRadius);
self.rawLayer->setCircleRadius(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)circleRadius {
+- (NSExpression *)circleRadius {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getCircleRadius();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultCircleRadius());
+ propertyValue = self.rawLayer->getDefaultCircleRadius();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
- (void)setCircleRadiusTransition:(MGLTransition )transition {
@@ -244,45 +244,45 @@ namespace mbgl {
return transition;
}
-- (void)setCircleScaleAlignment:(MGLStyleValue<NSValue *> *)circleScaleAlignment {
+- (void)setCircleScaleAlignment:(NSExpression *)circleScaleAlignment {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::style::CirclePitchScaleType, NSValue *, mbgl::style::CirclePitchScaleType, MGLCircleScaleAlignment>().toEnumPropertyValue(circleScaleAlignment);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::style::CirclePitchScaleType, NSValue *, mbgl::style::CirclePitchScaleType, MGLCircleScaleAlignment>().toPropertyValue<mbgl::style::PropertyValue<mbgl::style::CirclePitchScaleType>>(circleScaleAlignment);
self.rawLayer->setCirclePitchScale(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)circleScaleAlignment {
+- (NSExpression *)circleScaleAlignment {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getCirclePitchScale();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<mbgl::style::CirclePitchScaleType, NSValue *, mbgl::style::CirclePitchScaleType, MGLCircleScaleAlignment>().toEnumStyleValue(self.rawLayer->getDefaultCirclePitchScale());
+ propertyValue = self.rawLayer->getDefaultCirclePitchScale();
}
- return MGLStyleValueTransformer<mbgl::style::CirclePitchScaleType, NSValue *, mbgl::style::CirclePitchScaleType, MGLCircleScaleAlignment>().toEnumStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::style::CirclePitchScaleType, NSValue *, mbgl::style::CirclePitchScaleType, MGLCircleScaleAlignment>().toExpression(propertyValue);
}
-- (void)setCirclePitchScale:(MGLStyleValue<NSValue *> *)circlePitchScale {
+- (void)setCirclePitchScale:(NSExpression *)circlePitchScale {
}
-- (MGLStyleValue<NSValue *> *)circlePitchScale {
+- (NSExpression *)circlePitchScale {
return self.circleScaleAlignment;
}
-- (void)setCircleStrokeColor:(MGLStyleValue<MGLColor *> *)circleStrokeColor {
+- (void)setCircleStrokeColor:(NSExpression *)circleStrokeColor {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenPropertyValue(circleStrokeColor);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<mbgl::Color>>(circleStrokeColor);
self.rawLayer->setCircleStrokeColor(mbglValue);
}
-- (MGLStyleValue<MGLColor *> *)circleStrokeColor {
+- (NSExpression *)circleStrokeColor {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getCircleStrokeColor();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(self.rawLayer->getDefaultCircleStrokeColor());
+ propertyValue = self.rawLayer->getDefaultCircleStrokeColor();
}
- return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toExpression(propertyValue);
}
- (void)setCircleStrokeColorTransition:(MGLTransition )transition {
@@ -303,21 +303,21 @@ namespace mbgl {
return transition;
}
-- (void)setCircleStrokeOpacity:(MGLStyleValue<NSNumber *> *)circleStrokeOpacity {
+- (void)setCircleStrokeOpacity:(NSExpression *)circleStrokeOpacity {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(circleStrokeOpacity);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(circleStrokeOpacity);
self.rawLayer->setCircleStrokeOpacity(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)circleStrokeOpacity {
+- (NSExpression *)circleStrokeOpacity {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getCircleStrokeOpacity();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultCircleStrokeOpacity());
+ propertyValue = self.rawLayer->getDefaultCircleStrokeOpacity();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
- (void)setCircleStrokeOpacityTransition:(MGLTransition )transition {
@@ -338,21 +338,21 @@ namespace mbgl {
return transition;
}
-- (void)setCircleStrokeWidth:(MGLStyleValue<NSNumber *> *)circleStrokeWidth {
+- (void)setCircleStrokeWidth:(NSExpression *)circleStrokeWidth {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(circleStrokeWidth);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(circleStrokeWidth);
self.rawLayer->setCircleStrokeWidth(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)circleStrokeWidth {
+- (NSExpression *)circleStrokeWidth {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getCircleStrokeWidth();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultCircleStrokeWidth());
+ propertyValue = self.rawLayer->getDefaultCircleStrokeWidth();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
- (void)setCircleStrokeWidthTransition:(MGLTransition )transition {
@@ -373,21 +373,21 @@ namespace mbgl {
return transition;
}
-- (void)setCircleTranslation:(MGLStyleValue<NSValue *> *)circleTranslation {
+- (void)setCircleTranslation:(NSExpression *)circleTranslation {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toInterpolatablePropertyValue(circleTranslation);
+ auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toPropertyValue<mbgl::style::PropertyValue<std::array<float, 2>>>(circleTranslation);
self.rawLayer->setCircleTranslate(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)circleTranslation {
+- (NSExpression *)circleTranslation {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getCircleTranslate();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toStyleValue(self.rawLayer->getDefaultCircleTranslate());
+ propertyValue = self.rawLayer->getDefaultCircleTranslate();
}
- return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toExpression(propertyValue);
}
- (void)setCircleTranslationTransition:(MGLTransition )transition {
@@ -408,34 +408,34 @@ namespace mbgl {
return transition;
}
-- (void)setCircleTranslate:(MGLStyleValue<NSValue *> *)circleTranslate {
+- (void)setCircleTranslate:(NSExpression *)circleTranslate {
}
-- (MGLStyleValue<NSValue *> *)circleTranslate {
+- (NSExpression *)circleTranslate {
return self.circleTranslation;
}
-- (void)setCircleTranslationAnchor:(MGLStyleValue<NSValue *> *)circleTranslationAnchor {
+- (void)setCircleTranslationAnchor:(NSExpression *)circleTranslationAnchor {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLCircleTranslationAnchor>().toEnumPropertyValue(circleTranslationAnchor);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLCircleTranslationAnchor>().toPropertyValue<mbgl::style::PropertyValue<mbgl::style::TranslateAnchorType>>(circleTranslationAnchor);
self.rawLayer->setCircleTranslateAnchor(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)circleTranslationAnchor {
+- (NSExpression *)circleTranslationAnchor {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getCircleTranslateAnchor();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLCircleTranslationAnchor>().toEnumStyleValue(self.rawLayer->getDefaultCircleTranslateAnchor());
+ propertyValue = self.rawLayer->getDefaultCircleTranslateAnchor();
}
- return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLCircleTranslationAnchor>().toEnumStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLCircleTranslationAnchor>().toExpression(propertyValue);
}
-- (void)setCircleTranslateAnchor:(MGLStyleValue<NSValue *> *)circleTranslateAnchor {
+- (void)setCircleTranslateAnchor:(NSExpression *)circleTranslateAnchor {
}
-- (MGLStyleValue<NSValue *> *)circleTranslateAnchor {
+- (NSExpression *)circleTranslateAnchor {
return self.circleTranslationAnchor;
}
diff --git a/platform/darwin/src/MGLComputedShapeSource.h b/platform/darwin/src/MGLComputedShapeSource.h
new file mode 100644
index 0000000000..f90f2c94b1
--- /dev/null
+++ b/platform/darwin/src/MGLComputedShapeSource.h
@@ -0,0 +1,113 @@
+#import "MGLAbstractShapeSource.h"
+
+#import "MGLFoundation.h"
+#import "MGLGeometry.h"
+#import "MGLTypes.h"
+#import "MGLShape.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@protocol MGLFeature;
+
+/**
+ Data source for `MGLComputedShapeSource`. This protocol defines two optional methods for fetching
+ data, one based on tile coordinates, and one based on a bounding box. Classes that implement this
+ protocol must implement one, and only one of the methods. Methods on this protocol will not be
+ called on main thread, they will be called on the caller's `requestQueue`.
+ */
+@protocol MGLComputedShapeSourceDataSource <NSObject>
+
+@optional
+/**
+ Fetch features for a tile. This method will not be invoked on the main queue, it
+ will be invoked on the caller's `requestQueue`.
+ @param x Tile X coordinate.
+ @param y Tile Y coordinate.
+ @param zoomLevel Tile zoom level.
+ */
+- (NSArray<MGLShape <MGLFeature> *>*)featuresInTileAtX:(NSUInteger)x y:(NSUInteger)y zoomLevel:(NSUInteger)zoomLevel;
+
+/**
+ Fetch features for a tile. This method will not be invoked on the main queue, it
+ will be invoked on the caller's `requestQueue`.
+ @param bounds The bounds to fetch data for.
+ @param zoomLevel Tile zoom level.
+ */
+- (NSArray<MGLShape <MGLFeature> *>*)featuresInCoordinateBounds:(MGLCoordinateBounds)bounds zoomLevel:(NSUInteger)zoomLevel;
+
+@end
+
+/**
+ A source for vector data that is fetched one tile at a time. Useful for sources that are
+ too large to fit in memory, or are already divided into tiles, but not in Mapbox Vector Tile format.
+
+ Supported options are `MGLShapeSourceOptionMinimumZoomLevel`, `MGLShapeSourceOptionMaximumZoomLevel`,
+ `MGLShapeSourceOptionBuffer`, and `MGLShapeSourceOptionSimplificationTolerance.` This source does
+ not support clustering.
+ */
+MGL_EXPORT
+@interface MGLComputedShapeSource : MGLAbstractShapeSource
+
+/**
+ Returns a custom shape data source initialized with an identifier, and a
+ dictionary of options for the source according to the
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson">style
+ specification</a>.
+
+ @param identifier A string that uniquely identifies the source.
+ @param options An `NSDictionary` of options for this source.
+ */
+- (instancetype)initWithIdentifier:(NSString *)identifier options:(nullable NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options NS_DESIGNATED_INITIALIZER;
+
+/**
+ Returns a custom shape data source initialized with an identifier, data source, and a
+ dictionary of options for the source according to the
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson">style
+ specification</a>.
+
+ @param identifier A string that uniquely identifies the source.
+ @param options An `NSDictionary` of options for this source.
+ */
+- (instancetype)initWithIdentifier:(NSString *)identifier dataSource:(id<MGLComputedShapeSourceDataSource>)dataSource options:(nullable NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options;
+
+/**
+ Invalidates all the features and properties intersecting with or contained in
+ the specified bounds. New fetch requests will immediately be invoked on the
+ `MGLComputedShapeSourceDataSource`.
+ @param bounds Coordinate bounds to invalidate.
+ */
+- (void) invalidateBounds:(MGLCoordinateBounds)bounds;
+
+/**
+ Invalidates all the feautres and properties of a given tile. A new fetch request
+ will immediately be invoked on the `MGLComputedShapeSourceDataSource`.
+ @param x Tile X coordinate.
+ @param y Tile Y coordinate.
+ @param zoomLevel Tile zoom level.
+ */
+- (void) invalidateTileAtX:(NSUInteger)x y:(NSUInteger)y zoomLevel:(NSUInteger)zoomLevel;
+
+/**
+ Set a new set of features for a tile. This method can be invkoed from background threads.
+ For best performance, use this method only to update tiles that have already been requested
+ through `MGLComputedShapeSourceDataSource.`
+ @param features Features for the tile.
+ @param x Tile X coordinate.
+ @param y Tile Y coordinate.
+ @param zoomLevel Tile zoom level.
+ */
+- (void) setFeatures:(NSArray<MGLShape <MGLFeature> *>*)features inTileAtX:(NSUInteger)x y:(NSUInteger)y zoomLevel:(NSUInteger)zoomLevel;
+
+/**
+ An object that implements the `MGLComputedShapeSourceDataSource` protocol that will be queried for tile data.
+ */
+@property (nonatomic, weak, nullable) id<MGLComputedShapeSourceDataSource> dataSource;
+
+/**
+ A queue that calls to the data source will be made on.
+ */
+@property (nonatomic, readonly) NSOperationQueue *requestQueue;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLComputedShapeSource.mm b/platform/darwin/src/MGLComputedShapeSource.mm
new file mode 100644
index 0000000000..0a3c92bb97
--- /dev/null
+++ b/platform/darwin/src/MGLComputedShapeSource.mm
@@ -0,0 +1,169 @@
+#import "MGLComputedShapeSource.h"
+
+#import "MGLMapView_Private.h"
+#import "MGLSource_Private.h"
+#import "MGLShape_Private.h"
+#import "MGLAbstractShapeSource_Private.h"
+#import "MGLGeometry_Private.h"
+
+#include <mbgl/map/map.hpp>
+#include <mbgl/style/sources/custom_geometry_source.hpp>
+#include <mbgl/tile/tile_id.hpp>
+#include <mbgl/util/geojson.hpp>
+
+@interface MGLComputedShapeSource () {
+ std::unique_ptr<mbgl::style::CustomGeometrySource> _pendingSource;
+}
+
+@property (nonatomic, readwrite) NSDictionary *options;
+@property (nonatomic, assign) BOOL dataSourceImplementsFeaturesForTile;
+@property (nonatomic, assign) BOOL dataSourceImplementsFeaturesForBounds;
+
+@end
+
+@interface MGLComputedShapeSourceFetchOperation : NSOperation
+
+@property (nonatomic, readonly) uint8_t z;
+@property (nonatomic, readonly) uint32_t x;
+@property (nonatomic, readonly) uint32_t y;
+@property (nonatomic, assign) BOOL dataSourceImplementsFeaturesForTile;
+@property (nonatomic, assign) BOOL dataSourceImplementsFeaturesForBounds;
+@property (nonatomic, weak, nullable) id<MGLComputedShapeSourceDataSource> dataSource;
+@property (nonatomic, nullable) mbgl::style::CustomGeometrySource *rawSource;
+
+- (instancetype)initForSource:(MGLComputedShapeSource*)source tile:(const mbgl::CanonicalTileID&)tileId;
+
+@end
+
+@implementation MGLComputedShapeSourceFetchOperation
+
+- (instancetype)initForSource:(MGLComputedShapeSource*)source tile:(const mbgl::CanonicalTileID&)tileID {
+ self = [super init];
+ _z = tileID.z;
+ _x = tileID.x;
+ _y = tileID.y;
+ _dataSourceImplementsFeaturesForTile = source.dataSourceImplementsFeaturesForTile;
+ _dataSourceImplementsFeaturesForBounds = source.dataSourceImplementsFeaturesForBounds;
+ _dataSource = source.dataSource;
+ mbgl::style::CustomGeometrySource *rawSource = static_cast<mbgl::style::CustomGeometrySource *>(source.rawSource);
+ _rawSource = rawSource;
+ return self;
+}
+
+- (void)main {
+ if ([self isCancelled]) {
+ return;
+ }
+
+ NSArray<MGLShape <MGLFeature> *> *data;
+ if(!self.dataSource) {
+ data = nil;
+ } else if(self.dataSourceImplementsFeaturesForTile) {
+ data = [self.dataSource featuresInTileAtX:self.x
+ y:self.y
+ zoomLevel:self.z];
+ } else {
+ mbgl::CanonicalTileID tileID = mbgl::CanonicalTileID(self.z, self.x, self.y);
+ mbgl::LatLngBounds tileBounds = mbgl::LatLngBounds(tileID);
+ data = [self.dataSource featuresInCoordinateBounds:MGLCoordinateBoundsFromLatLngBounds(tileBounds)
+ zoomLevel:self.z];
+ }
+
+ if(![self isCancelled]) {
+ mbgl::FeatureCollection featureCollection;
+ featureCollection.reserve(data.count);
+ for (MGLShape <MGLFeature> * feature in data) {
+ mbgl::Feature geoJsonObject = [feature geoJSONObject].get<mbgl::Feature>();
+ featureCollection.push_back(geoJsonObject);
+ }
+ const auto geojson = mbgl::GeoJSON{featureCollection};
+ if(![self isCancelled] && self.rawSource) {
+ self.rawSource->setTileData(mbgl::CanonicalTileID(self.z, self.x, self.y), geojson);
+ }
+ }
+}
+
+- (void)cancel {
+ [super cancel];
+ self.rawSource = NULL;
+}
+
+@end
+
+@implementation MGLComputedShapeSource
+
+- (instancetype)initWithIdentifier:(NSString *)identifier options:(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options {
+ NSOperationQueue *requestQueue = [[NSOperationQueue alloc] init];
+ requestQueue.name = [NSString stringWithFormat:@"mgl.MGLComputedShapeSource.%@", identifier];
+ requestQueue.qualityOfService = NSQualityOfServiceUtility;
+ requestQueue.maxConcurrentOperationCount = 4;
+
+ auto sourceOptions = MBGLCustomGeometrySourceOptionsFromDictionary(options);
+ sourceOptions.fetchTileFunction = ^void(const mbgl::CanonicalTileID& tileID) {
+ NSOperation *operation = [[MGLComputedShapeSourceFetchOperation alloc] initForSource:self tile:tileID];
+ [requestQueue addOperation:operation];
+ };
+
+ sourceOptions.cancelTileFunction = ^void(const mbgl::CanonicalTileID& tileID) {
+ for (MGLComputedShapeSourceFetchOperation *operation in requestQueue.operations) {
+ if (operation.x == tileID.x && operation.y == tileID.y && operation.z == tileID.z) {
+ [operation cancel];
+ }
+ }
+ };
+
+ auto source = std::make_unique<mbgl::style::CustomGeometrySource>(identifier.UTF8String, sourceOptions);
+
+ if (self = [super initWithPendingSource:std::move(source)]) {
+ _requestQueue = requestQueue;
+ }
+ return self;
+}
+
+- (instancetype)initWithIdentifier:(NSString *)identifier dataSource:(id<MGLComputedShapeSourceDataSource>)dataSource options:(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options {
+ if (self = [self initWithIdentifier:identifier options:options]) {
+ [self setDataSource:dataSource];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [self.requestQueue cancelAllOperations];
+}
+
+- (void)setFeatures:(NSArray<MGLShape <MGLFeature> *>*)features inTileAtX:(NSUInteger)x y:(NSUInteger)y zoomLevel:(NSUInteger)zoomLevel {
+ mbgl::CanonicalTileID tileID = mbgl::CanonicalTileID((uint8_t)zoomLevel, (uint32_t)x, (uint32_t)y);
+ mbgl::FeatureCollection featureCollection;
+ featureCollection.reserve(features.count);
+ for (MGLShape <MGLFeature> * feature in features) {
+ mbgl::Feature geoJsonObject = [feature geoJSONObject].get<mbgl::Feature>();
+ featureCollection.push_back(geoJsonObject);
+ }
+ const auto geojson = mbgl::GeoJSON{featureCollection};
+ static_cast<mbgl::style::CustomGeometrySource *>(self.rawSource)->setTileData(tileID, geojson);
+}
+
+- (void)setDataSource:(id<MGLComputedShapeSourceDataSource>)dataSource {
+ [self.requestQueue cancelAllOperations];
+ // Check which method the datasource implements, to avoid having to check for each tile
+ self.dataSourceImplementsFeaturesForTile = [dataSource respondsToSelector:@selector(featuresInTileAtX:y:zoomLevel:)];
+ self.dataSourceImplementsFeaturesForBounds = [dataSource respondsToSelector:@selector(featuresInCoordinateBounds:zoomLevel:)];
+
+ if(!self.dataSourceImplementsFeaturesForBounds && !self.dataSourceImplementsFeaturesForTile) {
+ [NSException raise:@"Invalid Datasource" format:@"Datasource does not implement any MGLComputedShapeSourceDataSource methods"];
+ } else if(self.dataSourceImplementsFeaturesForBounds && self.dataSourceImplementsFeaturesForTile) {
+ [NSException raise:@"Invalid Datasource" format:@"Datasource implements multiple MGLComputedShapeSourceDataSource methods"];
+ }
+
+ _dataSource = dataSource;
+}
+
+- (void) invalidateBounds:(MGLCoordinateBounds)bounds {
+ ((mbgl::style::CustomGeometrySource *)self.rawSource)->invalidateRegion(MGLLatLngBoundsFromCoordinateBounds(bounds));
+}
+
+- (void) invalidateTileAtX:(NSUInteger)x y:(NSUInteger)y zoomLevel:(NSUInteger)z {
+ ((mbgl::style::CustomGeometrySource *)self.rawSource)->invalidateTile(mbgl::CanonicalTileID(z, (unsigned int)x, (unsigned int)y));
+}
+
+@end
diff --git a/platform/darwin/src/MGLConversion.h b/platform/darwin/src/MGLConversion.h
index d6363b28eb..92a6720e6a 100644
--- a/platform/darwin/src/MGLConversion.h
+++ b/platform/darwin/src/MGLConversion.h
@@ -1,9 +1,4 @@
-#import <Foundation/Foundation.h>
-
-#include <mbgl/util/logging.hpp>
#include <mbgl/style/conversion.hpp>
-#include <mbgl/util/feature.hpp>
-#include <mbgl/util/optional.hpp>
NS_ASSUME_NONNULL_BEGIN
@@ -11,128 +6,151 @@ namespace mbgl {
namespace style {
namespace conversion {
-/**
- A minimal wrapper class conforming to the requirements for `objectMember(v, name)` (see mbgl/style/conversion.hpp)
- This is necessary because using `NSObject*` as the value type in `optional<NSObject*>` causes problems for the ARC,
- due to things like `optional(const value_type& __v)`
- */
-class OptionalNSObjectValue {
+// A wrapper class for `id`, so as not to confuse ARC.
+class Holder {
public:
- OptionalNSObjectValue(NSObject * _Nullable _value) : value(_value) {}
-
- explicit operator bool() const {
- return value;
+ Holder(const id v) : value(v) {}
+ const id value;
+};
+
+template <>
+class ConversionTraits<Holder> {
+public:
+ static bool isUndefined(const Holder& holder) {
+ const id value = holder.value;
+ return !value || value == [NSNull null];
}
-
- NSObject * _Nullable operator*() {
- NSCAssert(this, @"Expected non-null value.");
- return value;
+
+ static bool isArray(const Holder& holder) {
+ const id value = holder.value;
+ return [value isKindOfClass:[NSArray class]];
}
-private:
- NSObject * _Nullable value;
-};
-inline bool isUndefined(const id value) {
- return !value || value == [NSNull null];
-}
+ static bool isObject(const Holder& holder) {
+ const id value = holder.value;
+ return [value isKindOfClass:[NSDictionary class]];
+ }
-inline bool isArray(const id value) {
- return [value isKindOfClass:[NSArray class]];
-}
+ static std::size_t arrayLength(const Holder& holder) {
+ const id value = holder.value;
+ NSCAssert([value isKindOfClass:[NSArray class]], @"Value must be an NSArray for getLength().");
+ NSArray *array = value;
+ auto length = [array count];
+ NSCAssert(length <= std::numeric_limits<size_t>::max(), @"Array length out of bounds.");
+ return length;
+ }
-inline bool isObject(const id value) {
- return [value isKindOfClass:[NSDictionary class]];
-}
+ static Holder arrayMember(const Holder& holder, std::size_t i) {
+ const id value = holder.value;
+ NSCAssert([value isKindOfClass:[NSArray class]], @"Value must be an NSArray for get(int).");
+ NSCAssert(i < NSUIntegerMax, @"Index must be less than NSUIntegerMax");
+ return {[value objectAtIndex: i]};
+ }
-inline std::size_t arrayLength(const id value) {
- NSCAssert([value isKindOfClass:[NSArray class]], @"Value must be an NSArray for getLength().");
- NSArray *array = value;
- auto length = [array count];
- NSCAssert(length <= std::numeric_limits<size_t>::max(), @"Array length out of bounds.");
- return length;
-}
+ static optional<Holder> objectMember(const Holder& holder, const char *key) {
+ const id value = holder.value;
+ NSCAssert([value isKindOfClass:[NSDictionary class]], @"Value must be an NSDictionary for get(string).");
+ NSObject *member = [value objectForKey: @(key)];
+ if (member && member != [NSNull null]) {
+ return {member};
+ } else {
+ return {};
+ }
+ }
-inline NSObject *arrayMember(const id value, std::size_t i) {
- NSCAssert([value isKindOfClass:[NSArray class]], @"Value must be an NSArray for get(int).");
- NSCAssert(i < NSUIntegerMax, @"Index must be less than NSUIntegerMax");
- return [value objectAtIndex: i];
-}
+// Compiler is wrong about `Fn` parameter missing a nullability specifier.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnullability-completeness"
+ template <class Fn>
+ static optional<Error> eachMember(const Holder& holder, Fn&& visit) {
+#pragma clang diagnostic pop
+ [holder.value enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
+ auto result = visit(std::string(static_cast<const char *>([key UTF8String])), obj);
+ if (result) {
+ *stop = YES;
+ }
+ }];
+ return {};
+ }
-inline OptionalNSObjectValue objectMember(const id value, const char *key) {
- NSCAssert([value isKindOfClass:[NSDictionary class]], @"Value must be an NSDictionary for get(string).");
- NSObject *member = [value objectForKey: @(key)];
- if (member && member != [NSNull null]) {
- return { member };
- } else {
- return { nullptr };
+ static optional<bool> toBool(const Holder& holder) {
+ const id value = holder.value;
+ if (_isBool(value)) {
+ return ((NSNumber *)value).boolValue;
+ } else {
+ return {};
+ }
}
-}
-// Not implemented (unneeded for MGLStyleFunction conversion):
-// optional<Error> eachMember(const NSObject*, Fn&&)
+ static optional<float> toNumber(const Holder& holder) {
+ const id value = holder.value;
+ if (_isNumber(value)) {
+ return ((NSNumber *)value).floatValue;
+ } else {
+ return {};
+ }
+ }
-inline bool _isBool(const id value) {
- if (![value isKindOfClass:[NSNumber class]]) return false;
- // char: 32-bit boolean
- // BOOL: 64-bit boolean
- NSNumber *number = value;
- return ((strcmp([number objCType], @encode(char)) == 0) ||
- (strcmp([number objCType], @encode(BOOL)) == 0));
-}
-
-inline bool _isNumber(const id value) {
- return [value isKindOfClass:[NSNumber class]] && !_isBool(value);
-}
-
-inline bool _isString(const id value) {
- return [value isKindOfClass:[NSString class]];
-}
+ static optional<double> toDouble(const Holder& holder) {
+ const id value = holder.value;
+ if (_isNumber(value)) {
+ return ((NSNumber *)value).doubleValue;
+ } else {
+ return {};
+ }
+ }
-inline optional<bool> toBool(const id value) {
- if (_isBool(value)) {
- return ((NSNumber *)value).boolValue;
- } else {
- return {};
+ static optional<std::string> toString(const Holder& holder) {
+ const id value = holder.value;
+ if (_isString(value)) {
+ return std::string(static_cast<const char *>([value UTF8String]));
+ } else {
+ return {};
+ }
}
-}
-inline optional<float> toNumber(const id value) {
- if (_isNumber(value)) {
- return ((NSNumber *)value).floatValue;
- } else {
- return {};
+ static optional<mbgl::Value> toValue(const Holder& holder) {
+ const id value = holder.value;
+ if (isUndefined(value)) {
+ return {};
+ } else if (_isBool(value)) {
+ return { *toBool(holder) };
+ } else if ( _isString(value)) {
+ return { *toString(holder) };
+ } else if (_isNumber(value)) {
+ // Need to cast to a double here as the float is otherwise considered a bool...
+ return { static_cast<double>(*toNumber(holder)) };
+ } else {
+ return {};
+ }
}
-}
-inline optional<double> toDouble(const id value) {
- if (_isNumber(value)) {
- return ((NSNumber *)value).doubleValue;
- } else {
+ static optional<GeoJSON> toGeoJSON(const Holder& holder, Error& error) {
+ error = { "toGeoJSON not implemented" };
return {};
}
-}
-inline optional<std::string> toString(const id value) {
- if (_isString(value)) {
- return std::string(static_cast<const char *>([value UTF8String]));
- } else {
- return {};
+private:
+ static bool _isBool(const id value) {
+ if (![value isKindOfClass:[NSNumber class]]) return false;
+ // char: 32-bit boolean
+ // BOOL: 64-bit boolean
+ NSNumber *number = value;
+ return ((strcmp([number objCType], @encode(char)) == 0) ||
+ (strcmp([number objCType], @encode(BOOL)) == 0));
}
-}
-inline optional<mbgl::Value> toValue(const id value) {
- if (isUndefined(value)) {
- return {};
- } else if (_isBool(value)) {
- return { *toBool(value) };
- } else if ( _isString(value)) {
- return { *toString(value) };
- } else if (_isNumber(value)) {
- // Need to cast to a double here as the float is otherwise considered a bool...
- return { static_cast<double>(*toNumber(value)) };
- } else {
- return {};
+ static bool _isNumber(const id value) {
+ return [value isKindOfClass:[NSNumber class]] && !_isBool(value);
}
+
+ static bool _isString(const id value) {
+ return [value isKindOfClass:[NSString class]];
+ }
+};
+
+inline Convertible makeConvertible(const id value) {
+ return Convertible(Holder(value));
}
} // namespace conversion
@@ -140,4 +158,3 @@ inline optional<mbgl::Value> toValue(const id value) {
} // namespace mbgl
NS_ASSUME_NONNULL_END
-
diff --git a/platform/darwin/src/MGLFeature.mm b/platform/darwin/src/MGLFeature.mm
index 84f1a1ff25..ee2c71be21 100644
--- a/platform/darwin/src/MGLFeature.mm
+++ b/platform/darwin/src/MGLFeature.mm
@@ -7,12 +7,12 @@
#import "MGLShape_Private.h"
#import "MGLPointCollection_Private.h"
-#import "MGLPolyline+MGLAdditions.h"
-#import "MGLPolygon+MGLAdditions.h"
+#import "MGLPolyline_Private.h"
+#import "MGLPolygon_Private.h"
+
#import "NSDictionary+MGLAdditions.h"
#import "NSArray+MGLAdditions.h"
-
-#import "NSExpression+MGLAdditions.h"
+#import "NSExpression+MGLPrivateAdditions.h"
#import <mbgl/util/geometry.hpp>
#import <mbgl/style/conversion/geojson.hpp>
diff --git a/platform/darwin/src/MGLFillExtrusionStyleLayer.h b/platform/darwin/src/MGLFillExtrusionStyleLayer.h
index c4fb9aa77e..d1d5af6ba2 100644
--- a/platform/darwin/src/MGLFillExtrusionStyleLayer.h
+++ b/platform/darwin/src/MGLFillExtrusionStyleLayer.h
@@ -2,13 +2,13 @@
// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
#import "MGLFoundation.h"
-#import "MGLStyleValue.h"
#import "MGLVectorStyleLayer.h"
NS_ASSUME_NONNULL_BEGIN
/**
- Controls the translation reference point.
+ Controls the frame of reference for
+ `MGLFillExtrusionStyleLayer.fillExtrusionTranslation`.
Values of this type are used in the `MGLFillExtrusionStyleLayer.fillExtrusionTranslationAnchor`
property.
@@ -44,8 +44,8 @@ typedef NS_ENUM(NSUInteger, MGLFillExtrusionTranslationAnchor) {
```swift
let layer = MGLFillExtrusionStyleLayer(identifier: "buildings", source: buildings)
layer.sourceLayerIdentifier = "building"
- layer.fillExtrusionHeight = MGLStyleValue(interpolationMode: .identity, sourceStops: nil, attributeName: "height", options: nil)
- layer.fillExtrusionBase = MGLStyleValue(interpolationMode: .identity, sourceStops: nil, attributeName: "min_height", options: nil)
+ layer.fillExtrusionHeight = NSExpression(forKeyPath: "height")
+ layer.fillExtrusionBase = NSExpression(forKeyPath: "min_height")
layer.predicate = NSPredicate(format: "extrude == 'true'")
mapView.style?.addLayer(layer)
```
@@ -76,30 +76,22 @@ MGL_EXPORT
This property is measured in meters.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `0`. Set this property to `nil` to reset
- it to the default value.
+ The default value of this property is an expression that evaluates to the float
+ `0`. Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `fillExtrusionHeight` is
non-`nil`. Otherwise, it is ignored.
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
+ You can set this property to an expression containing any of the following:
+
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *fillExtrusionBase;
+@property (nonatomic, null_resettable) NSExpression *fillExtrusionBase;
/**
The transition affecting any changes to this layer’s `fillExtrusionBase` property.
@@ -108,69 +100,29 @@ MGL_EXPORT
*/
@property (nonatomic) MGLTransition fillExtrusionBaseTransition;
-#if TARGET_OS_IPHONE
/**
The base color of this layer. The extrusion's surfaces will be shaded
differently based on this color in combination with the `light` settings. If
this color is specified with an alpha component, the alpha component will be
ignored; use `fillExtrusionOpacity` to set layer opacityco.
- The default value of this property is an `MGLStyleValue` object containing
+ The default value of this property is an expression that evaluates to
`UIColor.blackColor`. Set this property to `nil` to reset it to the default
value.
This property is only applied to the style if `fillExtrusionPattern` is set to
`nil`. Otherwise, it is ignored.
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- */
-@property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *fillExtrusionColor;
-#else
-/**
- The base color of this layer. The extrusion's surfaces will be shaded
- differently based on this color in combination with the `light` settings. If
- this color is specified with an alpha component, the alpha component will be
- ignored; use `fillExtrusionOpacity` to set layer opacityco.
-
- The default value of this property is an `MGLStyleValue` object containing
- `NSColor.blackColor`. Set this property to `nil` to reset it to the default
- value.
-
- This property is only applied to the style if `fillExtrusionPattern` is set to
- `nil`. Otherwise, it is ignored.
+ You can set this property to an expression containing any of the following:
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
+ * Constant `UIColor` values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *fillExtrusionColor;
-#endif
+@property (nonatomic, null_resettable) NSExpression *fillExtrusionColor;
/**
The transition affecting any changes to this layer’s `fillExtrusionColor` property.
@@ -184,27 +136,19 @@ MGL_EXPORT
This property is measured in meters.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `0`. Set this property to `nil` to reset
- it to the default value.
-
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
+ The default value of this property is an expression that evaluates to the float
+ `0`. Set this property to `nil` to reset it to the default value.
+
+ You can set this property to an expression containing any of the following:
+
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *fillExtrusionHeight;
+@property (nonatomic, null_resettable) NSExpression *fillExtrusionHeight;
/**
The transition affecting any changes to this layer’s `fillExtrusionHeight` property.
@@ -217,18 +161,21 @@ MGL_EXPORT
The opacity of the entire fill extrusion layer. This is rendered on a
per-layer, not per-feature, basis, and data-driven styling is not available.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `1`. Set this property to `nil` to reset
- it to the default value.
+ The default value of this property is an expression that evaluates to the float
+ `1`. Set this property to `nil` to reset it to the default value.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
+
+ This property does not support applying interpolation or step functions to
+ feature attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *fillExtrusionOpacity;
+@property (nonatomic, null_resettable) NSExpression *fillExtrusionOpacity;
/**
The transition affecting any changes to this layer’s `fillExtrusionOpacity` property.
@@ -242,13 +189,19 @@ MGL_EXPORT
seamless patterns, image width and height must be a factor of two (2, 4, 8,
..., 512).
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
+
+ * Constant string values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Step functions applied to the `$zoomLevel` variable
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of
- `MGLInterpolationModeInterval`
+ This property does not support applying interpolation functions to the
+ `$zoomLevel` variable or applying interpolation or step functions to feature
+ attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSString *> *fillExtrusionPattern;
+@property (nonatomic, null_resettable) NSExpression *fillExtrusionPattern;
/**
The transition affecting any changes to this layer’s `fillExtrusionPattern` property.
@@ -263,7 +216,7 @@ MGL_EXPORT
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an
+ The default value of this property is an expression that evaluates to an
`NSValue` object containing a `CGVector` struct set to 0 points rightward and 0
points downward. Set this property to `nil` to reset it to the default value.
@@ -271,21 +224,25 @@ MGL_EXPORT
href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-fill-extrusion-translate"><code>fill-extrusion-translate</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
+
+ * Constant `CGVector` values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
+ This property does not support applying interpolation or step functions to
+ feature attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *fillExtrusionTranslation;
+@property (nonatomic, null_resettable) NSExpression *fillExtrusionTranslation;
#else
/**
The geometry's offset.
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an
+ The default value of this property is an expression that evaluates to an
`NSValue` object containing a `CGVector` struct set to 0 points rightward and 0
points upward. Set this property to `nil` to reset it to the default value.
@@ -293,14 +250,18 @@ MGL_EXPORT
href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-fill-extrusion-translate"><code>fill-extrusion-translate</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
+ * Constant `CGVector` values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
+
+ This property does not support applying interpolation or step functions to
+ feature attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *fillExtrusionTranslation;
+@property (nonatomic, null_resettable) NSExpression *fillExtrusionTranslation;
#endif
/**
@@ -310,14 +271,13 @@ MGL_EXPORT
*/
@property (nonatomic) MGLTransition fillExtrusionTranslationTransition;
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *fillExtrusionTranslate __attribute__((unavailable("Use fillExtrusionTranslation instead.")));
+@property (nonatomic, null_resettable) NSExpression *fillExtrusionTranslate __attribute__((unavailable("Use fillExtrusionTranslation instead.")));
/**
- Controls the translation reference point.
+ Controls the frame of reference for `fillExtrusionTranslation`.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSValue` object containing `MGLFillExtrusionTranslationAnchorMap`. Set this
- property to `nil` to reset it to the default value.
+ The default value of this property is an expression that evaluates to `map`.
+ Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `fillExtrusionTranslation` is
non-`nil`. Otherwise, it is ignored.
@@ -326,15 +286,24 @@ MGL_EXPORT
href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-fill-extrusion-translate-anchor"><code>fill-extrusion-translate-anchor</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
+
+ * Constant `MGLFillExtrusionTranslationAnchor` values
+ * Any of the following constant string values:
+ * `map`: The fill extrusion is translated relative to the map.
+ * `viewport`: The fill extrusion is translated relative to the viewport.
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Step functions applied to the `$zoomLevel` variable
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of
- `MGLInterpolationModeInterval`
+ This property does not support applying interpolation functions to the
+ `$zoomLevel` variable or applying interpolation or step functions to feature
+ attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *fillExtrusionTranslationAnchor;
+@property (nonatomic, null_resettable) NSExpression *fillExtrusionTranslationAnchor;
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *fillExtrusionTranslateAnchor __attribute__((unavailable("Use fillExtrusionTranslationAnchor instead.")));
+@property (nonatomic, null_resettable) NSExpression *fillExtrusionTranslateAnchor __attribute__((unavailable("Use fillExtrusionTranslationAnchor instead.")));
@end
diff --git a/platform/darwin/src/MGLFillExtrusionStyleLayer.mm b/platform/darwin/src/MGLFillExtrusionStyleLayer.mm
index b00ed8e09f..1baa264689 100644
--- a/platform/darwin/src/MGLFillExtrusionStyleLayer.mm
+++ b/platform/darwin/src/MGLFillExtrusionStyleLayer.mm
@@ -77,21 +77,21 @@ namespace mbgl {
#pragma mark - Accessing the Paint Attributes
-- (void)setFillExtrusionBase:(MGLStyleValue<NSNumber *> *)fillExtrusionBase {
+- (void)setFillExtrusionBase:(NSExpression *)fillExtrusionBase {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(fillExtrusionBase);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(fillExtrusionBase);
self.rawLayer->setFillExtrusionBase(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)fillExtrusionBase {
+- (NSExpression *)fillExtrusionBase {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getFillExtrusionBase();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultFillExtrusionBase());
+ propertyValue = self.rawLayer->getDefaultFillExtrusionBase();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
- (void)setFillExtrusionBaseTransition:(MGLTransition )transition {
@@ -112,21 +112,21 @@ namespace mbgl {
return transition;
}
-- (void)setFillExtrusionColor:(MGLStyleValue<MGLColor *> *)fillExtrusionColor {
+- (void)setFillExtrusionColor:(NSExpression *)fillExtrusionColor {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenPropertyValue(fillExtrusionColor);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<mbgl::Color>>(fillExtrusionColor);
self.rawLayer->setFillExtrusionColor(mbglValue);
}
-- (MGLStyleValue<MGLColor *> *)fillExtrusionColor {
+- (NSExpression *)fillExtrusionColor {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getFillExtrusionColor();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(self.rawLayer->getDefaultFillExtrusionColor());
+ propertyValue = self.rawLayer->getDefaultFillExtrusionColor();
}
- return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toExpression(propertyValue);
}
- (void)setFillExtrusionColorTransition:(MGLTransition )transition {
@@ -147,21 +147,21 @@ namespace mbgl {
return transition;
}
-- (void)setFillExtrusionHeight:(MGLStyleValue<NSNumber *> *)fillExtrusionHeight {
+- (void)setFillExtrusionHeight:(NSExpression *)fillExtrusionHeight {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(fillExtrusionHeight);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(fillExtrusionHeight);
self.rawLayer->setFillExtrusionHeight(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)fillExtrusionHeight {
+- (NSExpression *)fillExtrusionHeight {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getFillExtrusionHeight();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultFillExtrusionHeight());
+ propertyValue = self.rawLayer->getDefaultFillExtrusionHeight();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
- (void)setFillExtrusionHeightTransition:(MGLTransition )transition {
@@ -182,21 +182,21 @@ namespace mbgl {
return transition;
}
-- (void)setFillExtrusionOpacity:(MGLStyleValue<NSNumber *> *)fillExtrusionOpacity {
+- (void)setFillExtrusionOpacity:(NSExpression *)fillExtrusionOpacity {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(fillExtrusionOpacity);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(fillExtrusionOpacity);
self.rawLayer->setFillExtrusionOpacity(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)fillExtrusionOpacity {
+- (NSExpression *)fillExtrusionOpacity {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getFillExtrusionOpacity();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultFillExtrusionOpacity());
+ propertyValue = self.rawLayer->getDefaultFillExtrusionOpacity();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
- (void)setFillExtrusionOpacityTransition:(MGLTransition )transition {
@@ -217,21 +217,21 @@ namespace mbgl {
return transition;
}
-- (void)setFillExtrusionPattern:(MGLStyleValue<NSString *> *)fillExtrusionPattern {
+- (void)setFillExtrusionPattern:(NSExpression *)fillExtrusionPattern {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<std::string, NSString *>().toPropertyValue(fillExtrusionPattern);
+ auto mbglValue = MGLStyleValueTransformer<std::string, NSString *>().toPropertyValue<mbgl::style::PropertyValue<std::string>>(fillExtrusionPattern);
self.rawLayer->setFillExtrusionPattern(mbglValue);
}
-- (MGLStyleValue<NSString *> *)fillExtrusionPattern {
+- (NSExpression *)fillExtrusionPattern {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getFillExtrusionPattern();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<std::string, NSString *>().toStyleValue(self.rawLayer->getDefaultFillExtrusionPattern());
+ propertyValue = self.rawLayer->getDefaultFillExtrusionPattern();
}
- return MGLStyleValueTransformer<std::string, NSString *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
}
- (void)setFillExtrusionPatternTransition:(MGLTransition )transition {
@@ -252,21 +252,21 @@ namespace mbgl {
return transition;
}
-- (void)setFillExtrusionTranslation:(MGLStyleValue<NSValue *> *)fillExtrusionTranslation {
+- (void)setFillExtrusionTranslation:(NSExpression *)fillExtrusionTranslation {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toInterpolatablePropertyValue(fillExtrusionTranslation);
+ auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toPropertyValue<mbgl::style::PropertyValue<std::array<float, 2>>>(fillExtrusionTranslation);
self.rawLayer->setFillExtrusionTranslate(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)fillExtrusionTranslation {
+- (NSExpression *)fillExtrusionTranslation {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getFillExtrusionTranslate();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toStyleValue(self.rawLayer->getDefaultFillExtrusionTranslate());
+ propertyValue = self.rawLayer->getDefaultFillExtrusionTranslate();
}
- return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toExpression(propertyValue);
}
- (void)setFillExtrusionTranslationTransition:(MGLTransition )transition {
@@ -287,34 +287,34 @@ namespace mbgl {
return transition;
}
-- (void)setFillExtrusionTranslate:(MGLStyleValue<NSValue *> *)fillExtrusionTranslate {
+- (void)setFillExtrusionTranslate:(NSExpression *)fillExtrusionTranslate {
}
-- (MGLStyleValue<NSValue *> *)fillExtrusionTranslate {
+- (NSExpression *)fillExtrusionTranslate {
return self.fillExtrusionTranslation;
}
-- (void)setFillExtrusionTranslationAnchor:(MGLStyleValue<NSValue *> *)fillExtrusionTranslationAnchor {
+- (void)setFillExtrusionTranslationAnchor:(NSExpression *)fillExtrusionTranslationAnchor {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLFillExtrusionTranslationAnchor>().toEnumPropertyValue(fillExtrusionTranslationAnchor);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLFillExtrusionTranslationAnchor>().toPropertyValue<mbgl::style::PropertyValue<mbgl::style::TranslateAnchorType>>(fillExtrusionTranslationAnchor);
self.rawLayer->setFillExtrusionTranslateAnchor(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)fillExtrusionTranslationAnchor {
+- (NSExpression *)fillExtrusionTranslationAnchor {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getFillExtrusionTranslateAnchor();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLFillExtrusionTranslationAnchor>().toEnumStyleValue(self.rawLayer->getDefaultFillExtrusionTranslateAnchor());
+ propertyValue = self.rawLayer->getDefaultFillExtrusionTranslateAnchor();
}
- return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLFillExtrusionTranslationAnchor>().toEnumStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLFillExtrusionTranslationAnchor>().toExpression(propertyValue);
}
-- (void)setFillExtrusionTranslateAnchor:(MGLStyleValue<NSValue *> *)fillExtrusionTranslateAnchor {
+- (void)setFillExtrusionTranslateAnchor:(NSExpression *)fillExtrusionTranslateAnchor {
}
-- (MGLStyleValue<NSValue *> *)fillExtrusionTranslateAnchor {
+- (NSExpression *)fillExtrusionTranslateAnchor {
return self.fillExtrusionTranslationAnchor;
}
diff --git a/platform/darwin/src/MGLFillStyleLayer.h b/platform/darwin/src/MGLFillStyleLayer.h
index 6e3297bdec..5caab91b45 100644
--- a/platform/darwin/src/MGLFillStyleLayer.h
+++ b/platform/darwin/src/MGLFillStyleLayer.h
@@ -2,13 +2,12 @@
// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
#import "MGLFoundation.h"
-#import "MGLStyleValue.h"
#import "MGLVectorStyleLayer.h"
NS_ASSUME_NONNULL_BEGIN
/**
- Controls the translation reference point.
+ Controls the frame of reference for `MGLFillStyleLayer.fillTranslation`.
Values of this type are used in the `MGLFillStyleLayer.fillTranslationAnchor`
property.
@@ -44,7 +43,7 @@ typedef NS_ENUM(NSUInteger, MGLFillTranslationAnchor) {
```swift
let layer = MGLFillStyleLayer(identifier: "parks", source: parks)
layer.sourceLayerIdentifier = "parks"
- layer.fillColor = MGLStyleValue(rawValue: .green)
+ layer.fillColor = NSExpression(forConstantValue: UIColor.green)
layer.predicate = NSPredicate(format: "type == %@", "national-park")
mapView.style?.addLayer(layer)
```
@@ -72,81 +71,49 @@ MGL_EXPORT
/**
Whether or not the fill should be antialiased.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing `YES`. Set this property to `nil` to reset it to
- the default value.
+ The default value of this property is an expression that evaluates to `YES`.
+ Set this property to `nil` to reset it to the default value.
This attribute corresponds to the <a
href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-fill-antialias"><code>fill-antialias</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of
- `MGLInterpolationModeInterval`
+ * Constant Boolean values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Step functions applied to the `$zoomLevel` variable
+
+ This property does not support applying interpolation functions to the
+ `$zoomLevel` variable or applying interpolation or step functions to feature
+ attributes.
*/
-@property (nonatomic, null_resettable, getter=isFillAntialiased) MGLStyleValue<NSNumber *> *fillAntialiased;
+@property (nonatomic, null_resettable, getter=isFillAntialiased) NSExpression *fillAntialiased;
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *fillAntialias __attribute__((unavailable("Use fillAntialiased instead.")));
+@property (nonatomic, null_resettable) NSExpression *fillAntialias __attribute__((unavailable("Use fillAntialiased instead.")));
-#if TARGET_OS_IPHONE
/**
The color of the filled part of this layer.
- The default value of this property is an `MGLStyleValue` object containing
+ The default value of this property is an expression that evaluates to
`UIColor.blackColor`. Set this property to `nil` to reset it to the default
value.
This property is only applied to the style if `fillPattern` is set to `nil`.
Otherwise, it is ignored.
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- */
-@property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *fillColor;
-#else
-/**
- The color of the filled part of this layer.
-
- The default value of this property is an `MGLStyleValue` object containing
- `NSColor.blackColor`. Set this property to `nil` to reset it to the default
- value.
-
- This property is only applied to the style if `fillPattern` is set to `nil`.
- Otherwise, it is ignored.
+ You can set this property to an expression containing any of the following:
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
+ * Constant `UIColor` values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *fillColor;
-#endif
+@property (nonatomic, null_resettable) NSExpression *fillColor;
/**
The transition affecting any changes to this layer’s `fillColor` property.
@@ -159,27 +126,19 @@ MGL_EXPORT
The opacity of the entire fill layer. In contrast to the `fillColor`, this
value will also affect the 1pt stroke around the fill, if the stroke is used.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `1`. Set this property to `nil` to reset
- it to the default value.
-
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
+ The default value of this property is an expression that evaluates to the float
+ `1`. Set this property to `nil` to reset it to the default value.
+
+ You can set this property to an expression containing any of the following:
+
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *fillOpacity;
+@property (nonatomic, null_resettable) NSExpression *fillOpacity;
/**
The transition affecting any changes to this layer’s `fillOpacity` property.
@@ -188,57 +147,23 @@ MGL_EXPORT
*/
@property (nonatomic) MGLTransition fillOpacityTransition;
-#if TARGET_OS_IPHONE
/**
The outline color of the fill. Matches the value of `fillColor` if unspecified.
This property is only applied to the style if `fillPattern` is set to `nil`,
- and `fillAntialiased` is set to an `MGLStyleValue` object containing an
- `NSNumber` object containing `YES`. Otherwise, it is ignored.
-
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- */
-@property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *fillOutlineColor;
-#else
-/**
- The outline color of the fill. Matches the value of `fillColor` if unspecified.
+ and `fillAntialiased` is set to an expression that evaluates to `YES`.
+ Otherwise, it is ignored.
- This property is only applied to the style if `fillPattern` is set to `nil`,
- and `fillAntialiased` is set to an `MGLStyleValue` object containing an
- `NSNumber` object containing `YES`. Otherwise, it is ignored.
-
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
+ You can set this property to an expression containing any of the following:
+
+ * Constant `UIColor` values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *fillOutlineColor;
-#endif
+@property (nonatomic, null_resettable) NSExpression *fillOutlineColor;
/**
The transition affecting any changes to this layer’s `fillOutlineColor` property.
@@ -251,13 +176,19 @@ MGL_EXPORT
Name of image in sprite to use for drawing image fills. For seamless patterns,
image width and height must be a factor of two (2, 4, 8, ..., 512).
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
+
+ * Constant string values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Step functions applied to the `$zoomLevel` variable
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of
- `MGLInterpolationModeInterval`
+ This property does not support applying interpolation functions to the
+ `$zoomLevel` variable or applying interpolation or step functions to feature
+ attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSString *> *fillPattern;
+@property (nonatomic, null_resettable) NSExpression *fillPattern;
/**
The transition affecting any changes to this layer’s `fillPattern` property.
@@ -272,7 +203,7 @@ MGL_EXPORT
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an
+ The default value of this property is an expression that evaluates to an
`NSValue` object containing a `CGVector` struct set to 0 points rightward and 0
points downward. Set this property to `nil` to reset it to the default value.
@@ -280,21 +211,25 @@ MGL_EXPORT
href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-fill-translate"><code>fill-translate</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
+ * Constant `CGVector` values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
+
+ This property does not support applying interpolation or step functions to
+ feature attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *fillTranslation;
+@property (nonatomic, null_resettable) NSExpression *fillTranslation;
#else
/**
The geometry's offset.
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an
+ The default value of this property is an expression that evaluates to an
`NSValue` object containing a `CGVector` struct set to 0 points rightward and 0
points upward. Set this property to `nil` to reset it to the default value.
@@ -302,14 +237,18 @@ MGL_EXPORT
href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-fill-translate"><code>fill-translate</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
+
+ * Constant `CGVector` values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
+ This property does not support applying interpolation or step functions to
+ feature attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *fillTranslation;
+@property (nonatomic, null_resettable) NSExpression *fillTranslation;
#endif
/**
@@ -319,14 +258,13 @@ MGL_EXPORT
*/
@property (nonatomic) MGLTransition fillTranslationTransition;
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *fillTranslate __attribute__((unavailable("Use fillTranslation instead.")));
+@property (nonatomic, null_resettable) NSExpression *fillTranslate __attribute__((unavailable("Use fillTranslation instead.")));
/**
- Controls the translation reference point.
+ Controls the frame of reference for `fillTranslation`.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSValue` object containing `MGLFillTranslationAnchorMap`. Set this property to
- `nil` to reset it to the default value.
+ The default value of this property is an expression that evaluates to `map`.
+ Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `fillTranslation` is non-`nil`.
Otherwise, it is ignored.
@@ -335,15 +273,24 @@ MGL_EXPORT
href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-fill-translate-anchor"><code>fill-translate-anchor</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
+
+ * Constant `MGLFillTranslationAnchor` values
+ * Any of the following constant string values:
+ * `map`: The fill is translated relative to the map.
+ * `viewport`: The fill is translated relative to the viewport.
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Step functions applied to the `$zoomLevel` variable
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of
- `MGLInterpolationModeInterval`
+ This property does not support applying interpolation functions to the
+ `$zoomLevel` variable or applying interpolation or step functions to feature
+ attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *fillTranslationAnchor;
+@property (nonatomic, null_resettable) NSExpression *fillTranslationAnchor;
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *fillTranslateAnchor __attribute__((unavailable("Use fillTranslationAnchor instead.")));
+@property (nonatomic, null_resettable) NSExpression *fillTranslateAnchor __attribute__((unavailable("Use fillTranslationAnchor instead.")));
@end
diff --git a/platform/darwin/src/MGLFillStyleLayer.mm b/platform/darwin/src/MGLFillStyleLayer.mm
index 71b01a6661..ab28b414b5 100644
--- a/platform/darwin/src/MGLFillStyleLayer.mm
+++ b/platform/darwin/src/MGLFillStyleLayer.mm
@@ -77,45 +77,45 @@ namespace mbgl {
#pragma mark - Accessing the Paint Attributes
-- (void)setFillAntialiased:(MGLStyleValue<NSNumber *> *)fillAntialiased {
+- (void)setFillAntialiased:(NSExpression *)fillAntialiased {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue(fillAntialiased);
+ auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<bool>>(fillAntialiased);
self.rawLayer->setFillAntialias(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)isFillAntialiased {
+- (NSExpression *)isFillAntialiased {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getFillAntialias();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(self.rawLayer->getDefaultFillAntialias());
+ propertyValue = self.rawLayer->getDefaultFillAntialias();
}
- return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<bool, NSNumber *>().toExpression(propertyValue);
}
-- (void)setFillAntialias:(MGLStyleValue<NSNumber *> *)fillAntialias {
+- (void)setFillAntialias:(NSExpression *)fillAntialias {
}
-- (MGLStyleValue<NSNumber *> *)fillAntialias {
+- (NSExpression *)fillAntialias {
return self.isFillAntialiased;
}
-- (void)setFillColor:(MGLStyleValue<MGLColor *> *)fillColor {
+- (void)setFillColor:(NSExpression *)fillColor {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenPropertyValue(fillColor);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<mbgl::Color>>(fillColor);
self.rawLayer->setFillColor(mbglValue);
}
-- (MGLStyleValue<MGLColor *> *)fillColor {
+- (NSExpression *)fillColor {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getFillColor();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(self.rawLayer->getDefaultFillColor());
+ propertyValue = self.rawLayer->getDefaultFillColor();
}
- return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toExpression(propertyValue);
}
- (void)setFillColorTransition:(MGLTransition )transition {
@@ -136,21 +136,21 @@ namespace mbgl {
return transition;
}
-- (void)setFillOpacity:(MGLStyleValue<NSNumber *> *)fillOpacity {
+- (void)setFillOpacity:(NSExpression *)fillOpacity {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(fillOpacity);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(fillOpacity);
self.rawLayer->setFillOpacity(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)fillOpacity {
+- (NSExpression *)fillOpacity {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getFillOpacity();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultFillOpacity());
+ propertyValue = self.rawLayer->getDefaultFillOpacity();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
- (void)setFillOpacityTransition:(MGLTransition )transition {
@@ -171,21 +171,21 @@ namespace mbgl {
return transition;
}
-- (void)setFillOutlineColor:(MGLStyleValue<MGLColor *> *)fillOutlineColor {
+- (void)setFillOutlineColor:(NSExpression *)fillOutlineColor {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenPropertyValue(fillOutlineColor);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<mbgl::Color>>(fillOutlineColor);
self.rawLayer->setFillOutlineColor(mbglValue);
}
-- (MGLStyleValue<MGLColor *> *)fillOutlineColor {
+- (NSExpression *)fillOutlineColor {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getFillOutlineColor();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(self.rawLayer->getDefaultFillOutlineColor());
+ propertyValue = self.rawLayer->getDefaultFillOutlineColor();
}
- return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toExpression(propertyValue);
}
- (void)setFillOutlineColorTransition:(MGLTransition )transition {
@@ -206,21 +206,21 @@ namespace mbgl {
return transition;
}
-- (void)setFillPattern:(MGLStyleValue<NSString *> *)fillPattern {
+- (void)setFillPattern:(NSExpression *)fillPattern {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<std::string, NSString *>().toPropertyValue(fillPattern);
+ auto mbglValue = MGLStyleValueTransformer<std::string, NSString *>().toPropertyValue<mbgl::style::PropertyValue<std::string>>(fillPattern);
self.rawLayer->setFillPattern(mbglValue);
}
-- (MGLStyleValue<NSString *> *)fillPattern {
+- (NSExpression *)fillPattern {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getFillPattern();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<std::string, NSString *>().toStyleValue(self.rawLayer->getDefaultFillPattern());
+ propertyValue = self.rawLayer->getDefaultFillPattern();
}
- return MGLStyleValueTransformer<std::string, NSString *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
}
- (void)setFillPatternTransition:(MGLTransition )transition {
@@ -241,21 +241,21 @@ namespace mbgl {
return transition;
}
-- (void)setFillTranslation:(MGLStyleValue<NSValue *> *)fillTranslation {
+- (void)setFillTranslation:(NSExpression *)fillTranslation {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toInterpolatablePropertyValue(fillTranslation);
+ auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toPropertyValue<mbgl::style::PropertyValue<std::array<float, 2>>>(fillTranslation);
self.rawLayer->setFillTranslate(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)fillTranslation {
+- (NSExpression *)fillTranslation {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getFillTranslate();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toStyleValue(self.rawLayer->getDefaultFillTranslate());
+ propertyValue = self.rawLayer->getDefaultFillTranslate();
}
- return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toExpression(propertyValue);
}
- (void)setFillTranslationTransition:(MGLTransition )transition {
@@ -276,34 +276,34 @@ namespace mbgl {
return transition;
}
-- (void)setFillTranslate:(MGLStyleValue<NSValue *> *)fillTranslate {
+- (void)setFillTranslate:(NSExpression *)fillTranslate {
}
-- (MGLStyleValue<NSValue *> *)fillTranslate {
+- (NSExpression *)fillTranslate {
return self.fillTranslation;
}
-- (void)setFillTranslationAnchor:(MGLStyleValue<NSValue *> *)fillTranslationAnchor {
+- (void)setFillTranslationAnchor:(NSExpression *)fillTranslationAnchor {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLFillTranslationAnchor>().toEnumPropertyValue(fillTranslationAnchor);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLFillTranslationAnchor>().toPropertyValue<mbgl::style::PropertyValue<mbgl::style::TranslateAnchorType>>(fillTranslationAnchor);
self.rawLayer->setFillTranslateAnchor(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)fillTranslationAnchor {
+- (NSExpression *)fillTranslationAnchor {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getFillTranslateAnchor();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLFillTranslationAnchor>().toEnumStyleValue(self.rawLayer->getDefaultFillTranslateAnchor());
+ propertyValue = self.rawLayer->getDefaultFillTranslateAnchor();
}
- return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLFillTranslationAnchor>().toEnumStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLFillTranslationAnchor>().toExpression(propertyValue);
}
-- (void)setFillTranslateAnchor:(MGLStyleValue<NSValue *> *)fillTranslateAnchor {
+- (void)setFillTranslateAnchor:(NSExpression *)fillTranslateAnchor {
}
-- (MGLStyleValue<NSValue *> *)fillTranslateAnchor {
+- (NSExpression *)fillTranslateAnchor {
return self.fillTranslationAnchor;
}
diff --git a/platform/darwin/src/MGLForegroundStyleLayer.h b/platform/darwin/src/MGLForegroundStyleLayer.h
index bcd323fb99..7e05023ef1 100644
--- a/platform/darwin/src/MGLForegroundStyleLayer.h
+++ b/platform/darwin/src/MGLForegroundStyleLayer.h
@@ -11,10 +11,11 @@ NS_ASSUME_NONNULL_BEGIN
`MGLForegroundStyleLayer` is an abstract superclass for style layers whose
content is defined by an `MGLSource` object.
- Create instances of `MGLRasterStyleLayer` and the concrete subclasses of
- `MGLVectorStyleLayer` in order to use `MGLForegroundStyleLayer`'s methods.
- Do not create instances of `MGLForegroundStyleLayer` directly, and do not
- create your own subclasses of this class.
+ Create instances of `MGLRasterStyleLayer`, `MGLHillshadeStyleLayer`, and the
+ concrete subclasses of `MGLVectorStyleLayer` in order to use
+ `MGLForegroundStyleLayer`'s methods. Do not create instances of
+ `MGLForegroundStyleLayer` directly, and do not create your own subclasses of
+ this class.
*/
MGL_EXPORT
@interface MGLForegroundStyleLayer : MGLStyleLayer
diff --git a/platform/darwin/src/MGLHeatmapStyleLayer.h b/platform/darwin/src/MGLHeatmapStyleLayer.h
new file mode 100644
index 0000000000..35095fd52e
--- /dev/null
+++ b/platform/darwin/src/MGLHeatmapStyleLayer.h
@@ -0,0 +1,189 @@
+// This file is generated.
+// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
+
+#import "MGLFoundation.h"
+#import "MGLVectorStyleLayer.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ An `MGLHeatmapStyleLayer` is a style layer that renders a <a
+ href="https://en.wikipedia.org/wiki/Heat_map">heatmap</a>.
+
+ A heatmap visualizes the spatial distribution of a large, dense set of point
+ data, using color to avoid cluttering the map with individual points at low
+ zoom levels. The points are weighted by an attribute you specify. Use a heatmap
+ style layer in conjunction with point or point collection features in vector
+ tiles loaded by an `MGLVectorSource` object or `MGLPointAnnotation`,
+ `MGLPointFeature`, `MGLPointCollection`, or `MGLPointCollectionFeature`
+ instances in an `MGLShapeSource` object.
+
+ Consider accompanying a heatmap style layer with an `MGLCircleStyleLayer` or
+ `MGLSymbolStyleLayer` at high zoom levels. If you are unsure whether the point
+ data in an `MGLShapeSource` is dense enough to warrant a heatmap, you can
+ alternatively cluster the source using the `MGLShapeSourceOptionClustered`
+ option and render the data using an `MGLCircleStyleLayer` or
+ `MGLSymbolStyleLayer`.
+
+ You can access an existing heatmap style layer using the
+ `-[MGLStyle layerWithIdentifier:]` method if you know its identifier;
+ otherwise, find it using the `MGLStyle.layers` property. You can also create a
+ new heatmap style layer and add it to the style using a method such as
+ `-[MGLStyle addLayer:]`.
+
+ ### Example
+
+ ```swift
+ let layer = MGLHeatmapStyleLayer(identifier: "earthquake-heat", source: earthquakes)
+ layer.heatmapWeight = NSExpression(format: "FUNCTION(magnitude, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)",
+ [0: 0,
+ 6: 1])
+ layer.heatmapIntensity = NSExpression(format: "FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)",
+ [0: 1,
+ 9: 3])
+ mapView.style?.addLayer(layer)
+ ```
+ */
+MGL_EXPORT
+@interface MGLHeatmapStyleLayer : MGLVectorStyleLayer
+
+/**
+ Returns a heatmap style layer initialized with an identifier and source.
+
+ After initializing and configuring the style layer, add it to a map view’s
+ style using the `-[MGLStyle addLayer:]` or
+ `-[MGLStyle insertLayer:belowLayer:]` method.
+
+ @param identifier A string that uniquely identifies the source in the style to
+ which it is added.
+ @param source The source from which to obtain the data to style. If the source
+ has not yet been added to the current style, the behavior is undefined.
+ @return An initialized foreground style layer.
+ */
+- (instancetype)initWithIdentifier:(NSString *)identifier source:(MGLSource *)source;
+
+#pragma mark - Accessing the Paint Attributes
+
+/**
+ Defines the color of each point based on its density value in a heatmap. Should
+ be an expression that uses `$heatmapDensity` as input.
+
+ The default value of this property is an expression that evaluates to a rainbow
+ color scale from blue to red. Set this property to `nil` to reset it to the
+ default value.
+
+ You can set this property to an expression containing any of the following:
+
+ * Constant `UIColor` values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$heatmapDensity` variable
+
+ This property does not support applying interpolation or step functions to
+ feature attributes.
+ */
+@property (nonatomic, null_resettable) NSExpression *heatmapColor;
+
+/**
+ Similar to `heatmapWeight` but controls the intensity of the heatmap globally.
+ Primarily used for adjusting the heatmap based on zoom level.
+
+ The default value of this property is an expression that evaluates to the float
+ `1`. Set this property to `nil` to reset it to the default value.
+
+ You can set this property to an expression containing any of the following:
+
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
+
+ This property does not support applying interpolation or step functions to
+ feature attributes.
+ */
+@property (nonatomic, null_resettable) NSExpression *heatmapIntensity;
+
+/**
+ The transition affecting any changes to this layer’s `heatmapIntensity` property.
+
+ This property corresponds to the `heatmap-intensity-transition` property in the style JSON file format.
+*/
+@property (nonatomic) MGLTransition heatmapIntensityTransition;
+
+/**
+ The global opacity at which the heatmap layer will be drawn.
+
+ The default value of this property is an expression that evaluates to the float
+ `1`. Set this property to `nil` to reset it to the default value.
+
+ You can set this property to an expression containing any of the following:
+
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
+
+ This property does not support applying interpolation or step functions to
+ feature attributes.
+ */
+@property (nonatomic, null_resettable) NSExpression *heatmapOpacity;
+
+/**
+ The transition affecting any changes to this layer’s `heatmapOpacity` property.
+
+ This property corresponds to the `heatmap-opacity-transition` property in the style JSON file format.
+*/
+@property (nonatomic) MGLTransition heatmapOpacityTransition;
+
+/**
+ Radius of influence of one heatmap point in points. Increasing the value makes
+ the heatmap smoother, but less detailed.
+
+ This property is measured in points.
+
+ The default value of this property is an expression that evaluates to the float
+ `30`. Set this property to `nil` to reset it to the default value.
+
+ You can set this property to an expression containing any of the following:
+
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
+ */
+@property (nonatomic, null_resettable) NSExpression *heatmapRadius;
+
+/**
+ The transition affecting any changes to this layer’s `heatmapRadius` property.
+
+ This property corresponds to the `heatmap-radius-transition` property in the style JSON file format.
+*/
+@property (nonatomic) MGLTransition heatmapRadiusTransition;
+
+/**
+ A measure of how much an individual point contributes to the heatmap. A value
+ of 10 would be equivalent to having 10 points of weight 1 in the same spot.
+ Especially useful when combined with clustering.
+
+ The default value of this property is an expression that evaluates to the float
+ `1`. Set this property to `nil` to reset it to the default value.
+
+ You can set this property to an expression containing any of the following:
+
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
+ */
+@property (nonatomic, null_resettable) NSExpression *heatmapWeight;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLHeatmapStyleLayer.mm b/platform/darwin/src/MGLHeatmapStyleLayer.mm
new file mode 100644
index 0000000000..a394dbda3b
--- /dev/null
+++ b/platform/darwin/src/MGLHeatmapStyleLayer.mm
@@ -0,0 +1,210 @@
+// This file is generated.
+// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
+
+#import "MGLSource.h"
+#import "NSPredicate+MGLAdditions.h"
+#import "NSDate+MGLAdditions.h"
+#import "MGLStyleLayer_Private.h"
+#import "MGLStyleValue_Private.h"
+#import "MGLHeatmapStyleLayer.h"
+
+#include <mbgl/style/transition_options.hpp>
+#include <mbgl/style/layers/heatmap_layer.hpp>
+
+@interface MGLHeatmapStyleLayer ()
+
+@property (nonatomic, readonly) mbgl::style::HeatmapLayer *rawLayer;
+
+@end
+
+@implementation MGLHeatmapStyleLayer
+
+- (instancetype)initWithIdentifier:(NSString *)identifier source:(MGLSource *)source
+{
+ auto layer = std::make_unique<mbgl::style::HeatmapLayer>(identifier.UTF8String, source.identifier.UTF8String);
+ return self = [super initWithPendingLayer:std::move(layer)];
+}
+
+- (mbgl::style::HeatmapLayer *)rawLayer
+{
+ return (mbgl::style::HeatmapLayer *)super.rawLayer;
+}
+
+- (NSString *)sourceIdentifier
+{
+ MGLAssertStyleLayerIsValid();
+
+ return @(self.rawLayer->getSourceID().c_str());
+}
+
+- (NSString *)sourceLayerIdentifier
+{
+ MGLAssertStyleLayerIsValid();
+
+ auto layerID = self.rawLayer->getSourceLayer();
+ return layerID.empty() ? nil : @(layerID.c_str());
+}
+
+- (void)setSourceLayerIdentifier:(NSString *)sourceLayerIdentifier
+{
+ MGLAssertStyleLayerIsValid();
+
+ self.rawLayer->setSourceLayer(sourceLayerIdentifier.UTF8String ?: "");
+}
+
+- (void)setPredicate:(NSPredicate *)predicate
+{
+ MGLAssertStyleLayerIsValid();
+
+ self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::NullFilter());
+}
+
+- (NSPredicate *)predicate
+{
+ MGLAssertStyleLayerIsValid();
+
+ return [NSPredicate mgl_predicateWithFilter:self.rawLayer->getFilter()];
+}
+
+#pragma mark - Accessing the Paint Attributes
+
+- (void)setHeatmapColor:(NSExpression *)heatmapColor {
+ MGLAssertStyleLayerIsValid();
+
+ auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toPropertyValue<mbgl::style::HeatmapColorPropertyValue>(heatmapColor);
+ self.rawLayer->setHeatmapColor(mbglValue);
+}
+
+- (NSExpression *)heatmapColor {
+ MGLAssertStyleLayerIsValid();
+
+ auto propertyValue = self.rawLayer->getHeatmapColor();
+ if (propertyValue.isUndefined()) {
+ propertyValue = self.rawLayer->getDefaultHeatmapColor();
+ }
+ return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toExpression(propertyValue);
+}
+
+- (void)setHeatmapIntensity:(NSExpression *)heatmapIntensity {
+ MGLAssertStyleLayerIsValid();
+
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(heatmapIntensity);
+ self.rawLayer->setHeatmapIntensity(mbglValue);
+}
+
+- (NSExpression *)heatmapIntensity {
+ MGLAssertStyleLayerIsValid();
+
+ auto propertyValue = self.rawLayer->getHeatmapIntensity();
+ if (propertyValue.isUndefined()) {
+ propertyValue = self.rawLayer->getDefaultHeatmapIntensity();
+ }
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
+}
+
+- (void)setHeatmapIntensityTransition:(MGLTransition )transition {
+ MGLAssertStyleLayerIsValid();
+
+ mbgl::style::TransitionOptions options { { MGLDurationFromTimeInterval(transition.duration) }, { MGLDurationFromTimeInterval(transition.delay) } };
+ self.rawLayer->setHeatmapIntensityTransition(options);
+}
+
+- (MGLTransition)heatmapIntensityTransition {
+ MGLAssertStyleLayerIsValid();
+
+ mbgl::style::TransitionOptions transitionOptions = self.rawLayer->getHeatmapIntensityTransition();
+ MGLTransition transition;
+ transition.duration = MGLTimeIntervalFromDuration(transitionOptions.duration.value_or(mbgl::Duration::zero()));
+ transition.delay = MGLTimeIntervalFromDuration(transitionOptions.delay.value_or(mbgl::Duration::zero()));
+
+ return transition;
+}
+
+- (void)setHeatmapOpacity:(NSExpression *)heatmapOpacity {
+ MGLAssertStyleLayerIsValid();
+
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(heatmapOpacity);
+ self.rawLayer->setHeatmapOpacity(mbglValue);
+}
+
+- (NSExpression *)heatmapOpacity {
+ MGLAssertStyleLayerIsValid();
+
+ auto propertyValue = self.rawLayer->getHeatmapOpacity();
+ if (propertyValue.isUndefined()) {
+ propertyValue = self.rawLayer->getDefaultHeatmapOpacity();
+ }
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
+}
+
+- (void)setHeatmapOpacityTransition:(MGLTransition )transition {
+ MGLAssertStyleLayerIsValid();
+
+ mbgl::style::TransitionOptions options { { MGLDurationFromTimeInterval(transition.duration) }, { MGLDurationFromTimeInterval(transition.delay) } };
+ self.rawLayer->setHeatmapOpacityTransition(options);
+}
+
+- (MGLTransition)heatmapOpacityTransition {
+ MGLAssertStyleLayerIsValid();
+
+ mbgl::style::TransitionOptions transitionOptions = self.rawLayer->getHeatmapOpacityTransition();
+ MGLTransition transition;
+ transition.duration = MGLTimeIntervalFromDuration(transitionOptions.duration.value_or(mbgl::Duration::zero()));
+ transition.delay = MGLTimeIntervalFromDuration(transitionOptions.delay.value_or(mbgl::Duration::zero()));
+
+ return transition;
+}
+
+- (void)setHeatmapRadius:(NSExpression *)heatmapRadius {
+ MGLAssertStyleLayerIsValid();
+
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(heatmapRadius);
+ self.rawLayer->setHeatmapRadius(mbglValue);
+}
+
+- (NSExpression *)heatmapRadius {
+ MGLAssertStyleLayerIsValid();
+
+ auto propertyValue = self.rawLayer->getHeatmapRadius();
+ if (propertyValue.isUndefined()) {
+ propertyValue = self.rawLayer->getDefaultHeatmapRadius();
+ }
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
+}
+
+- (void)setHeatmapRadiusTransition:(MGLTransition )transition {
+ MGLAssertStyleLayerIsValid();
+
+ mbgl::style::TransitionOptions options { { MGLDurationFromTimeInterval(transition.duration) }, { MGLDurationFromTimeInterval(transition.delay) } };
+ self.rawLayer->setHeatmapRadiusTransition(options);
+}
+
+- (MGLTransition)heatmapRadiusTransition {
+ MGLAssertStyleLayerIsValid();
+
+ mbgl::style::TransitionOptions transitionOptions = self.rawLayer->getHeatmapRadiusTransition();
+ MGLTransition transition;
+ transition.duration = MGLTimeIntervalFromDuration(transitionOptions.duration.value_or(mbgl::Duration::zero()));
+ transition.delay = MGLTimeIntervalFromDuration(transitionOptions.delay.value_or(mbgl::Duration::zero()));
+
+ return transition;
+}
+
+- (void)setHeatmapWeight:(NSExpression *)heatmapWeight {
+ MGLAssertStyleLayerIsValid();
+
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(heatmapWeight);
+ self.rawLayer->setHeatmapWeight(mbglValue);
+}
+
+- (NSExpression *)heatmapWeight {
+ MGLAssertStyleLayerIsValid();
+
+ auto propertyValue = self.rawLayer->getHeatmapWeight();
+ if (propertyValue.isUndefined()) {
+ propertyValue = self.rawLayer->getDefaultHeatmapWeight();
+ }
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
+}
+
+@end
diff --git a/platform/darwin/src/MGLHillshadeStyleLayer.h b/platform/darwin/src/MGLHillshadeStyleLayer.h
new file mode 100644
index 0000000000..b2eeb59aba
--- /dev/null
+++ b/platform/darwin/src/MGLHillshadeStyleLayer.h
@@ -0,0 +1,257 @@
+// This file is generated.
+// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
+
+#import "MGLFoundation.h"
+#import "MGLForegroundStyleLayer.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ Direction of light source when map is rotated.
+
+ Values of this type are used in the `MGLHillshadeStyleLayer.hillshadeIlluminationAnchor`
+ property.
+ */
+typedef NS_ENUM(NSUInteger, MGLHillshadeIlluminationAnchor) {
+ /**
+ The hillshade illumination is relative to the north direction.
+ */
+ MGLHillshadeIlluminationAnchorMap,
+ /**
+ The hillshade illumination is relative to the top of the viewport.
+ */
+ MGLHillshadeIlluminationAnchorViewport,
+};
+
+/**
+ An `MGLHillshadeStyleLayer` is a style layer that renders raster <a
+ href="https://en.wikipedia.org/wiki/Digital_elevation_model">digital elevation
+ model</a> (DEM) tiles on the map.
+
+ Use a hillshade style layer to configure the color parameters of raster tiles
+ loaded by an `MGLRasterDEMSource` object. For example, you could use a
+ hillshade style layer to render <a
+ href="https://www.mapbox.com/help/access-elevation-data/#mapbox-terrain-rgb">Mapbox
+ Terrain-RGB</a> data.
+
+ To display posterized hillshading based on vector shapes, as with the <a
+ href="https://www.mapbox.com/vector-tiles/mapbox-terrain/">Mapbox Terrain</a>
+ source, use an `MGLVectorSource` object in conjunction with several
+ `MGLFillStyleLayer` objects.
+
+ You can access an existing hillshade style layer using the
+ `-[MGLStyle layerWithIdentifier:]` method if you know its identifier;
+ otherwise, find it using the `MGLStyle.layers` property. You can also create a
+ new hillshade style layer and add it to the style using a method such as
+ `-[MGLStyle addLayer:]`.
+
+ ### Example
+
+ ```swift
+ let layer = MGLHillshadeStyleLayer(identifier: "hills", source: source)
+ layer.hillshadeExaggeration = NSExpression(forConstantValue: 0.6)
+ if let canalShadowLayer = mapView.style?.layer(withIdentifier: "waterway-river-canal-shadow") {
+ mapView.style?.insertLayer(layer, below: canalShadowLayer)
+ }
+ ```
+ */
+MGL_EXPORT
+@interface MGLHillshadeStyleLayer : MGLForegroundStyleLayer
+
+/**
+ Returns a hillshade style layer initialized with an identifier and source.
+
+ After initializing and configuring the style layer, add it to a map view’s
+ style using the `-[MGLStyle addLayer:]` or
+ `-[MGLStyle insertLayer:belowLayer:]` method.
+
+ @param identifier A string that uniquely identifies the source in the style to
+ which it is added.
+ @param source The source from which to obtain the data to style. If the source
+ has not yet been added to the current style, the behavior is undefined.
+ @return An initialized foreground style layer.
+ */
+- (instancetype)initWithIdentifier:(NSString *)identifier source:(MGLSource *)source;
+
+#pragma mark - Accessing the Paint Attributes
+
+/**
+ The shading color used to accentuate rugged terrain like sharp cliffs and
+ gorges.
+
+ The default value of this property is an expression that evaluates to
+ `UIColor.blackColor`. Set this property to `nil` to reset it to the default
+ value.
+
+ You can set this property to an expression containing any of the following:
+
+ * Constant `UIColor` values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
+
+ This property does not support applying interpolation or step functions to
+ feature attributes.
+ */
+@property (nonatomic, null_resettable) NSExpression *hillshadeAccentColor;
+
+/**
+ The transition affecting any changes to this layer’s `hillshadeAccentColor` property.
+
+ This property corresponds to the `hillshade-accent-color-transition` property in the style JSON file format.
+*/
+@property (nonatomic) MGLTransition hillshadeAccentColorTransition;
+
+/**
+ Intensity of the hillshade
+
+ The default value of this property is an expression that evaluates to the float
+ `0.5`. Set this property to `nil` to reset it to the default value.
+
+ You can set this property to an expression containing any of the following:
+
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
+
+ This property does not support applying interpolation or step functions to
+ feature attributes.
+ */
+@property (nonatomic, null_resettable) NSExpression *hillshadeExaggeration;
+
+/**
+ The transition affecting any changes to this layer’s `hillshadeExaggeration` property.
+
+ This property corresponds to the `hillshade-exaggeration-transition` property in the style JSON file format.
+*/
+@property (nonatomic) MGLTransition hillshadeExaggerationTransition;
+
+/**
+ The shading color of areas that faces towards the light source.
+
+ The default value of this property is an expression that evaluates to
+ `UIColor.whiteColor`. Set this property to `nil` to reset it to the default
+ value.
+
+ You can set this property to an expression containing any of the following:
+
+ * Constant `UIColor` values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
+
+ This property does not support applying interpolation or step functions to
+ feature attributes.
+ */
+@property (nonatomic, null_resettable) NSExpression *hillshadeHighlightColor;
+
+/**
+ The transition affecting any changes to this layer’s `hillshadeHighlightColor` property.
+
+ This property corresponds to the `hillshade-highlight-color-transition` property in the style JSON file format.
+*/
+@property (nonatomic) MGLTransition hillshadeHighlightColorTransition;
+
+/**
+ Direction of light source when map is rotated.
+
+ The default value of this property is an expression that evaluates to
+ `viewport`. Set this property to `nil` to reset it to the default value.
+
+ You can set this property to an expression containing any of the following:
+
+ * Constant `MGLHillshadeIlluminationAnchor` values
+ * Any of the following constant string values:
+ * `map`: The hillshade illumination is relative to the north direction.
+ * `viewport`: The hillshade illumination is relative to the top of the
+ viewport.
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Step functions applied to the `$zoomLevel` variable
+
+ This property does not support applying interpolation functions to the
+ `$zoomLevel` variable or applying interpolation or step functions to feature
+ attributes.
+ */
+@property (nonatomic, null_resettable) NSExpression *hillshadeIlluminationAnchor;
+
+/**
+ The direction of the light source used to generate the hillshading with 0 as
+ the top of the viewport if `hillshadeIlluminationAnchor` is set to
+ `MGLHillshadeIlluminationAnchorViewport` and due north if
+ `hillshadeIlluminationAnchor` is set to `MGLHillshadeIlluminationAnchorMap`.
+
+ The default value of this property is an expression that evaluates to the float
+ `335`. Set this property to `nil` to reset it to the default value.
+
+ You can set this property to an expression containing any of the following:
+
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
+
+ This property does not support applying interpolation or step functions to
+ feature attributes.
+ */
+@property (nonatomic, null_resettable) NSExpression *hillshadeIlluminationDirection;
+
+/**
+ The shading color of areas that face away from the light source.
+
+ The default value of this property is an expression that evaluates to
+ `UIColor.blackColor`. Set this property to `nil` to reset it to the default
+ value.
+
+ You can set this property to an expression containing any of the following:
+
+ * Constant `UIColor` values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
+
+ This property does not support applying interpolation or step functions to
+ feature attributes.
+ */
+@property (nonatomic, null_resettable) NSExpression *hillshadeShadowColor;
+
+/**
+ The transition affecting any changes to this layer’s `hillshadeShadowColor` property.
+
+ This property corresponds to the `hillshade-shadow-color-transition` property in the style JSON file format.
+*/
+@property (nonatomic) MGLTransition hillshadeShadowColorTransition;
+
+@end
+
+/**
+ Methods for wrapping an enumeration value for a style layer attribute in an
+ `MGLHillshadeStyleLayer` object and unwrapping its raw value.
+ */
+@interface NSValue (MGLHillshadeStyleLayerAdditions)
+
+#pragma mark Working with Hillshade Style Layer Attribute Values
+
+/**
+ Creates a new value object containing the given `MGLHillshadeIlluminationAnchor` enumeration.
+
+ @param hillshadeIlluminationAnchor The value for the new object.
+ @return A new value object that contains the enumeration value.
+ */
++ (instancetype)valueWithMGLHillshadeIlluminationAnchor:(MGLHillshadeIlluminationAnchor)hillshadeIlluminationAnchor;
+
+/**
+ The `MGLHillshadeIlluminationAnchor` enumeration representation of the value.
+ */
+@property (readonly) MGLHillshadeIlluminationAnchor MGLHillshadeIlluminationAnchorValue;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLHillshadeStyleLayer.mm b/platform/darwin/src/MGLHillshadeStyleLayer.mm
new file mode 100644
index 0000000000..2383c1ce26
--- /dev/null
+++ b/platform/darwin/src/MGLHillshadeStyleLayer.mm
@@ -0,0 +1,239 @@
+// This file is generated.
+// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
+
+#import "MGLSource.h"
+#import "NSPredicate+MGLAdditions.h"
+#import "NSDate+MGLAdditions.h"
+#import "MGLStyleLayer_Private.h"
+#import "MGLStyleValue_Private.h"
+#import "MGLHillshadeStyleLayer.h"
+
+#include <mbgl/style/transition_options.hpp>
+#include <mbgl/style/layers/hillshade_layer.hpp>
+
+namespace mbgl {
+
+ MBGL_DEFINE_ENUM(MGLHillshadeIlluminationAnchor, {
+ { MGLHillshadeIlluminationAnchorMap, "map" },
+ { MGLHillshadeIlluminationAnchorViewport, "viewport" },
+ });
+
+}
+
+@interface MGLHillshadeStyleLayer ()
+
+@property (nonatomic, readonly) mbgl::style::HillshadeLayer *rawLayer;
+
+@end
+
+@implementation MGLHillshadeStyleLayer
+
+- (instancetype)initWithIdentifier:(NSString *)identifier source:(MGLSource *)source
+{
+ auto layer = std::make_unique<mbgl::style::HillshadeLayer>(identifier.UTF8String, source.identifier.UTF8String);
+ return self = [super initWithPendingLayer:std::move(layer)];
+}
+
+- (mbgl::style::HillshadeLayer *)rawLayer
+{
+ return (mbgl::style::HillshadeLayer *)super.rawLayer;
+}
+
+- (NSString *)sourceIdentifier
+{
+ MGLAssertStyleLayerIsValid();
+
+ return @(self.rawLayer->getSourceID().c_str());
+}
+
+#pragma mark - Accessing the Paint Attributes
+
+- (void)setHillshadeAccentColor:(NSExpression *)hillshadeAccentColor {
+ MGLAssertStyleLayerIsValid();
+
+ auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toPropertyValue<mbgl::style::PropertyValue<mbgl::Color>>(hillshadeAccentColor);
+ self.rawLayer->setHillshadeAccentColor(mbglValue);
+}
+
+- (NSExpression *)hillshadeAccentColor {
+ MGLAssertStyleLayerIsValid();
+
+ auto propertyValue = self.rawLayer->getHillshadeAccentColor();
+ if (propertyValue.isUndefined()) {
+ propertyValue = self.rawLayer->getDefaultHillshadeAccentColor();
+ }
+ return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toExpression(propertyValue);
+}
+
+- (void)setHillshadeAccentColorTransition:(MGLTransition )transition {
+ MGLAssertStyleLayerIsValid();
+
+ mbgl::style::TransitionOptions options { { MGLDurationFromTimeInterval(transition.duration) }, { MGLDurationFromTimeInterval(transition.delay) } };
+ self.rawLayer->setHillshadeAccentColorTransition(options);
+}
+
+- (MGLTransition)hillshadeAccentColorTransition {
+ MGLAssertStyleLayerIsValid();
+
+ mbgl::style::TransitionOptions transitionOptions = self.rawLayer->getHillshadeAccentColorTransition();
+ MGLTransition transition;
+ transition.duration = MGLTimeIntervalFromDuration(transitionOptions.duration.value_or(mbgl::Duration::zero()));
+ transition.delay = MGLTimeIntervalFromDuration(transitionOptions.delay.value_or(mbgl::Duration::zero()));
+
+ return transition;
+}
+
+- (void)setHillshadeExaggeration:(NSExpression *)hillshadeExaggeration {
+ MGLAssertStyleLayerIsValid();
+
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(hillshadeExaggeration);
+ self.rawLayer->setHillshadeExaggeration(mbglValue);
+}
+
+- (NSExpression *)hillshadeExaggeration {
+ MGLAssertStyleLayerIsValid();
+
+ auto propertyValue = self.rawLayer->getHillshadeExaggeration();
+ if (propertyValue.isUndefined()) {
+ propertyValue = self.rawLayer->getDefaultHillshadeExaggeration();
+ }
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
+}
+
+- (void)setHillshadeExaggerationTransition:(MGLTransition )transition {
+ MGLAssertStyleLayerIsValid();
+
+ mbgl::style::TransitionOptions options { { MGLDurationFromTimeInterval(transition.duration) }, { MGLDurationFromTimeInterval(transition.delay) } };
+ self.rawLayer->setHillshadeExaggerationTransition(options);
+}
+
+- (MGLTransition)hillshadeExaggerationTransition {
+ MGLAssertStyleLayerIsValid();
+
+ mbgl::style::TransitionOptions transitionOptions = self.rawLayer->getHillshadeExaggerationTransition();
+ MGLTransition transition;
+ transition.duration = MGLTimeIntervalFromDuration(transitionOptions.duration.value_or(mbgl::Duration::zero()));
+ transition.delay = MGLTimeIntervalFromDuration(transitionOptions.delay.value_or(mbgl::Duration::zero()));
+
+ return transition;
+}
+
+- (void)setHillshadeHighlightColor:(NSExpression *)hillshadeHighlightColor {
+ MGLAssertStyleLayerIsValid();
+
+ auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toPropertyValue<mbgl::style::PropertyValue<mbgl::Color>>(hillshadeHighlightColor);
+ self.rawLayer->setHillshadeHighlightColor(mbglValue);
+}
+
+- (NSExpression *)hillshadeHighlightColor {
+ MGLAssertStyleLayerIsValid();
+
+ auto propertyValue = self.rawLayer->getHillshadeHighlightColor();
+ if (propertyValue.isUndefined()) {
+ propertyValue = self.rawLayer->getDefaultHillshadeHighlightColor();
+ }
+ return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toExpression(propertyValue);
+}
+
+- (void)setHillshadeHighlightColorTransition:(MGLTransition )transition {
+ MGLAssertStyleLayerIsValid();
+
+ mbgl::style::TransitionOptions options { { MGLDurationFromTimeInterval(transition.duration) }, { MGLDurationFromTimeInterval(transition.delay) } };
+ self.rawLayer->setHillshadeHighlightColorTransition(options);
+}
+
+- (MGLTransition)hillshadeHighlightColorTransition {
+ MGLAssertStyleLayerIsValid();
+
+ mbgl::style::TransitionOptions transitionOptions = self.rawLayer->getHillshadeHighlightColorTransition();
+ MGLTransition transition;
+ transition.duration = MGLTimeIntervalFromDuration(transitionOptions.duration.value_or(mbgl::Duration::zero()));
+ transition.delay = MGLTimeIntervalFromDuration(transitionOptions.delay.value_or(mbgl::Duration::zero()));
+
+ return transition;
+}
+
+- (void)setHillshadeIlluminationAnchor:(NSExpression *)hillshadeIlluminationAnchor {
+ MGLAssertStyleLayerIsValid();
+
+ auto mbglValue = MGLStyleValueTransformer<mbgl::style::HillshadeIlluminationAnchorType, NSValue *, mbgl::style::HillshadeIlluminationAnchorType, MGLHillshadeIlluminationAnchor>().toPropertyValue<mbgl::style::PropertyValue<mbgl::style::HillshadeIlluminationAnchorType>>(hillshadeIlluminationAnchor);
+ self.rawLayer->setHillshadeIlluminationAnchor(mbglValue);
+}
+
+- (NSExpression *)hillshadeIlluminationAnchor {
+ MGLAssertStyleLayerIsValid();
+
+ auto propertyValue = self.rawLayer->getHillshadeIlluminationAnchor();
+ if (propertyValue.isUndefined()) {
+ propertyValue = self.rawLayer->getDefaultHillshadeIlluminationAnchor();
+ }
+ return MGLStyleValueTransformer<mbgl::style::HillshadeIlluminationAnchorType, NSValue *, mbgl::style::HillshadeIlluminationAnchorType, MGLHillshadeIlluminationAnchor>().toExpression(propertyValue);
+}
+
+- (void)setHillshadeIlluminationDirection:(NSExpression *)hillshadeIlluminationDirection {
+ MGLAssertStyleLayerIsValid();
+
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(hillshadeIlluminationDirection);
+ self.rawLayer->setHillshadeIlluminationDirection(mbglValue);
+}
+
+- (NSExpression *)hillshadeIlluminationDirection {
+ MGLAssertStyleLayerIsValid();
+
+ auto propertyValue = self.rawLayer->getHillshadeIlluminationDirection();
+ if (propertyValue.isUndefined()) {
+ propertyValue = self.rawLayer->getDefaultHillshadeIlluminationDirection();
+ }
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
+}
+
+- (void)setHillshadeShadowColor:(NSExpression *)hillshadeShadowColor {
+ MGLAssertStyleLayerIsValid();
+
+ auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toPropertyValue<mbgl::style::PropertyValue<mbgl::Color>>(hillshadeShadowColor);
+ self.rawLayer->setHillshadeShadowColor(mbglValue);
+}
+
+- (NSExpression *)hillshadeShadowColor {
+ MGLAssertStyleLayerIsValid();
+
+ auto propertyValue = self.rawLayer->getHillshadeShadowColor();
+ if (propertyValue.isUndefined()) {
+ propertyValue = self.rawLayer->getDefaultHillshadeShadowColor();
+ }
+ return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toExpression(propertyValue);
+}
+
+- (void)setHillshadeShadowColorTransition:(MGLTransition )transition {
+ MGLAssertStyleLayerIsValid();
+
+ mbgl::style::TransitionOptions options { { MGLDurationFromTimeInterval(transition.duration) }, { MGLDurationFromTimeInterval(transition.delay) } };
+ self.rawLayer->setHillshadeShadowColorTransition(options);
+}
+
+- (MGLTransition)hillshadeShadowColorTransition {
+ MGLAssertStyleLayerIsValid();
+
+ mbgl::style::TransitionOptions transitionOptions = self.rawLayer->getHillshadeShadowColorTransition();
+ MGLTransition transition;
+ transition.duration = MGLTimeIntervalFromDuration(transitionOptions.duration.value_or(mbgl::Duration::zero()));
+ transition.delay = MGLTimeIntervalFromDuration(transitionOptions.delay.value_or(mbgl::Duration::zero()));
+
+ return transition;
+}
+
+@end
+
+@implementation NSValue (MGLHillshadeStyleLayerAdditions)
+
++ (NSValue *)valueWithMGLHillshadeIlluminationAnchor:(MGLHillshadeIlluminationAnchor)hillshadeIlluminationAnchor {
+ return [NSValue value:&hillshadeIlluminationAnchor withObjCType:@encode(MGLHillshadeIlluminationAnchor)];
+}
+
+- (MGLHillshadeIlluminationAnchor)MGLHillshadeIlluminationAnchorValue {
+ MGLHillshadeIlluminationAnchor hillshadeIlluminationAnchor;
+ [self getValue:&hillshadeIlluminationAnchor];
+ return hillshadeIlluminationAnchor;
+}
+
+@end
diff --git a/platform/darwin/src/MGLLight.h b/platform/darwin/src/MGLLight.h
index 55b789f043..d7e64cbfad 100644
--- a/platform/darwin/src/MGLLight.h
+++ b/platform/darwin/src/MGLLight.h
@@ -27,7 +27,7 @@ typedef NS_ENUM(NSUInteger, MGLLightAnchor) {
A structure containing information about the position of the light source
relative to lit geometries.
*/
-typedef struct MGLSphericalPosition {
+typedef struct __attribute__((objc_boxable)) MGLSphericalPosition {
/** Distance from the center of the base of an object to its light. */
CGFloat radial;
/** Position of the light relative to 0° (0° when `MGLLight.anchor` is set to viewport corresponds
@@ -65,20 +65,31 @@ MGL_EXPORT
/**
Whether extruded geometries are lit relative to the map or viewport.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSValue` object containing `MGLLightAnchorViewport`.
+ The default value of this property is an expression that evaluates to
+ `viewport`.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of
- `MGLInterpolationModeInterval`
+ * Constant `MGLAnchor` values
+ * Any of the following constant string values:
+ * `map`: The position of the light source is aligned to the rotation of the
+ map.
+ * `viewport`: The position of the light source is aligned to the rotation of
+ the viewport.
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Step functions applied to the `$zoomLevel` variable
+
+ This property does not support applying interpolation functions to the
+ `$zoomLevel` variable or applying interpolation or step functions to feature
+ attributes.
This property corresponds to the <a
href="https://www.mapbox.com/mapbox-gl-js/style-spec/#light-anchor"><code>anchor</code></a>
light property in the Mapbox Style Specification.
*/
-@property (nonatomic) MGLStyleValue<NSValue *> *anchor;
+@property (nonatomic) NSExpression *anchor;
/**
Position of the `MGLLight` source relative to lit (extruded) geometries, in a
@@ -90,21 +101,25 @@ MGL_EXPORT
corresponds to due north, and degrees proceed clockwise), and polar indicates
the height of the light (from 0°, directly above, to 180°, directly below).
- The default value of this property is an `MGLStyleValue` object containing an
+ The default value of this property is an expression that evaluates to an
`MGLSphericalPosition` struct set to 1.15 radial, 210 azimuthal and 30 polar.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
+
+ * Constant `MGLSphericalPosition` values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
+ This property does not support applying interpolation or step functions to
+ feature attributes.
This property corresponds to the <a
href="https://www.mapbox.com/mapbox-gl-js/style-spec/#light-position"><code>position</code></a>
light property in the Mapbox Style Specification.
*/
-@property (nonatomic) MGLStyleValue<NSValue *> *position;
+@property (nonatomic) NSExpression *position;
/**
The transition affecting any changes to this layer’s `position` property.
@@ -113,45 +128,28 @@ MGL_EXPORT
*/
@property (nonatomic) MGLTransition positionTransition;
-#if TARGET_OS_IPHONE
/**
Color tint for lighting extruded geometries.
- The default value of this property is an `MGLStyleValue` object containing
+ The default value of this property is an expression that evaluates to
`UIColor.whiteColor`.
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
-
- This property corresponds to the <a
- href="https://www.mapbox.com/mapbox-gl-js/style-spec/#light-color"><code>color</code></a>
- light property in the Mapbox Style Specification.
- */
-@property (nonatomic) MGLStyleValue<UIColor *> *color;
-#else
-/**
- Color tint for lighting extruded geometries.
-
- The default value of this property is an `MGLStyleValue` object containing
- `NSColor.whiteColor`.
+ You can set this property to an expression containing any of the following:
- You can set this property to an instance of:
+ * Constant `UIColor` values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
+ This property does not support applying interpolation or step functions to
+ feature attributes.
This property corresponds to the <a
href="https://www.mapbox.com/mapbox-gl-js/style-spec/#light-color"><code>color</code></a>
light property in the Mapbox Style Specification.
*/
-@property (nonatomic) MGLStyleValue<NSColor *> *color;
-#endif
+@property (nonatomic) NSExpression *color;
/**
The transition affecting any changes to this layer’s `color` property.
@@ -164,21 +162,25 @@ MGL_EXPORT
Intensity of lighting (on a scale from 0 to 1). Higher numbers will present as
more extreme contrast.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `0.5`.
+ The default value of this property is an expression that evaluates to the float
+ `0.5`.
+
+ You can set this property to an expression containing any of the following:
- You can set this property to an instance of:
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
+ This property does not support applying interpolation or step functions to
+ feature attributes.
This property corresponds to the <a
href="https://www.mapbox.com/mapbox-gl-js/style-spec/#light-intensity"><code>intensity</code></a>
light property in the Mapbox Style Specification.
*/
-@property (nonatomic) MGLStyleValue<NSNumber *> *intensity;
+@property (nonatomic) NSExpression *intensity;
/**
The transition affecting any changes to this layer’s `intensity` property.
diff --git a/platform/darwin/src/MGLLight.h.ejs b/platform/darwin/src/MGLLight.h.ejs
index 26ecefc3af..56c3312107 100644
--- a/platform/darwin/src/MGLLight.h.ejs
+++ b/platform/darwin/src/MGLLight.h.ejs
@@ -33,7 +33,7 @@ typedef NS_ENUM(NSUInteger, MGLLight<%- camelize(property.name) %>) {
A structure containing information about the position of the light source
relative to lit geometries.
*/
-typedef struct MGLSphericalPosition {
+typedef struct __attribute__((objc_boxable)) MGLSphericalPosition {
/** Distance from the center of the base of an object to its light. */
CGFloat radial;
/** Position of the light relative to 0° (0° when `MGLLight.anchor` is set to viewport corresponds
@@ -77,7 +77,7 @@ MGL_EXPORT
href="https://www.mapbox.com/mapbox-gl-js/style-spec/#light-<%- originalPropertyName(property) %>"><code><%- originalPropertyName(property) %></code></a>
light property in the Mapbox Style Specification.
*/
-@property (nonatomic<% if (property.getter) { %>, getter=<%- objCGetter(property) -%><% } %>) MGLStyleValue<<%- propertyType(property, true) %>> *<%- camelizeWithLeadingLowercase(property.name) %>;
+@property (nonatomic<% if (property.getter) { %>, getter=<%- objCGetter(property) -%><% } %>) NSExpression *<%- camelizeWithLeadingLowercase(property.name) %>;
<% if (property.transition) { -%>
/**
@@ -89,7 +89,7 @@ MGL_EXPORT
<% } -%>
<% if (property.original) { -%>
-@property (nonatomic<% if (!property.required) { %>, null_resettable<% } %>) MGLStyleValue<<%- propertyType(property, true) %>> *<%- camelizeWithLeadingLowercase(originalPropertyName(property)) %> __attribute__((unavailable("Use <%- camelizeWithLeadingLowercase(property.name) %> instead.")));
+@property (nonatomic<% if (!property.required) { %>, null_resettable<% } %>) NSExpression *<%- camelizeWithLeadingLowercase(originalPropertyName(property)) %> __attribute__((unavailable("Use <%- camelizeWithLeadingLowercase(property.name) %> instead.")));
<% } -%>
<% } -%>
diff --git a/platform/darwin/src/MGLLight.mm b/platform/darwin/src/MGLLight.mm
index c83ef127a6..db2893ed44 100644
--- a/platform/darwin/src/MGLLight.mm
+++ b/platform/darwin/src/MGLLight.mm
@@ -44,39 +44,39 @@ NS_INLINE mbgl::style::TransitionOptions MGLOptionsFromTransition(MGLTransition
{
if (self = [super init]) {
auto anchor = mbglLight->getAnchor();
- MGLStyleValue<NSValue *> *anchorStyleValue;
+ NSExpression *anchorExpression;
if (anchor.isUndefined()) {
mbgl::style::PropertyValue<mbgl::style::LightAnchorType> defaultAnchor = mbglLight->getDefaultAnchor();
- anchorStyleValue = MGLStyleValueTransformer<mbgl::style::LightAnchorType, NSValue *, mbgl::style::LightAnchorType, MGLLightAnchor>().toEnumStyleValue(defaultAnchor);
+ anchorExpression = MGLStyleValueTransformer<mbgl::style::LightAnchorType, NSValue *, mbgl::style::LightAnchorType, MGLLightAnchor>().toExpression(defaultAnchor);
} else {
- anchorStyleValue = MGLStyleValueTransformer<mbgl::style::LightAnchorType, NSValue *, mbgl::style::LightAnchorType, MGLLightAnchor>().toEnumStyleValue(anchor);
+ anchorExpression = MGLStyleValueTransformer<mbgl::style::LightAnchorType, NSValue *, mbgl::style::LightAnchorType, MGLLightAnchor>().toExpression(anchor);
}
- _anchor = anchorStyleValue;
+ _anchor = anchorExpression;
auto positionValue = mbglLight->getPosition();
if (positionValue.isUndefined()) {
- _position = MGLStyleValueTransformer<mbgl::style::Position, NSValue *>().toStyleValue(mbglLight->getDefaultPosition());
+ _position = MGLStyleValueTransformer<mbgl::style::Position, NSValue *>().toExpression(mbglLight->getDefaultPosition());
} else {
- _position = MGLStyleValueTransformer<mbgl::style::Position, NSValue *>().toStyleValue(positionValue);
+ _position = MGLStyleValueTransformer<mbgl::style::Position, NSValue *>().toExpression(positionValue);
}
_positionTransition = MGLTransitionFromOptions(mbglLight->getPositionTransition());
auto colorValue = mbglLight->getColor();
if (colorValue.isUndefined()) {
- _color = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toStyleValue(mbglLight->getDefaultColor());
+ _color = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toExpression(mbglLight->getDefaultColor());
} else {
- _color = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toStyleValue(colorValue);
+ _color = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toExpression(colorValue);
}
_colorTransition = MGLTransitionFromOptions(mbglLight->getColorTransition());
auto intensityValue = mbglLight->getIntensity();
if (intensityValue.isUndefined()) {
- _intensity = MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(mbglLight->getDefaultIntensity());
+ _intensity = MGLStyleValueTransformer<float, NSNumber *>().toExpression(mbglLight->getDefaultIntensity());
} else {
- _intensity = MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(intensityValue);
+ _intensity = MGLStyleValueTransformer<float, NSNumber *>().toExpression(intensityValue);
}
_intensityTransition = MGLTransitionFromOptions(mbglLight->getIntensityTransition());
@@ -89,20 +89,20 @@ NS_INLINE mbgl::style::TransitionOptions MGLOptionsFromTransition(MGLTransition
- (mbgl::style::Light)mbglLight
{
mbgl::style::Light mbglLight;
- auto anchor = MGLStyleValueTransformer<mbgl::style::LightAnchorType, NSValue *, mbgl::style::LightAnchorType, MGLLightAnchor>().toEnumPropertyValue(self.anchor);
+ auto anchor = MGLStyleValueTransformer<mbgl::style::LightAnchorType, NSValue *, mbgl::style::LightAnchorType, MGLLightAnchor>().toPropertyValue<mbgl::style::PropertyValue<mbgl::style::LightAnchorType>>(self.anchor);
mbglLight.setAnchor(anchor);
- auto position = MGLStyleValueTransformer<mbgl::style::Position, NSValue *>().toInterpolatablePropertyValue(self.position);
+ auto position = MGLStyleValueTransformer<mbgl::style::Position, NSValue *>().toPropertyValue<mbgl::style::PropertyValue<mbgl::style::Position>>(self.position);
mbglLight.setPosition(position);
mbglLight.setPositionTransition(MGLOptionsFromTransition(self.positionTransition));
- auto color = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toInterpolatablePropertyValue(self.color);
+ auto color = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toPropertyValue<mbgl::style::PropertyValue<mbgl::Color>>(self.color);
mbglLight.setColor(color);
mbglLight.setColorTransition(MGLOptionsFromTransition(self.colorTransition));
- auto intensity = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(self.intensity);
+ auto intensity = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(self.intensity);
mbglLight.setIntensity(intensity);
mbglLight.setIntensityTransition(MGLOptionsFromTransition(self.intensityTransition));
diff --git a/platform/darwin/src/MGLLight.mm.ejs b/platform/darwin/src/MGLLight.mm.ejs
index 0d0da124c8..b241269519 100644
--- a/platform/darwin/src/MGLLight.mm.ejs
+++ b/platform/darwin/src/MGLLight.mm.ejs
@@ -55,15 +55,15 @@ NS_INLINE mbgl::style::TransitionOptions MGLOptionsFromTransition(MGLTransition
<% for (const property of properties) { -%>
<% if (property.type == "enum") { -%>
auto <%- camelizeWithLeadingLowercase(property.name) -%> = mbglLight->get<%- camelize(property.name) -%>();
- MGLStyleValue<NSValue *> *<%- camelizeWithLeadingLowercase(property.name) -%>StyleValue;
+ NSExpression *<%- camelizeWithLeadingLowercase(property.name) -%>Expression;
if (<%- camelizeWithLeadingLowercase(property.name) -%>.isUndefined()) {
mbgl::style::PropertyValue<mbgl::style::Light<%- camelize(property.name) -%>Type> default<%- camelize(property.name) -%> = mbglLight->getDefault<%- camelize(property.name) -%>();
- <%- camelizeWithLeadingLowercase(property.name) -%>StyleValue = MGLStyleValueTransformer<mbgl::style::LightAnchorType, NSValue *, mbgl::style::Light<%- camelize(property.name) -%>Type, MGLLight<%- camelize(property.name) -%>>().toEnumStyleValue(default<%- camelize(property.name) -%>);
+ <%- camelizeWithLeadingLowercase(property.name) -%>Expression = MGLStyleValueTransformer<mbgl::style::LightAnchorType, NSValue *, mbgl::style::Light<%- camelize(property.name) -%>Type, MGLLight<%- camelize(property.name) -%>>().toExpression(default<%- camelize(property.name) -%>);
} else {
- <%- camelizeWithLeadingLowercase(property.name) -%>StyleValue = MGLStyleValueTransformer<mbgl::style::Light<%- camelize(property.name) -%>Type, NSValue *, mbgl::style::Light<%- camelize(property.name) -%>Type, MGLLight<%- camelize(property.name) -%>>().toEnumStyleValue(<%- camelizeWithLeadingLowercase(property.name) -%>);
+ <%- camelizeWithLeadingLowercase(property.name) -%>Expression = MGLStyleValueTransformer<mbgl::style::Light<%- camelize(property.name) -%>Type, NSValue *, mbgl::style::Light<%- camelize(property.name) -%>Type, MGLLight<%- camelize(property.name) -%>>().toExpression(<%- camelizeWithLeadingLowercase(property.name) -%>);
}
- _<%- camelizeWithLeadingLowercase(property.name) -%> = <%- camelizeWithLeadingLowercase(property.name) -%>StyleValue;
+ _<%- camelizeWithLeadingLowercase(property.name) -%> = <%- camelizeWithLeadingLowercase(property.name) -%>Expression;
<% if (property.transition) { -%>
_<%- camelizeWithLeadingLowercase(property.name) -%>Transition = MGLTransitionFromOptions(mbglLight->get<%- camelize(property.name) -%>Transition());
@@ -72,9 +72,9 @@ NS_INLINE mbgl::style::TransitionOptions MGLOptionsFromTransition(MGLTransition
<% } else {-%>
auto <%- camelizeWithLeadingLowercase(property.name) -%>Value = mbglLight->get<%- camelize(property.name) -%>();
if (<%- camelizeWithLeadingLowercase(property.name) -%>Value.isUndefined()) {
- _<%- camelizeWithLeadingLowercase(property.name) -%> = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toStyleValue(mbglLight->getDefault<%- camelize(property.name) -%>());
+ _<%- camelizeWithLeadingLowercase(property.name) -%> = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toExpression(mbglLight->getDefault<%- camelize(property.name) -%>());
} else {
- _<%- camelizeWithLeadingLowercase(property.name) -%> = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toStyleValue(<%- camelizeWithLeadingLowercase(property.name) -%>Value);
+ _<%- camelizeWithLeadingLowercase(property.name) -%> = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toExpression(<%- camelizeWithLeadingLowercase(property.name) -%>Value);
}
<% if (property.transition) { -%>
_<%- camelizeWithLeadingLowercase(property.name) -%>Transition = MGLTransitionFromOptions(mbglLight->get<%- camelize(property.name) -%>Transition());
@@ -93,11 +93,11 @@ NS_INLINE mbgl::style::TransitionOptions MGLOptionsFromTransition(MGLTransition
<% if (properties.length) { -%>
<% for (const property of properties) { -%>
<% if (property.type == "enum") { -%>
- auto <%- camelizeWithLeadingLowercase(property.name) -%> = MGLStyleValueTransformer<mbgl::style::Light<%- camelize(property.name) -%>Type, NSValue *, mbgl::style::Light<%- camelize(property.name) -%>Type, MGLLight<%- camelize(property.name) -%>>().toEnumPropertyValue(self.<%- camelizeWithLeadingLowercase(property.name) -%>);
+ auto <%- camelizeWithLeadingLowercase(property.name) -%> = MGLStyleValueTransformer<mbgl::style::Light<%- camelize(property.name) -%>Type, NSValue *, mbgl::style::Light<%- camelize(property.name) -%>Type, MGLLight<%- camelize(property.name) -%>>().toPropertyValue<mbgl::style::PropertyValue<<%- valueTransformerArguments(property)[0] %>>>(self.<%- camelizeWithLeadingLowercase(property.name) -%>);
mbglLight.set<%- camelize(property.name) -%>(<%- camelizeWithLeadingLowercase(property.name) -%>);
<% } else {-%>
- auto <%- camelizeWithLeadingLowercase(property.name) -%> = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toInterpolatablePropertyValue(self.<%- camelizeWithLeadingLowercase(property.name) -%>);
+ auto <%- camelizeWithLeadingLowercase(property.name) -%> = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toPropertyValue<mbgl::style::PropertyValue<<%- valueTransformerArguments(property)[0] %>>>(self.<%- camelizeWithLeadingLowercase(property.name) -%>);
mbglLight.set<%- camelize(property.name) -%>(<%- camelizeWithLeadingLowercase(property.name) -%>);
<% } -%>
diff --git a/platform/darwin/src/MGLLineStyleLayer.h b/platform/darwin/src/MGLLineStyleLayer.h
index 46025ddbf0..003f48ae43 100644
--- a/platform/darwin/src/MGLLineStyleLayer.h
+++ b/platform/darwin/src/MGLLineStyleLayer.h
@@ -2,7 +2,6 @@
// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
#import "MGLFoundation.h"
-#import "MGLStyleValue.h"
#import "MGLVectorStyleLayer.h"
NS_ASSUME_NONNULL_BEGIN
@@ -58,7 +57,7 @@ typedef NS_ENUM(NSUInteger, MGLLineJoin) {
};
/**
- Controls the translation reference point.
+ Controls the frame of reference for `MGLLineStyleLayer.lineTranslation`.
Values of this type are used in the `MGLLineStyleLayer.lineTranslationAnchor`
property.
@@ -94,12 +93,11 @@ typedef NS_ENUM(NSUInteger, MGLLineTranslationAnchor) {
```swift
let layer = MGLLineStyleLayer(identifier: "trails-path", source: trails)
layer.sourceLayerIdentifier = "trails"
- layer.lineWidth = MGLStyleValue(interpolationMode: .exponential,
- cameraStops: [14: MGLStyleValue(rawValue: 2),
- 18: MGLStyleValue(rawValue: 20)],
- options: [.interpolationBase: 1.5])
- layer.lineColor = MGLStyleValue(rawValue: .brown)
- layer.lineCap = MGLStyleValue(rawValue: NSValue(mglLineCap: .round))
+ layer.lineWidth = NSExpression(format: "FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'exponential', 1.5, %@)",
+ [14: 2,
+ 18: 20])
+ layer.lineColor = NSExpression(forConstantValue: UIColor.brown)
+ layer.lineCap = NSExpression(forConstantValue: "round")
layer.predicate = NSPredicate(format: "%K == %@", "trail-type", "mountain-biking")
mapView.style?.addLayer(layer)
```
@@ -127,82 +125,99 @@ MGL_EXPORT
/**
The display of line endings.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSValue` object containing `MGLLineCapButt`. Set this property to `nil` to
- reset it to the default value.
-
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of
- `MGLInterpolationModeInterval`
+ The default value of this property is an expression that evaluates to `butt`.
+ Set this property to `nil` to reset it to the default value.
+
+ You can set this property to an expression containing any of the following:
+
+ * Constant `MGLLineCap` values
+ * Any of the following constant string values:
+ * `butt`: A cap with a squared-off end which is drawn to the exact endpoint
+ of the line.
+ * `round`: A cap with a rounded end which is drawn beyond the endpoint of the
+ line at a radius of one-half of the line's width and centered on the endpoint
+ of the line.
+ * `square`: A cap with a squared-off end which is drawn beyond the endpoint
+ of the line at a distance of one-half of the line's width.
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Step functions applied to the `$zoomLevel` variable
+
+ This property does not support applying interpolation functions to the
+ `$zoomLevel` variable or applying interpolation or step functions to feature
+ attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *lineCap;
+@property (nonatomic, null_resettable) NSExpression *lineCap;
/**
The display of lines when joining.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSValue` object containing `MGLLineJoinMiter`. Set this property to `nil` to
- reset it to the default value.
-
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
+ The default value of this property is an expression that evaluates to `miter`.
+ Set this property to `nil` to reset it to the default value.
+
+ You can set this property to an expression containing any of the following:
+
+ * Constant `MGLLineJoin` values
+ * Any of the following constant string values:
+ * `bevel`: A join with a squared-off end which is drawn beyond the endpoint
+ of the line at a distance of one-half of the line's width.
+ * `round`: A join with a rounded end which is drawn beyond the endpoint of
+ the line at a radius of one-half of the line's width and centered on the
+ endpoint of the line.
+ * `miter`: A join with a sharp, angled corner which is drawn with the outer
+ sides beyond the endpoint of the path until they meet.
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *lineJoin;
+@property (nonatomic, null_resettable) NSExpression *lineJoin;
/**
Used to automatically convert miter joins to bevel joins for sharp angles.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `2`. Set this property to `nil` to reset
- it to the default value.
+ The default value of this property is an expression that evaluates to the float
+ `2`. Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `lineJoin` is set to an
- `MGLStyleValue` object containing an `NSValue` object containing
- `MGLLineJoinMiter`. Otherwise, it is ignored.
+ expression that evaluates to `miter`. Otherwise, it is ignored.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
+
+ This property does not support applying interpolation or step functions to
+ feature attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *lineMiterLimit;
+@property (nonatomic, null_resettable) NSExpression *lineMiterLimit;
/**
Used to automatically convert round joins to miter joins for shallow angles.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `1.05`. Set this property to `nil` to
- reset it to the default value.
+ The default value of this property is an expression that evaluates to the float
+ `1.05`. Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `lineJoin` is set to an
- `MGLStyleValue` object containing an `NSValue` object containing
- `MGLLineJoinRound`. Otherwise, it is ignored.
+ expression that evaluates to `round`. Otherwise, it is ignored.
+
+ You can set this property to an expression containing any of the following:
- You can set this property to an instance of:
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
+ This property does not support applying interpolation or step functions to
+ feature attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *lineRoundLimit;
+@property (nonatomic, null_resettable) NSExpression *lineRoundLimit;
#pragma mark - Accessing the Paint Attributes
@@ -211,27 +226,19 @@ MGL_EXPORT
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `0`. Set this property to `nil` to reset
- it to the default value.
-
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
+ The default value of this property is an expression that evaluates to the float
+ `0`. Set this property to `nil` to reset it to the default value.
+
+ You can set this property to an expression containing any of the following:
+
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *lineBlur;
+@property (nonatomic, null_resettable) NSExpression *lineBlur;
/**
The transition affecting any changes to this layer’s `lineBlur` property.
@@ -240,63 +247,26 @@ MGL_EXPORT
*/
@property (nonatomic) MGLTransition lineBlurTransition;
-#if TARGET_OS_IPHONE
/**
The color with which the line will be drawn.
- The default value of this property is an `MGLStyleValue` object containing
+ The default value of this property is an expression that evaluates to
`UIColor.blackColor`. Set this property to `nil` to reset it to the default
value.
This property is only applied to the style if `linePattern` is set to `nil`.
Otherwise, it is ignored.
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- */
-@property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *lineColor;
-#else
-/**
- The color with which the line will be drawn.
-
- The default value of this property is an `MGLStyleValue` object containing
- `NSColor.blackColor`. Set this property to `nil` to reset it to the default
- value.
-
- This property is only applied to the style if `linePattern` is set to `nil`.
- Otherwise, it is ignored.
+ You can set this property to an expression containing any of the following:
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
+ * Constant `UIColor` values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *lineColor;
-#endif
+@property (nonatomic, null_resettable) NSExpression *lineColor;
/**
The transition affecting any changes to this layer’s `lineColor` property.
@@ -319,13 +289,19 @@ MGL_EXPORT
href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-line-dasharray"><code>line-dasharray</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of
- `MGLInterpolationModeInterval`
+ * Constant array values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Step functions applied to the `$zoomLevel` variable
+
+ This property does not support applying interpolation functions to the
+ `$zoomLevel` variable or applying interpolation or step functions to feature
+ attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSArray<NSNumber *> *> *lineDashPattern;
+@property (nonatomic, null_resettable) NSExpression *lineDashPattern;
/**
The transition affecting any changes to this layer’s `lineDashPattern` property.
@@ -334,7 +310,7 @@ MGL_EXPORT
*/
@property (nonatomic) MGLTransition lineDashPatternTransition;
-@property (nonatomic, null_resettable) MGLStyleValue<NSArray<NSNumber *> *> *lineDasharray __attribute__((unavailable("Use lineDashPattern instead.")));
+@property (nonatomic, null_resettable) NSExpression *lineDasharray __attribute__((unavailable("Use lineDashPattern instead.")));
/**
Draws a line casing outside of a line's actual path. Value indicates the width
@@ -342,27 +318,19 @@ MGL_EXPORT
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `0`. Set this property to `nil` to reset
- it to the default value.
-
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
+ The default value of this property is an expression that evaluates to the float
+ `0`. Set this property to `nil` to reset it to the default value.
+
+ You can set this property to an expression containing any of the following:
+
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *lineGapWidth;
+@property (nonatomic, null_resettable) NSExpression *lineGapWidth;
/**
The transition affecting any changes to this layer’s `lineGapWidth` property.
@@ -379,27 +347,19 @@ MGL_EXPORT
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `0`. Set this property to `nil` to reset
- it to the default value.
-
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
+ The default value of this property is an expression that evaluates to the float
+ `0`. Set this property to `nil` to reset it to the default value.
+
+ You can set this property to an expression containing any of the following:
+
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *lineOffset;
+@property (nonatomic, null_resettable) NSExpression *lineOffset;
/**
The transition affecting any changes to this layer’s `lineOffset` property.
@@ -411,27 +371,19 @@ MGL_EXPORT
/**
The opacity at which the line will be drawn.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `1`. Set this property to `nil` to reset
- it to the default value.
-
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
+ The default value of this property is an expression that evaluates to the float
+ `1`. Set this property to `nil` to reset it to the default value.
+
+ You can set this property to an expression containing any of the following:
+
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *lineOpacity;
+@property (nonatomic, null_resettable) NSExpression *lineOpacity;
/**
The transition affecting any changes to this layer’s `lineOpacity` property.
@@ -444,13 +396,19 @@ MGL_EXPORT
Name of image in style images to use for drawing image lines. For seamless
patterns, image width must be a factor of two (2, 4, 8, ..., 512).
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
+
+ * Constant string values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Step functions applied to the `$zoomLevel` variable
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of
- `MGLInterpolationModeInterval`
+ This property does not support applying interpolation functions to the
+ `$zoomLevel` variable or applying interpolation or step functions to feature
+ attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSString *> *linePattern;
+@property (nonatomic, null_resettable) NSExpression *linePattern;
/**
The transition affecting any changes to this layer’s `linePattern` property.
@@ -465,7 +423,7 @@ MGL_EXPORT
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an
+ The default value of this property is an expression that evaluates to an
`NSValue` object containing a `CGVector` struct set to 0 points rightward and 0
points downward. Set this property to `nil` to reset it to the default value.
@@ -473,21 +431,25 @@ MGL_EXPORT
href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-line-translate"><code>line-translate</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
+ * Constant `CGVector` values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
+
+ This property does not support applying interpolation or step functions to
+ feature attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *lineTranslation;
+@property (nonatomic, null_resettable) NSExpression *lineTranslation;
#else
/**
The geometry's offset.
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an
+ The default value of this property is an expression that evaluates to an
`NSValue` object containing a `CGVector` struct set to 0 points rightward and 0
points upward. Set this property to `nil` to reset it to the default value.
@@ -495,14 +457,18 @@ MGL_EXPORT
href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-line-translate"><code>line-translate</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
+
+ * Constant `CGVector` values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
+ This property does not support applying interpolation or step functions to
+ feature attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *lineTranslation;
+@property (nonatomic, null_resettable) NSExpression *lineTranslation;
#endif
/**
@@ -512,14 +478,13 @@ MGL_EXPORT
*/
@property (nonatomic) MGLTransition lineTranslationTransition;
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *lineTranslate __attribute__((unavailable("Use lineTranslation instead.")));
+@property (nonatomic, null_resettable) NSExpression *lineTranslate __attribute__((unavailable("Use lineTranslation instead.")));
/**
- Controls the translation reference point.
+ Controls the frame of reference for `lineTranslation`.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSValue` object containing `MGLLineTranslationAnchorMap`. Set this property to
- `nil` to reset it to the default value.
+ The default value of this property is an expression that evaluates to `map`.
+ Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `lineTranslation` is non-`nil`.
Otherwise, it is ignored.
@@ -528,42 +493,43 @@ MGL_EXPORT
href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-line-translate-anchor"><code>line-translate-anchor</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of
- `MGLInterpolationModeInterval`
+ * Constant `MGLLineTranslationAnchor` values
+ * Any of the following constant string values:
+ * `map`: The line is translated relative to the map.
+ * `viewport`: The line is translated relative to the viewport.
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Step functions applied to the `$zoomLevel` variable
+
+ This property does not support applying interpolation functions to the
+ `$zoomLevel` variable or applying interpolation or step functions to feature
+ attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *lineTranslationAnchor;
+@property (nonatomic, null_resettable) NSExpression *lineTranslationAnchor;
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *lineTranslateAnchor __attribute__((unavailable("Use lineTranslationAnchor instead.")));
+@property (nonatomic, null_resettable) NSExpression *lineTranslateAnchor __attribute__((unavailable("Use lineTranslationAnchor instead.")));
/**
Stroke thickness.
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `1`. Set this property to `nil` to reset
- it to the default value.
-
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
+ The default value of this property is an expression that evaluates to the float
+ `1`. Set this property to `nil` to reset it to the default value.
+
+ You can set this property to an expression containing any of the following:
+
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *lineWidth;
+@property (nonatomic, null_resettable) NSExpression *lineWidth;
/**
The transition affecting any changes to this layer’s `lineWidth` property.
diff --git a/platform/darwin/src/MGLLineStyleLayer.mm b/platform/darwin/src/MGLLineStyleLayer.mm
index 5b2652cdeb..4e6ff27b39 100644
--- a/platform/darwin/src/MGLLineStyleLayer.mm
+++ b/platform/darwin/src/MGLLineStyleLayer.mm
@@ -89,91 +89,91 @@ namespace mbgl {
#pragma mark - Accessing the Layout Attributes
-- (void)setLineCap:(MGLStyleValue<NSValue *> *)lineCap {
+- (void)setLineCap:(NSExpression *)lineCap {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::style::LineCapType, NSValue *, mbgl::style::LineCapType, MGLLineCap>().toEnumPropertyValue(lineCap);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::style::LineCapType, NSValue *, mbgl::style::LineCapType, MGLLineCap>().toPropertyValue<mbgl::style::PropertyValue<mbgl::style::LineCapType>>(lineCap);
self.rawLayer->setLineCap(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)lineCap {
+- (NSExpression *)lineCap {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getLineCap();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<mbgl::style::LineCapType, NSValue *, mbgl::style::LineCapType, MGLLineCap>().toEnumStyleValue(self.rawLayer->getDefaultLineCap());
+ propertyValue = self.rawLayer->getDefaultLineCap();
}
- return MGLStyleValueTransformer<mbgl::style::LineCapType, NSValue *, mbgl::style::LineCapType, MGLLineCap>().toEnumStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::style::LineCapType, NSValue *, mbgl::style::LineCapType, MGLLineCap>().toExpression(propertyValue);
}
-- (void)setLineJoin:(MGLStyleValue<NSValue *> *)lineJoin {
+- (void)setLineJoin:(NSExpression *)lineJoin {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::style::LineJoinType, NSValue *, mbgl::style::LineJoinType, MGLLineJoin>().toDataDrivenPropertyValue(lineJoin);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::style::LineJoinType, NSValue *, mbgl::style::LineJoinType, MGLLineJoin>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<mbgl::style::LineJoinType>>(lineJoin);
self.rawLayer->setLineJoin(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)lineJoin {
+- (NSExpression *)lineJoin {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getLineJoin();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<mbgl::style::LineJoinType, NSValue *, mbgl::style::LineJoinType, MGLLineJoin>().toDataDrivenStyleValue(self.rawLayer->getDefaultLineJoin());
+ propertyValue = self.rawLayer->getDefaultLineJoin();
}
- return MGLStyleValueTransformer<mbgl::style::LineJoinType, NSValue *, mbgl::style::LineJoinType, MGLLineJoin>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::style::LineJoinType, NSValue *, mbgl::style::LineJoinType, MGLLineJoin>().toExpression(propertyValue);
}
-- (void)setLineMiterLimit:(MGLStyleValue<NSNumber *> *)lineMiterLimit {
+- (void)setLineMiterLimit:(NSExpression *)lineMiterLimit {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(lineMiterLimit);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(lineMiterLimit);
self.rawLayer->setLineMiterLimit(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)lineMiterLimit {
+- (NSExpression *)lineMiterLimit {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getLineMiterLimit();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultLineMiterLimit());
+ propertyValue = self.rawLayer->getDefaultLineMiterLimit();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
-- (void)setLineRoundLimit:(MGLStyleValue<NSNumber *> *)lineRoundLimit {
+- (void)setLineRoundLimit:(NSExpression *)lineRoundLimit {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(lineRoundLimit);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(lineRoundLimit);
self.rawLayer->setLineRoundLimit(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)lineRoundLimit {
+- (NSExpression *)lineRoundLimit {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getLineRoundLimit();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultLineRoundLimit());
+ propertyValue = self.rawLayer->getDefaultLineRoundLimit();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
#pragma mark - Accessing the Paint Attributes
-- (void)setLineBlur:(MGLStyleValue<NSNumber *> *)lineBlur {
+- (void)setLineBlur:(NSExpression *)lineBlur {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(lineBlur);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(lineBlur);
self.rawLayer->setLineBlur(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)lineBlur {
+- (NSExpression *)lineBlur {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getLineBlur();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultLineBlur());
+ propertyValue = self.rawLayer->getDefaultLineBlur();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
- (void)setLineBlurTransition:(MGLTransition )transition {
@@ -194,21 +194,21 @@ namespace mbgl {
return transition;
}
-- (void)setLineColor:(MGLStyleValue<MGLColor *> *)lineColor {
+- (void)setLineColor:(NSExpression *)lineColor {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenPropertyValue(lineColor);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<mbgl::Color>>(lineColor);
self.rawLayer->setLineColor(mbglValue);
}
-- (MGLStyleValue<MGLColor *> *)lineColor {
+- (NSExpression *)lineColor {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getLineColor();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(self.rawLayer->getDefaultLineColor());
+ propertyValue = self.rawLayer->getDefaultLineColor();
}
- return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toExpression(propertyValue);
}
- (void)setLineColorTransition:(MGLTransition )transition {
@@ -229,21 +229,21 @@ namespace mbgl {
return transition;
}
-- (void)setLineDashPattern:(MGLStyleValue<NSArray<NSNumber *> *> *)lineDashPattern {
+- (void)setLineDashPattern:(NSExpression *)lineDashPattern {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<std::vector<float>, NSArray<NSNumber *> *, float>().toPropertyValue(lineDashPattern);
+ auto mbglValue = MGLStyleValueTransformer<std::vector<float>, NSArray<NSNumber *> *, float>().toPropertyValue<mbgl::style::PropertyValue<std::vector<float>>>(lineDashPattern);
self.rawLayer->setLineDasharray(mbglValue);
}
-- (MGLStyleValue<NSArray<NSNumber *> *> *)lineDashPattern {
+- (NSExpression *)lineDashPattern {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getLineDasharray();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<std::vector<float>, NSArray<NSNumber *> *, float>().toStyleValue(self.rawLayer->getDefaultLineDasharray());
+ propertyValue = self.rawLayer->getDefaultLineDasharray();
}
- return MGLStyleValueTransformer<std::vector<float>, NSArray<NSNumber *> *, float>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<std::vector<float>, NSArray<NSNumber *> *, float>().toExpression(propertyValue);
}
- (void)setLineDashPatternTransition:(MGLTransition )transition {
@@ -264,28 +264,28 @@ namespace mbgl {
return transition;
}
-- (void)setLineDasharray:(MGLStyleValue<NSArray<NSNumber *> *> *)lineDasharray {
+- (void)setLineDasharray:(NSExpression *)lineDasharray {
}
-- (MGLStyleValue<NSArray<NSNumber *> *> *)lineDasharray {
+- (NSExpression *)lineDasharray {
return self.lineDashPattern;
}
-- (void)setLineGapWidth:(MGLStyleValue<NSNumber *> *)lineGapWidth {
+- (void)setLineGapWidth:(NSExpression *)lineGapWidth {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(lineGapWidth);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(lineGapWidth);
self.rawLayer->setLineGapWidth(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)lineGapWidth {
+- (NSExpression *)lineGapWidth {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getLineGapWidth();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultLineGapWidth());
+ propertyValue = self.rawLayer->getDefaultLineGapWidth();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
- (void)setLineGapWidthTransition:(MGLTransition )transition {
@@ -306,21 +306,21 @@ namespace mbgl {
return transition;
}
-- (void)setLineOffset:(MGLStyleValue<NSNumber *> *)lineOffset {
+- (void)setLineOffset:(NSExpression *)lineOffset {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(lineOffset);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(lineOffset);
self.rawLayer->setLineOffset(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)lineOffset {
+- (NSExpression *)lineOffset {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getLineOffset();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultLineOffset());
+ propertyValue = self.rawLayer->getDefaultLineOffset();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
- (void)setLineOffsetTransition:(MGLTransition )transition {
@@ -341,21 +341,21 @@ namespace mbgl {
return transition;
}
-- (void)setLineOpacity:(MGLStyleValue<NSNumber *> *)lineOpacity {
+- (void)setLineOpacity:(NSExpression *)lineOpacity {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(lineOpacity);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(lineOpacity);
self.rawLayer->setLineOpacity(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)lineOpacity {
+- (NSExpression *)lineOpacity {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getLineOpacity();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultLineOpacity());
+ propertyValue = self.rawLayer->getDefaultLineOpacity();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
- (void)setLineOpacityTransition:(MGLTransition )transition {
@@ -376,21 +376,21 @@ namespace mbgl {
return transition;
}
-- (void)setLinePattern:(MGLStyleValue<NSString *> *)linePattern {
+- (void)setLinePattern:(NSExpression *)linePattern {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<std::string, NSString *>().toPropertyValue(linePattern);
+ auto mbglValue = MGLStyleValueTransformer<std::string, NSString *>().toPropertyValue<mbgl::style::PropertyValue<std::string>>(linePattern);
self.rawLayer->setLinePattern(mbglValue);
}
-- (MGLStyleValue<NSString *> *)linePattern {
+- (NSExpression *)linePattern {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getLinePattern();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<std::string, NSString *>().toStyleValue(self.rawLayer->getDefaultLinePattern());
+ propertyValue = self.rawLayer->getDefaultLinePattern();
}
- return MGLStyleValueTransformer<std::string, NSString *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
}
- (void)setLinePatternTransition:(MGLTransition )transition {
@@ -411,21 +411,21 @@ namespace mbgl {
return transition;
}
-- (void)setLineTranslation:(MGLStyleValue<NSValue *> *)lineTranslation {
+- (void)setLineTranslation:(NSExpression *)lineTranslation {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toInterpolatablePropertyValue(lineTranslation);
+ auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toPropertyValue<mbgl::style::PropertyValue<std::array<float, 2>>>(lineTranslation);
self.rawLayer->setLineTranslate(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)lineTranslation {
+- (NSExpression *)lineTranslation {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getLineTranslate();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toStyleValue(self.rawLayer->getDefaultLineTranslate());
+ propertyValue = self.rawLayer->getDefaultLineTranslate();
}
- return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toExpression(propertyValue);
}
- (void)setLineTranslationTransition:(MGLTransition )transition {
@@ -446,52 +446,52 @@ namespace mbgl {
return transition;
}
-- (void)setLineTranslate:(MGLStyleValue<NSValue *> *)lineTranslate {
+- (void)setLineTranslate:(NSExpression *)lineTranslate {
}
-- (MGLStyleValue<NSValue *> *)lineTranslate {
+- (NSExpression *)lineTranslate {
return self.lineTranslation;
}
-- (void)setLineTranslationAnchor:(MGLStyleValue<NSValue *> *)lineTranslationAnchor {
+- (void)setLineTranslationAnchor:(NSExpression *)lineTranslationAnchor {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLLineTranslationAnchor>().toEnumPropertyValue(lineTranslationAnchor);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLLineTranslationAnchor>().toPropertyValue<mbgl::style::PropertyValue<mbgl::style::TranslateAnchorType>>(lineTranslationAnchor);
self.rawLayer->setLineTranslateAnchor(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)lineTranslationAnchor {
+- (NSExpression *)lineTranslationAnchor {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getLineTranslateAnchor();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLLineTranslationAnchor>().toEnumStyleValue(self.rawLayer->getDefaultLineTranslateAnchor());
+ propertyValue = self.rawLayer->getDefaultLineTranslateAnchor();
}
- return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLLineTranslationAnchor>().toEnumStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLLineTranslationAnchor>().toExpression(propertyValue);
}
-- (void)setLineTranslateAnchor:(MGLStyleValue<NSValue *> *)lineTranslateAnchor {
+- (void)setLineTranslateAnchor:(NSExpression *)lineTranslateAnchor {
}
-- (MGLStyleValue<NSValue *> *)lineTranslateAnchor {
+- (NSExpression *)lineTranslateAnchor {
return self.lineTranslationAnchor;
}
-- (void)setLineWidth:(MGLStyleValue<NSNumber *> *)lineWidth {
+- (void)setLineWidth:(NSExpression *)lineWidth {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(lineWidth);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(lineWidth);
self.rawLayer->setLineWidth(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)lineWidth {
+- (NSExpression *)lineWidth {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getLineWidth();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultLineWidth());
+ propertyValue = self.rawLayer->getDefaultLineWidth();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
- (void)setLineWidthTransition:(MGLTransition )transition {
diff --git a/platform/darwin/src/MGLMapSnapshotter.mm b/platform/darwin/src/MGLMapSnapshotter.mm
index db236a8aeb..11a5442761 100644
--- a/platform/darwin/src/MGLMapSnapshotter.mm
+++ b/platform/darwin/src/MGLMapSnapshotter.mm
@@ -343,6 +343,7 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
#else
image = [[NSImage alloc] initWithCGImage:cgimg size:[backgroundImage extent].size];
#endif
+
CGImageRelease(cgimg);
return image;
}
diff --git a/platform/darwin/src/MGLNetworkConfiguration.m b/platform/darwin/src/MGLNetworkConfiguration.m
index 82d333dc99..4cfa405a1a 100644
--- a/platform/darwin/src/MGLNetworkConfiguration.m
+++ b/platform/darwin/src/MGLNetworkConfiguration.m
@@ -13,7 +13,7 @@
+ (instancetype)sharedManager {
static dispatch_once_t onceToken;
static MGLNetworkConfiguration *_sharedManager;
- void (^setupBlock)() = ^{
+ void (^setupBlock)(void) = ^{
dispatch_once(&onceToken, ^{
_sharedManager = [[self alloc] init];
});
diff --git a/platform/darwin/src/MGLPolygon+MGLAdditions.h b/platform/darwin/src/MGLPolygon+MGLAdditions.h
deleted file mode 100644
index f409fb96ca..0000000000
--- a/platform/darwin/src/MGLPolygon+MGLAdditions.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#import <Mapbox/Mapbox.h>
-
-@interface MGLPolygon (MGLAdditions)
-
-- (NS_ARRAY_OF(id) *)mgl_coordinates;
-
-@end
diff --git a/platform/darwin/src/MGLPolygon+MGLAdditions.m b/platform/darwin/src/MGLPolygon+MGLAdditions.m
deleted file mode 100644
index 3e76a37157..0000000000
--- a/platform/darwin/src/MGLPolygon+MGLAdditions.m
+++ /dev/null
@@ -1,27 +0,0 @@
-#import "MGLPolygon+MGLAdditions.h"
-
-@implementation MGLPolygon (MGLAdditions)
-
-- (NS_ARRAY_OF(id) *)mgl_coordinates {
- NSMutableArray *coordinates = [NSMutableArray array];
-
- NSMutableArray *exteriorRing = [NSMutableArray array];
- for (NSUInteger index = 0; index < self.pointCount; index++) {
- CLLocationCoordinate2D coordinate = self.coordinates[index];
- [exteriorRing addObject:@[@(coordinate.longitude), @(coordinate.latitude)]];
- }
- [coordinates addObject:exteriorRing];
-
- for (MGLPolygon *interiorPolygon in self.interiorPolygons) {
- NSMutableArray *interiorRing = [NSMutableArray array];
- for (int index = 0; index < interiorPolygon.pointCount; index++) {
- CLLocationCoordinate2D coordinate = interiorPolygon.coordinates[index];
- [interiorRing addObject:@[@(coordinate.longitude), @(coordinate.latitude)]];
- }
- [coordinates addObject:interiorRing];
- }
-
- return [coordinates copy];
-}
-
-@end
diff --git a/platform/darwin/src/MGLPolygon.mm b/platform/darwin/src/MGLPolygon.mm
index e7843224e9..2af768d514 100644
--- a/platform/darwin/src/MGLPolygon.mm
+++ b/platform/darwin/src/MGLPolygon.mm
@@ -1,9 +1,9 @@
-#import "MGLPolygon.h"
+#import "MGLPolygon_Private.h"
#import "MGLMultiPoint_Private.h"
#import "MGLGeometry_Private.h"
-#import "MGLPolygon+MGLAdditions.h"
+#import "MGLFeature.h"
#import <mbgl/util/geojson.hpp>
#import <mapbox/polylabel.hpp>
@@ -102,6 +102,28 @@
@"coordinates": self.mgl_coordinates};
}
+- (NS_ARRAY_OF(id) *)mgl_coordinates {
+ NSMutableArray *coordinates = [NSMutableArray array];
+
+ NSMutableArray *exteriorRing = [NSMutableArray array];
+ for (NSUInteger index = 0; index < self.pointCount; index++) {
+ CLLocationCoordinate2D coordinate = self.coordinates[index];
+ [exteriorRing addObject:@[@(coordinate.longitude), @(coordinate.latitude)]];
+ }
+ [coordinates addObject:exteriorRing];
+
+ for (MGLPolygon *interiorPolygon in self.interiorPolygons) {
+ NSMutableArray *interiorRing = [NSMutableArray array];
+ for (int index = 0; index < interiorPolygon.pointCount; index++) {
+ CLLocationCoordinate2D coordinate = interiorPolygon.coordinates[index];
+ [interiorRing addObject:@[@(coordinate.longitude), @(coordinate.latitude)]];
+ }
+ [coordinates addObject:interiorRing];
+ }
+
+ return [coordinates copy];
+}
+
@end
@interface MGLMultiPolygon ()
diff --git a/platform/darwin/src/MGLPolygon_Private.h b/platform/darwin/src/MGLPolygon_Private.h
new file mode 100644
index 0000000000..75afcd61f6
--- /dev/null
+++ b/platform/darwin/src/MGLPolygon_Private.h
@@ -0,0 +1,11 @@
+#import "MGLPolygon.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface MGLPolygon (Private)
+
+- (NS_ARRAY_OF(id) *)mgl_coordinates;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLPolyline+MGLAdditions.h b/platform/darwin/src/MGLPolyline+MGLAdditions.h
deleted file mode 100644
index 4cdbbf17f9..0000000000
--- a/platform/darwin/src/MGLPolyline+MGLAdditions.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#import <Mapbox/Mapbox.h>
-
-@interface MGLPolyline (MGLAdditions)
-
-- (NS_ARRAY_OF(id) *)mgl_coordinates;
-
-@end
diff --git a/platform/darwin/src/MGLPolyline+MGLAdditions.m b/platform/darwin/src/MGLPolyline+MGLAdditions.m
deleted file mode 100644
index d1db2c58a0..0000000000
--- a/platform/darwin/src/MGLPolyline+MGLAdditions.m
+++ /dev/null
@@ -1,14 +0,0 @@
-#import "MGLPolyline+MGLAdditions.h"
-
-@implementation MGLPolyline (MGLAdditions)
-
-- (NS_ARRAY_OF(id) *)mgl_coordinates {
- NSMutableArray *coordinates = [[NSMutableArray alloc] initWithCapacity:self.pointCount];
- for (NSUInteger index = 0; index < self.pointCount; index++) {
- CLLocationCoordinate2D coordinate = self.coordinates[index];
- [coordinates addObject:@[@(coordinate.longitude), @(coordinate.latitude)]];
- }
- return [coordinates copy];
-}
-
-@end
diff --git a/platform/darwin/src/MGLPolyline.mm b/platform/darwin/src/MGLPolyline.mm
index 0e371a4dda..e011d09215 100644
--- a/platform/darwin/src/MGLPolyline.mm
+++ b/platform/darwin/src/MGLPolyline.mm
@@ -1,9 +1,9 @@
-#import "MGLPolyline.h"
+#import "MGLPolyline_Private.h"
#import "MGLMultiPoint_Private.h"
#import "MGLGeometry_Private.h"
-#import "MGLPolyline+MGLAdditions.h"
+#import "MGLFeature.h"
#import <mbgl/util/geojson.hpp>
#import <mapbox/polylabel.hpp>
@@ -49,6 +49,15 @@
@"coordinates": self.mgl_coordinates};
}
+- (NS_ARRAY_OF(id) *)mgl_coordinates {
+ NSMutableArray *coordinates = [[NSMutableArray alloc] initWithCapacity:self.pointCount];
+ for (NSUInteger index = 0; index < self.pointCount; index++) {
+ CLLocationCoordinate2D coordinate = self.coordinates[index];
+ [coordinates addObject:@[@(coordinate.longitude), @(coordinate.latitude)]];
+ }
+ return [coordinates copy];
+}
+
- (BOOL)isEqual:(id)other {
return self == other || ([other isKindOfClass:[MGLPolyline class]] && [super isEqual:other]);
}
diff --git a/platform/darwin/src/MGLPolyline_Private.h b/platform/darwin/src/MGLPolyline_Private.h
new file mode 100644
index 0000000000..405a0c5bc9
--- /dev/null
+++ b/platform/darwin/src/MGLPolyline_Private.h
@@ -0,0 +1,12 @@
+#import "MGLPolyline.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface MGLPolyline (Private)
+
+- (NS_ARRAY_OF(id) *)mgl_coordinates;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
diff --git a/platform/darwin/src/MGLRasterDEMSource.h b/platform/darwin/src/MGLRasterDEMSource.h
new file mode 100644
index 0000000000..d00912ca79
--- /dev/null
+++ b/platform/darwin/src/MGLRasterDEMSource.h
@@ -0,0 +1,37 @@
+#import "MGLFoundation.h"
+
+#import "MGLRasterSource.h"
+
+/**
+ `MGLRasterDEMSource` is a map content source that supplies rasterized
+ <a href="https://en.wikipedia.org/wiki/Digital_elevation_model">digital elevation model</a>
+ (DEM) tiles to be shown on the map. The location of and metadata about the
+ tiles are defined either by an option dictionary or by an external file that
+ conforms to the
+ <a href="https://github.com/mapbox/tilejson-spec/">TileJSON specification</a>.
+ A raster DEM source is added to an `MGLStyle` object along with one or more
+ `MGLHillshadeStyleLayer` objects. Use a hillshade style layer to control the
+ appearance of content supplied by the raster DEM source.
+
+ Each
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-raster-dem"><code>raster-dem</code></a>
+ source defined by the style JSON file is represented at runtime by an
+ `MGLRasterDEMSource` object that you can use to initialize new style layers.
+ You can also add and remove sources dynamically using methods such as
+ `-[MGLStyle addSource:]` and `-[MGLStyle sourceWithIdentifier:]`.
+
+ Currently, raster DEM sources only support the format used by
+ <a href="https://www.mapbox.com/help/access-elevation-data/#mapbox-terrain-rgb">Mapbox Terrain-RGB</a>.
+
+ ### Example
+
+ ```swift
+ let terrainRGBURL = URL(string: "mapbox://mapbox.terrain-rgb")!
+ let source = MGLRasterDEMSource(identifier: "hills", configurationURL: terrainRGBURL)
+ mapView.style?.addSource(source)
+ ```
+ */
+MGL_EXPORT
+@interface MGLRasterDEMSource : MGLRasterSource
+
+@end
diff --git a/platform/darwin/src/MGLRasterDEMSource.mm b/platform/darwin/src/MGLRasterDEMSource.mm
new file mode 100644
index 0000000000..d8639b70e3
--- /dev/null
+++ b/platform/darwin/src/MGLRasterDEMSource.mm
@@ -0,0 +1,17 @@
+#import "MGLRasterDEMSource.h"
+
+#import "MGLRasterSource_Private.h"
+#import "NSURL+MGLAdditions.h"
+
+#import <mbgl/style/sources/raster_dem_source.hpp>
+
+@implementation MGLRasterDEMSource
+
+- (std::unique_ptr<mbgl::style::RasterSource>)pendingSourceWithIdentifier:(NSString *)identifier configurationURL:(NSURL *)configurationURL tileSize:(CGFloat)tileSize {
+ NSString *configurationURLString = configurationURL.mgl_URLByStandardizingScheme.absoluteString;
+ return std::make_unique<mbgl::style::RasterDEMSource>(identifier.UTF8String,
+ configurationURLString.UTF8String,
+ uint16_t(round(tileSize)));
+}
+
+@end
diff --git a/platform/darwin/src/MGLRasterSource.mm b/platform/darwin/src/MGLRasterSource.mm
index 8fc18ba69d..c47cc199eb 100644
--- a/platform/darwin/src/MGLRasterSource.mm
+++ b/platform/darwin/src/MGLRasterSource.mm
@@ -33,12 +33,17 @@ static const CGFloat MGLRasterSourceRetinaTileSize = 512;
}
- (instancetype)initWithIdentifier:(NSString *)identifier configurationURL:(NSURL *)configurationURL tileSize:(CGFloat)tileSize {
- auto source = std::make_unique<mbgl::style::RasterSource>(identifier.UTF8String,
- configurationURL.mgl_URLByStandardizingScheme.absoluteString.UTF8String,
- uint16_t(round(tileSize)));
+ auto source = [self pendingSourceWithIdentifier:identifier configurationURL:configurationURL tileSize:tileSize];
return self = [super initWithPendingSource:std::move(source)];
}
+- (std::unique_ptr<mbgl::style::RasterSource>)pendingSourceWithIdentifier:(NSString *)identifier configurationURL:(NSURL *)configurationURL tileSize:(CGFloat)tileSize {
+ NSString *configurationURLString = configurationURL.mgl_URLByStandardizingScheme.absoluteString;
+ return std::make_unique<mbgl::style::RasterSource>(identifier.UTF8String,
+ configurationURLString.UTF8String,
+ uint16_t(round(tileSize)));
+}
+
- (instancetype)initWithIdentifier:(NSString *)identifier tileURLTemplates:(NS_ARRAY_OF(NSString *) *)tileURLTemplates options:(nullable NS_DICTIONARY_OF(MGLTileSourceOption, id) *)options {
mbgl::Tileset tileSet = MGLTileSetFromTileURLTemplates(tileURLTemplates, options);
diff --git a/platform/darwin/src/MGLRasterSource_Private.h b/platform/darwin/src/MGLRasterSource_Private.h
index 76790bd053..6f40fbc5a9 100644
--- a/platform/darwin/src/MGLRasterSource_Private.h
+++ b/platform/darwin/src/MGLRasterSource_Private.h
@@ -1,8 +1,21 @@
#import "MGLRasterSource.h"
+#include <memory>
+
+namespace mbgl {
+ namespace style {
+ class RasterSource;
+ }
+}
+
NS_ASSUME_NONNULL_BEGIN
@interface MGLRasterSource (Private)
+
+@property (nonatomic, readonly) mbgl::style::RasterSource *rawSource;
+
+- (std::unique_ptr<mbgl::style::RasterSource>)pendingSourceWithIdentifier:(NSString *)identifier configurationURL:(NSURL *)configurationURL tileSize:(CGFloat)tileSize;
+
@end
NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLRasterStyleLayer.h b/platform/darwin/src/MGLRasterStyleLayer.h
index 53a6a98b8a..7773246cad 100644
--- a/platform/darwin/src/MGLRasterStyleLayer.h
+++ b/platform/darwin/src/MGLRasterStyleLayer.h
@@ -2,22 +2,28 @@
// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
#import "MGLFoundation.h"
-#import "MGLStyleValue.h"
#import "MGLForegroundStyleLayer.h"
NS_ASSUME_NONNULL_BEGIN
/**
- An `MGLRasterStyleLayer` is a style layer that renders raster tiles on the map.
+ An `MGLRasterStyleLayer` is a style layer that renders georeferenced raster
+ imagery on the map, especially raster tiles.
Use a raster style layer to configure the color parameters of raster tiles
- loaded by an `MGLRasterSource` object. For example, you could use a raster
- style layer to render <a href="https://www.mapbox.com/satellite/">Mapbox
- Satellite</a> imagery, a <a
+ loaded by an `MGLRasterSource` object or raster images loaded by an
+ `MGLImageSource` object. For example, you could use a raster style layer to
+ render <a href="https://www.mapbox.com/satellite/">Mapbox Satellite</a>
+ imagery, a <a
href="https://www.mapbox.com/help/define-tileset/#raster-tilesets">raster tile
set</a> uploaded to Mapbox Studio, or a raster map authored in <a
href="https://tilemill-project.github.io/tilemill/">TileMill</a>, the classic
Mapbox Editor, or Mapbox Studio Classic.
+
+ Raster images may also be used as icons or patterns in a style layer. To
+ register an image for use as an icon or pattern, use the `-[MGLStyle
+ setImage:forName:]` method. To configure a point annotation’s image, use the
+ `MGLAnnotationImage` class.
You can access an existing raster style layer using the
`-[MGLStyle layerWithIdentifier:]` method if you know its identifier;
@@ -29,7 +35,7 @@ NS_ASSUME_NONNULL_BEGIN
```swift
let layer = MGLRasterStyleLayer(identifier: "clouds", source: source)
- layer.rasterOpacity = MGLStyleValue(rawValue: 0.5)
+ layer.rasterOpacity = NSExpression(forConstantValue: 0.5)
mapView.style?.addLayer(layer)
```
*/
@@ -57,22 +63,25 @@ MGL_EXPORT
Increase or reduce the brightness of the image. The value is the maximum
brightness.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `1`. Set this property to `nil` to reset
- it to the default value.
+ The default value of this property is an expression that evaluates to the float
+ `1`. Set this property to `nil` to reset it to the default value.
This attribute corresponds to the <a
href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-raster-brightness-max"><code>raster-brightness-max</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
+
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
+ This property does not support applying interpolation or step functions to
+ feature attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *maximumRasterBrightness;
+@property (nonatomic, null_resettable) NSExpression *maximumRasterBrightness;
/**
The transition affecting any changes to this layer’s `maximumRasterBrightness` property.
@@ -81,28 +90,31 @@ MGL_EXPORT
*/
@property (nonatomic) MGLTransition maximumRasterBrightnessTransition;
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *rasterBrightnessMax __attribute__((unavailable("Use maximumRasterBrightness instead.")));
+@property (nonatomic, null_resettable) NSExpression *rasterBrightnessMax __attribute__((unavailable("Use maximumRasterBrightness instead.")));
/**
Increase or reduce the brightness of the image. The value is the minimum
brightness.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `0`. Set this property to `nil` to reset
- it to the default value.
+ The default value of this property is an expression that evaluates to the float
+ `0`. Set this property to `nil` to reset it to the default value.
This attribute corresponds to the <a
href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-raster-brightness-min"><code>raster-brightness-min</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
+
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
+ This property does not support applying interpolation or step functions to
+ feature attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *minimumRasterBrightness;
+@property (nonatomic, null_resettable) NSExpression *minimumRasterBrightness;
/**
The transition affecting any changes to this layer’s `minimumRasterBrightness` property.
@@ -111,23 +123,26 @@ MGL_EXPORT
*/
@property (nonatomic) MGLTransition minimumRasterBrightnessTransition;
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *rasterBrightnessMin __attribute__((unavailable("Use minimumRasterBrightness instead.")));
+@property (nonatomic, null_resettable) NSExpression *rasterBrightnessMin __attribute__((unavailable("Use minimumRasterBrightness instead.")));
/**
Increase or reduce the contrast of the image.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `0`. Set this property to `nil` to reset
- it to the default value.
+ The default value of this property is an expression that evaluates to the float
+ `0`. Set this property to `nil` to reset it to the default value.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
+
+ This property does not support applying interpolation or step functions to
+ feature attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *rasterContrast;
+@property (nonatomic, null_resettable) NSExpression *rasterContrast;
/**
The transition affecting any changes to this layer’s `rasterContrast` property.
@@ -141,47 +156,46 @@ MGL_EXPORT
This property is measured in milliseconds.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `300`. Set this property to `nil` to
- reset it to the default value.
+ The default value of this property is an expression that evaluates to the float
+ `300`. Set this property to `nil` to reset it to the default value.
+
+ You can set this property to an expression containing any of the following:
- You can set this property to an instance of:
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
+ This property does not support applying interpolation or step functions to
+ feature attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *rasterFadeDuration;
-
-/**
- The transition affecting any changes to this layer’s `rasterFadeDuration` property.
-
- This property corresponds to the `raster-fade-duration-transition` property in the style JSON file format.
-*/
-@property (nonatomic) MGLTransition rasterFadeDurationTransition;
+@property (nonatomic, null_resettable) NSExpression *rasterFadeDuration;
/**
Rotates hues around the color wheel.
This property is measured in degrees.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `0`. Set this property to `nil` to reset
- it to the default value.
+ The default value of this property is an expression that evaluates to the float
+ `0`. Set this property to `nil` to reset it to the default value.
This attribute corresponds to the <a
href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-raster-hue-rotate"><code>raster-hue-rotate</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
+
+ This property does not support applying interpolation or step functions to
+ feature attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *rasterHueRotation;
+@property (nonatomic, null_resettable) NSExpression *rasterHueRotation;
/**
The transition affecting any changes to this layer’s `rasterHueRotation` property.
@@ -190,23 +204,26 @@ MGL_EXPORT
*/
@property (nonatomic) MGLTransition rasterHueRotationTransition;
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *rasterHueRotate __attribute__((unavailable("Use rasterHueRotation instead.")));
+@property (nonatomic, null_resettable) NSExpression *rasterHueRotate __attribute__((unavailable("Use rasterHueRotation instead.")));
/**
The opacity at which the image will be drawn.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `1`. Set this property to `nil` to reset
- it to the default value.
+ The default value of this property is an expression that evaluates to the float
+ `1`. Set this property to `nil` to reset it to the default value.
+
+ You can set this property to an expression containing any of the following:
- You can set this property to an instance of:
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
+ This property does not support applying interpolation or step functions to
+ feature attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *rasterOpacity;
+@property (nonatomic, null_resettable) NSExpression *rasterOpacity;
/**
The transition affecting any changes to this layer’s `rasterOpacity` property.
@@ -218,18 +235,21 @@ MGL_EXPORT
/**
Increase or reduce the saturation of the image.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `0`. Set this property to `nil` to reset
- it to the default value.
+ The default value of this property is an expression that evaluates to the float
+ `0`. Set this property to `nil` to reset it to the default value.
+
+ You can set this property to an expression containing any of the following:
- You can set this property to an instance of:
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
+ This property does not support applying interpolation or step functions to
+ feature attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *rasterSaturation;
+@property (nonatomic, null_resettable) NSExpression *rasterSaturation;
/**
The transition affecting any changes to this layer’s `rasterSaturation` property.
diff --git a/platform/darwin/src/MGLRasterStyleLayer.mm b/platform/darwin/src/MGLRasterStyleLayer.mm
index c63fd23529..94a58409de 100644
--- a/platform/darwin/src/MGLRasterStyleLayer.mm
+++ b/platform/darwin/src/MGLRasterStyleLayer.mm
@@ -39,21 +39,21 @@
#pragma mark - Accessing the Paint Attributes
-- (void)setMaximumRasterBrightness:(MGLStyleValue<NSNumber *> *)maximumRasterBrightness {
+- (void)setMaximumRasterBrightness:(NSExpression *)maximumRasterBrightness {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(maximumRasterBrightness);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(maximumRasterBrightness);
self.rawLayer->setRasterBrightnessMax(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)maximumRasterBrightness {
+- (NSExpression *)maximumRasterBrightness {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getRasterBrightnessMax();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultRasterBrightnessMax());
+ propertyValue = self.rawLayer->getDefaultRasterBrightnessMax();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
- (void)setMaximumRasterBrightnessTransition:(MGLTransition )transition {
@@ -74,28 +74,28 @@
return transition;
}
-- (void)setRasterBrightnessMax:(MGLStyleValue<NSNumber *> *)rasterBrightnessMax {
+- (void)setRasterBrightnessMax:(NSExpression *)rasterBrightnessMax {
}
-- (MGLStyleValue<NSNumber *> *)rasterBrightnessMax {
+- (NSExpression *)rasterBrightnessMax {
return self.maximumRasterBrightness;
}
-- (void)setMinimumRasterBrightness:(MGLStyleValue<NSNumber *> *)minimumRasterBrightness {
+- (void)setMinimumRasterBrightness:(NSExpression *)minimumRasterBrightness {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(minimumRasterBrightness);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(minimumRasterBrightness);
self.rawLayer->setRasterBrightnessMin(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)minimumRasterBrightness {
+- (NSExpression *)minimumRasterBrightness {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getRasterBrightnessMin();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultRasterBrightnessMin());
+ propertyValue = self.rawLayer->getDefaultRasterBrightnessMin();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
- (void)setMinimumRasterBrightnessTransition:(MGLTransition )transition {
@@ -116,28 +116,28 @@
return transition;
}
-- (void)setRasterBrightnessMin:(MGLStyleValue<NSNumber *> *)rasterBrightnessMin {
+- (void)setRasterBrightnessMin:(NSExpression *)rasterBrightnessMin {
}
-- (MGLStyleValue<NSNumber *> *)rasterBrightnessMin {
+- (NSExpression *)rasterBrightnessMin {
return self.minimumRasterBrightness;
}
-- (void)setRasterContrast:(MGLStyleValue<NSNumber *> *)rasterContrast {
+- (void)setRasterContrast:(NSExpression *)rasterContrast {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(rasterContrast);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(rasterContrast);
self.rawLayer->setRasterContrast(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)rasterContrast {
+- (NSExpression *)rasterContrast {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getRasterContrast();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultRasterContrast());
+ propertyValue = self.rawLayer->getDefaultRasterContrast();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
- (void)setRasterContrastTransition:(MGLTransition )transition {
@@ -158,56 +158,38 @@
return transition;
}
-- (void)setRasterFadeDuration:(MGLStyleValue<NSNumber *> *)rasterFadeDuration {
+- (void)setRasterFadeDuration:(NSExpression *)rasterFadeDuration {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(rasterFadeDuration);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(rasterFadeDuration);
self.rawLayer->setRasterFadeDuration(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)rasterFadeDuration {
+- (NSExpression *)rasterFadeDuration {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getRasterFadeDuration();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultRasterFadeDuration());
+ propertyValue = self.rawLayer->getDefaultRasterFadeDuration();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
-- (void)setRasterFadeDurationTransition:(MGLTransition )transition {
+- (void)setRasterHueRotation:(NSExpression *)rasterHueRotation {
MGLAssertStyleLayerIsValid();
- mbgl::style::TransitionOptions options { { MGLDurationFromTimeInterval(transition.duration) }, { MGLDurationFromTimeInterval(transition.delay) } };
- self.rawLayer->setRasterFadeDurationTransition(options);
-}
-
-- (MGLTransition)rasterFadeDurationTransition {
- MGLAssertStyleLayerIsValid();
-
- mbgl::style::TransitionOptions transitionOptions = self.rawLayer->getRasterFadeDurationTransition();
- MGLTransition transition;
- transition.duration = MGLTimeIntervalFromDuration(transitionOptions.duration.value_or(mbgl::Duration::zero()));
- transition.delay = MGLTimeIntervalFromDuration(transitionOptions.delay.value_or(mbgl::Duration::zero()));
-
- return transition;
-}
-
-- (void)setRasterHueRotation:(MGLStyleValue<NSNumber *> *)rasterHueRotation {
- MGLAssertStyleLayerIsValid();
-
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(rasterHueRotation);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(rasterHueRotation);
self.rawLayer->setRasterHueRotate(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)rasterHueRotation {
+- (NSExpression *)rasterHueRotation {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getRasterHueRotate();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultRasterHueRotate());
+ propertyValue = self.rawLayer->getDefaultRasterHueRotate();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
- (void)setRasterHueRotationTransition:(MGLTransition )transition {
@@ -228,28 +210,28 @@
return transition;
}
-- (void)setRasterHueRotate:(MGLStyleValue<NSNumber *> *)rasterHueRotate {
+- (void)setRasterHueRotate:(NSExpression *)rasterHueRotate {
}
-- (MGLStyleValue<NSNumber *> *)rasterHueRotate {
+- (NSExpression *)rasterHueRotate {
return self.rasterHueRotation;
}
-- (void)setRasterOpacity:(MGLStyleValue<NSNumber *> *)rasterOpacity {
+- (void)setRasterOpacity:(NSExpression *)rasterOpacity {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(rasterOpacity);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(rasterOpacity);
self.rawLayer->setRasterOpacity(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)rasterOpacity {
+- (NSExpression *)rasterOpacity {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getRasterOpacity();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultRasterOpacity());
+ propertyValue = self.rawLayer->getDefaultRasterOpacity();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
- (void)setRasterOpacityTransition:(MGLTransition )transition {
@@ -270,21 +252,21 @@
return transition;
}
-- (void)setRasterSaturation:(MGLStyleValue<NSNumber *> *)rasterSaturation {
+- (void)setRasterSaturation:(NSExpression *)rasterSaturation {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(rasterSaturation);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(rasterSaturation);
self.rawLayer->setRasterSaturation(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)rasterSaturation {
+- (NSExpression *)rasterSaturation {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getRasterSaturation();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultRasterSaturation());
+ propertyValue = self.rawLayer->getDefaultRasterSaturation();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
- (void)setRasterSaturationTransition:(MGLTransition )transition {
diff --git a/platform/darwin/src/MGLRendererConfiguration.h b/platform/darwin/src/MGLRendererConfiguration.h
index fbc6fc564d..31aad0a742 100644
--- a/platform/darwin/src/MGLRendererConfiguration.h
+++ b/platform/darwin/src/MGLRendererConfiguration.h
@@ -1,6 +1,6 @@
#import <Foundation/Foundation.h>
#import <mbgl/storage/default_file_source.hpp>
-#import <mbgl/map/mode.hpp>
+#import <mbgl/renderer/mode.hpp>
NS_ASSUME_NONNULL_BEGIN
diff --git a/platform/darwin/src/MGLRendererFrontend.h b/platform/darwin/src/MGLRendererFrontend.h
index 76904d008b..2df67ca4e4 100644
--- a/platform/darwin/src/MGLRendererFrontend.h
+++ b/platform/darwin/src/MGLRendererFrontend.h
@@ -56,9 +56,9 @@ public:
return renderer.get();
}
- void onLowMemory() {
+ void reduceMemoryUse() {
if (!renderer) return;
- renderer->onLowMemory();
+ renderer->reduceMemoryUse();
}
private:
diff --git a/platform/darwin/src/MGLShapeSource.h b/platform/darwin/src/MGLShapeSource.h
index 7460c83f50..929609e91f 100644
--- a/platform/darwin/src/MGLShapeSource.h
+++ b/platform/darwin/src/MGLShapeSource.h
@@ -1,4 +1,4 @@
-#import "MGLSource.h"
+#import "MGLAbstractShapeSource.h"
#import "MGLFoundation.h"
#import "MGLTypes.h"
@@ -9,77 +9,6 @@ NS_ASSUME_NONNULL_BEGIN
@protocol MGLFeature;
/**
- Options for `MGLShapeSource` objects.
- */
-typedef NSString *MGLShapeSourceOption NS_STRING_ENUM;
-
-/**
- An `NSNumber` object containing a Boolean enabling or disabling clustering.
- If the `shape` property contains point shapes, setting this option to
- `YES` clusters the points by radius into groups. The default value is `NO`.
-
- This attribute corresponds to the
- <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-cluster"><code>cluster</code></a>
- source property in the Mapbox Style Specification.
-
- This option only affects point features within a shape source.
- */
-extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionClustered;
-
-/**
- An `NSNumber` object containing an integer; specifies the radius of each
- cluster if clustering is enabled. A value of 512 produces a radius equal to
- the width of a tile. The default value is 50.
- */
-extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionClusterRadius;
-
-/**
- An `NSNumber` object containing an integer; specifies the maximum zoom level at
- which to cluster points if clustering is enabled. Defaults to one zoom level
- less than the value of `MGLShapeSourceOptionMaximumZoomLevel` so that, at the
- maximum zoom level, the shapes are not clustered.
-
- This attribute corresponds to the
- <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-clusterMaxZoom"><code>clusterMaxZoom</code></a>
- source property in the Mapbox Style Specification.
- */
-extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevelForClustering;
-
-/**
- An `NSNumber` object containing an integer; specifies the maximum zoom level at
- which to create vector tiles. A greater value produces greater detail at high
- zoom levels. The default value is 18.
-
- This attribute corresponds to the
- <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-maxzoom"><code>maxzoom</code></a>
- source property in the Mapbox Style Specification.
- */
-extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevel;
-
-/**
- An `NSNumber` object containing an integer; specifies the size of the tile
- buffer on each side. A value of 0 produces no buffer. A value of 512 produces a
- buffer as wide as the tile itself. Larger values produce fewer rendering
- artifacts near tile edges and slower performance. The default value is 128.
-
- This attribute corresponds to the
- <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-buffer"><code>buffer</code></a>
- source property in the Mapbox Style Specification.
- */
-extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionBuffer;
-
-/**
- An `NSNumber` object containing a double; specifies the Douglas-Peucker
- simplification tolerance. A greater value produces simpler geometries and
- improves performance. The default value is 0.375.
-
- This attribute corresponds to the
- <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-tolerance"><code>tolerance</code></a>
- source property in the Mapbox Style Specification.
- */
-extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance;
-
-/**
`MGLShapeSource` is a map content source that supplies vector shapes to be
shown on the map. The shapes may be instances of `MGLShape` or `MGLFeature`,
or they may be defined by local or external
@@ -112,7 +41,7 @@ extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionSimplificationT
```
*/
MGL_EXPORT
-@interface MGLShapeSource : MGLSource
+@interface MGLShapeSource : MGLAbstractShapeSource
#pragma mark Initializing a Source
diff --git a/platform/darwin/src/MGLShapeSource.mm b/platform/darwin/src/MGLShapeSource.mm
index 571cbdcc62..8fbb9a18ef 100644
--- a/platform/darwin/src/MGLShapeSource.mm
+++ b/platform/darwin/src/MGLShapeSource.mm
@@ -1,4 +1,5 @@
#import "MGLShapeSource_Private.h"
+#import "MGLAbstractShapeSource_Private.h"
#import "MGLStyle_Private.h"
#import "MGLMapView_Private.h"
@@ -13,12 +14,7 @@
#include <mbgl/style/sources/geojson_source.hpp>
#include <mbgl/renderer/renderer.hpp>
-const MGLShapeSourceOption MGLShapeSourceOptionClustered = @"MGLShapeSourceOptionClustered";
-const MGLShapeSourceOption MGLShapeSourceOptionClusterRadius = @"MGLShapeSourceOptionClusterRadius";
-const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevelForClustering = @"MGLShapeSourceOptionMaximumZoomLevelForClustering";
-const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevel = @"MGLShapeSourceOptionMaximumZoomLevel";
-const MGLShapeSourceOption MGLShapeSourceOptionBuffer = @"MGLShapeSourceOptionBuffer";
-const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance = @"MGLShapeSourceOptionSimplificationTolerance";
+
@interface MGLShapeSource ()
@@ -105,57 +101,3 @@ const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance = @"MGLSh
}
@end
-
-mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *options) {
- auto geoJSONOptions = mbgl::style::GeoJSONOptions();
-
- if (NSNumber *value = options[MGLShapeSourceOptionMaximumZoomLevel]) {
- if (![value isKindOfClass:[NSNumber class]]) {
- [NSException raise:NSInvalidArgumentException
- format:@"MGLShapeSourceOptionMaximumZoomLevel must be an NSNumber."];
- }
- geoJSONOptions.maxzoom = value.integerValue;
- }
-
- if (NSNumber *value = options[MGLShapeSourceOptionBuffer]) {
- if (![value isKindOfClass:[NSNumber class]]) {
- [NSException raise:NSInvalidArgumentException
- format:@"MGLShapeSourceOptionBuffer must be an NSNumber."];
- }
- geoJSONOptions.buffer = value.integerValue;
- }
-
- if (NSNumber *value = options[MGLShapeSourceOptionSimplificationTolerance]) {
- if (![value isKindOfClass:[NSNumber class]]) {
- [NSException raise:NSInvalidArgumentException
- format:@"MGLShapeSourceOptionSimplificationTolerance must be an NSNumber."];
- }
- geoJSONOptions.tolerance = value.doubleValue;
- }
-
- if (NSNumber *value = options[MGLShapeSourceOptionClusterRadius]) {
- if (![value isKindOfClass:[NSNumber class]]) {
- [NSException raise:NSInvalidArgumentException
- format:@"MGLShapeSourceOptionClusterRadius must be an NSNumber."];
- }
- geoJSONOptions.clusterRadius = value.integerValue;
- }
-
- if (NSNumber *value = options[MGLShapeSourceOptionMaximumZoomLevelForClustering]) {
- if (![value isKindOfClass:[NSNumber class]]) {
- [NSException raise:NSInvalidArgumentException
- format:@"MGLShapeSourceOptionMaximumZoomLevelForClustering must be an NSNumber."];
- }
- geoJSONOptions.clusterMaxZoom = value.integerValue;
- }
-
- if (NSNumber *value = options[MGLShapeSourceOptionClustered]) {
- if (![value isKindOfClass:[NSNumber class]]) {
- [NSException raise:NSInvalidArgumentException
- format:@"MGLShapeSourceOptionClustered must be an NSNumber."];
- }
- geoJSONOptions.cluster = value.boolValue;
- }
-
- return geoJSONOptions;
-}
diff --git a/platform/darwin/src/MGLShapeSource_Private.h b/platform/darwin/src/MGLShapeSource_Private.h
index 84eb5deed4..1ea2c39b8e 100644
--- a/platform/darwin/src/MGLShapeSource_Private.h
+++ b/platform/darwin/src/MGLShapeSource_Private.h
@@ -12,7 +12,4 @@ namespace mbgl {
@interface MGLShapeSource (Private)
@end
-MGL_EXPORT
-mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *options);
-
NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLStyle.mm b/platform/darwin/src/MGLStyle.mm
index 987ae5f063..f6fc5533be 100644
--- a/platform/darwin/src/MGLStyle.mm
+++ b/platform/darwin/src/MGLStyle.mm
@@ -8,6 +8,8 @@
#import "MGLLineStyleLayer.h"
#import "MGLCircleStyleLayer.h"
#import "MGLSymbolStyleLayer.h"
+#import "MGLHeatmapStyleLayer.h"
+#import "MGLHillshadeStyleLayer.h"
#import "MGLRasterStyleLayer.h"
#import "MGLBackgroundStyleLayer.h"
#import "MGLOpenGLStyleLayer.h"
@@ -17,8 +19,9 @@
#import "MGLLight_Private.h"
#import "MGLTileSource_Private.h"
#import "MGLVectorSource.h"
-#import "MGLVectorSource+MGLAdditions.h"
+#import "MGLVectorSource_Private.h"
#import "MGLRasterSource.h"
+#import "MGLRasterDEMSource.h"
#import "MGLShapeSource.h"
#import "MGLImageSource.h"
@@ -34,12 +37,15 @@
#include <mbgl/style/layers/line_layer.hpp>
#include <mbgl/style/layers/symbol_layer.hpp>
#include <mbgl/style/layers/raster_layer.hpp>
+#include <mbgl/style/layers/heatmap_layer.hpp>
+#include <mbgl/style/layers/hillshade_layer.hpp>
#include <mbgl/style/layers/circle_layer.hpp>
#include <mbgl/style/layers/background_layer.hpp>
#include <mbgl/style/layers/custom_layer.hpp>
#include <mbgl/style/sources/geojson_source.hpp>
#include <mbgl/style/sources/vector_source.hpp>
#include <mbgl/style/sources/raster_source.hpp>
+#include <mbgl/style/sources/raster_dem_source.hpp>
#include <mbgl/style/sources/image_source.hpp>
#import "NSDate+MGLAdditions.h"
@@ -219,7 +225,7 @@ static NSURL *MGLStyleURL_trafficNight;
}
- (MGLSource *)sourceFromMBGLSource:(mbgl::style::Source *)rawSource {
- if (MGLSource *source = rawSource->peer.empty() ? nil : mbgl::any_cast<SourceWrapper>(rawSource->peer).source) {
+ if (MGLSource *source = rawSource->peer.has_value() ? mbgl::util::any_cast<SourceWrapper>(rawSource->peer).source : nil) {
return source;
}
@@ -231,6 +237,8 @@ static NSURL *MGLStyleURL_trafficNight;
return [[MGLShapeSource alloc] initWithRawSource:geoJSONSource mapView:self.mapView];
} else if (auto rasterSource = rawSource->as<mbgl::style::RasterSource>()) {
return [[MGLRasterSource alloc] initWithRawSource:rasterSource mapView:self.mapView];
+ } else if (auto rasterDEMSource = rawSource->as<mbgl::style::RasterDEMSource>()) {
+ return [[MGLRasterDEMSource alloc] initWithRawSource:rasterDEMSource mapView:self.mapView];
} else if (auto imageSource = rawSource->as<mbgl::style::ImageSource>()) {
return [[MGLImageSource alloc] initWithRawSource:imageSource mapView:self.mapView];
} else {
@@ -381,7 +389,7 @@ static NSURL *MGLStyleURL_trafficNight;
{
NSParameterAssert(rawLayer);
- if (MGLStyleLayer *layer = rawLayer->peer.empty() ? nil : mbgl::any_cast<LayerWrapper>(rawLayer->peer).layer) {
+ if (MGLStyleLayer *layer = rawLayer->peer.has_value() ? mbgl::util::any_cast<LayerWrapper>(&(rawLayer->peer))->layer : nil) {
return layer;
}
@@ -395,6 +403,10 @@ static NSURL *MGLStyleURL_trafficNight;
return [[MGLSymbolStyleLayer alloc] initWithRawLayer:symbolLayer];
} else if (auto rasterLayer = rawLayer->as<mbgl::style::RasterLayer>()) {
return [[MGLRasterStyleLayer alloc] initWithRawLayer:rasterLayer];
+ } else if (auto heatmapLayer = rawLayer->as<mbgl::style::HeatmapLayer>()) {
+ return [[MGLHeatmapStyleLayer alloc] initWithRawLayer:heatmapLayer];
+ } else if (auto hillshadeLayer = rawLayer->as<mbgl::style::HillshadeLayer>()) {
+ return [[MGLHillshadeStyleLayer alloc] initWithRawLayer:hillshadeLayer];
} else if (auto circleLayer = rawLayer->as<mbgl::style::CircleLayer>()) {
return [[MGLCircleStyleLayer alloc] initWithRawLayer:circleLayer];
} else if (auto backgroundLayer = rawLayer->as<mbgl::style::BackgroundLayer>()) {
@@ -677,70 +689,31 @@ static NSURL *MGLStyleURL_trafficNight;
return localizedString;
};
- if ([layer.text isKindOfClass:[MGLConstantStyleValue class]]) {
- NSString *textField = [(MGLConstantStyleValue<NSString *> *)layer.text rawValue];
+ if (layer.text.expressionType == NSConstantValueExpressionType) {
+ NSString *textField = layer.text.constantValue;
NSString *localizingString = stringByLocalizingString(textField);
if (![textField isEqualToString:localizingString]) {
MGLTextLanguage *textLanguage = [[MGLTextLanguage alloc] initWithTextLanguage:textField
updatedTextField:localizingString];
[self.localizedLayersByIdentifier setObject:@{ textField : textLanguage } forKey:layer.identifier];
- layer.text = [MGLStyleValue<NSString *> valueWithRawValue:localizingString];
+ layer.text = [NSExpression expressionForConstantValue:localizingString];
}
}
- else if ([layer.text isKindOfClass:[MGLCameraStyleFunction class]]) {
- MGLCameraStyleFunction *function = (MGLCameraStyleFunction<NSString *> *)layer.text;
- NSMutableDictionary *stops = function.stops.mutableCopy;
- NSMutableDictionary *cameraStops = [NSMutableDictionary dictionary];
- [stops enumerateKeysAndObjectsUsingBlock:^(NSNumber *zoomLevel, MGLConstantStyleValue<NSString *> *stop, BOOL *done) {
- NSString *textField = stop.rawValue;
- NSString *localizingString = stringByLocalizingString(textField);
- if (![textField isEqualToString:localizingString]) {
- MGLTextLanguage *textLanguage = [[MGLTextLanguage alloc] initWithTextLanguage:textField
- updatedTextField:localizingString];
- [cameraStops setObject:textLanguage forKey:zoomLevel];
- stops[zoomLevel] = [MGLStyleValue<NSString *> valueWithRawValue:localizingString];
- }
-
- }];
- if (cameraStops.count > 0) {
- [self.localizedLayersByIdentifier setObject:cameraStops forKey:layer.identifier];
- }
- function.stops = stops;
- layer.text = function;
- }
}
} else {
[self.localizedLayersByIdentifier enumerateKeysAndObjectsUsingBlock:^(NSString *identifier, NSDictionary<NSObject *, MGLTextLanguage *> *textFields, BOOL *done) {
MGLSymbolStyleLayer *layer = (MGLSymbolStyleLayer *)[self.mapView.style layerWithIdentifier:identifier];
- if ([layer.text isKindOfClass:[MGLConstantStyleValue class]]) {
- NSString *textField = [(MGLConstantStyleValue<NSString *> *)layer.text rawValue];
+ if (layer.text.expressionType == NSConstantValueExpressionType) {
+ NSString *textField = layer.text.constantValue;
[textFields enumerateKeysAndObjectsUsingBlock:^(NSObject *originalLanguage, MGLTextLanguage *textLanguage, BOOL *done) {
if ([textLanguage.updatedTextField isEqualToString:textField]) {
- layer.text = [MGLStyleValue<NSString *> valueWithRawValue:textLanguage.originalTextField];
+ layer.text = [NSExpression expressionForConstantValue:textLanguage.originalTextField];
}
}];
}
- else if ([layer.text isKindOfClass:[MGLCameraStyleFunction class]]) {
- MGLCameraStyleFunction *function = (MGLCameraStyleFunction<NSString *> *)layer.text;
- NSMutableDictionary *stops = function.stops.mutableCopy;
- [textFields enumerateKeysAndObjectsUsingBlock:^(NSObject *zoomKey, MGLTextLanguage *textLanguage, BOOL *done) {
- if ([zoomKey isKindOfClass:[NSNumber class]]) {
- NSNumber *zoomLevel = (NSNumber*)zoomKey;
- MGLConstantStyleValue<NSString *> *stop = [stops objectForKey:zoomLevel];
- NSString *textField = stop.rawValue;
- if ([textLanguage.updatedTextField isEqualToString:textField]) {
- stops[zoomLevel] = [MGLStyleValue<NSString *> valueWithRawValue:textLanguage.originalTextField];
- }
- }
- }];
-
- function.stops = stops;
- layer.text = function;
- }
-
}];
self.localizedLayersByIdentifier = [NSMutableDictionary dictionary];
diff --git a/platform/darwin/src/MGLStyleLayer.h.ejs b/platform/darwin/src/MGLStyleLayer.h.ejs
index df42621c6d..05f450a4f8 100644
--- a/platform/darwin/src/MGLStyleLayer.h.ejs
+++ b/platform/darwin/src/MGLStyleLayer.h.ejs
@@ -9,10 +9,9 @@
// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
#import "MGLFoundation.h"
-#import "MGLStyleValue.h"
#import "MGL<%-
(type === 'background' ? '' :
- (type === 'raster' ? 'Foreground' :
+ (type === 'raster' || type === 'hillshade' ? 'Foreground' :
'Vector'))
%>StyleLayer.h"
@@ -79,7 +78,7 @@ typedef NS_ENUM(NSUInteger, MGL<%- camelize(property.name) %>) {
MGL_EXPORT
@interface MGL<%- camelize(type) %>StyleLayer : MGL<%-
(type === 'background' ? '' :
- (type === 'raster' ? 'Foreground' :
+ (type === 'raster' || type === 'hillshade' ? 'Foreground' :
'Vector'))
%>StyleLayer
<% if (type === 'background') { -%>
@@ -120,10 +119,10 @@ which it is added.
/**
<%- propertyDoc(property.name, property, type, 'layout').wrap(80, 1) %>
*/
-@property (nonatomic<% if (!property.required) { %>, null_resettable<% } if (property.getter) { %>, getter=<%- objCGetter(property) -%><% } %>) MGLStyleValue<<%- propertyType(property, true) %>> *<%- camelizeWithLeadingLowercase(property.name) %>;
+@property (nonatomic<% if (!property.required) { %>, null_resettable<% } if (property.getter) { %>, getter=<%- objCGetter(property) -%><% } %>) NSExpression *<%- camelizeWithLeadingLowercase(property.name) %>;
<% if (property.original) { %>
-@property (nonatomic<% if (!property.required) { %>, null_resettable<% } %>) MGLStyleValue<<%- propertyType(property, true) %>> *<%- camelizeWithLeadingLowercase(originalPropertyName(property)) %> __attribute__((unavailable("Use <%- camelizeWithLeadingLowercase(property.name) %> instead.")));
+@property (nonatomic<% if (!property.required) { %>, null_resettable<% } %>) NSExpression *<%- camelizeWithLeadingLowercase(originalPropertyName(property)) %> __attribute__((unavailable("Use <%- camelizeWithLeadingLowercase(property.name) %> instead.")));
<% } -%>
<% } -%>
@@ -135,7 +134,7 @@ which it is added.
/**
<%- propertyDoc(property.name, property, type, 'paint').wrap(80, 1) %>
*/
-@property (nonatomic<% if (!property.required) { %>, null_resettable<% } if (property.getter) { %>, getter=<%- objCGetter(property) -%><% } %>) MGLStyleValue<<%- propertyType(property, true) %>> *<%- camelizeWithLeadingLowercase(property.name) %>;
+@property (nonatomic<% if (!property.required) { %>, null_resettable<% } if (property.getter) { %>, getter=<%- objCGetter(property) -%><% } %>) NSExpression *<%- camelizeWithLeadingLowercase(property.name) %>;
<% if (property["transition"]) { -%>
/**
@@ -147,7 +146,7 @@ which it is added.
<% } -%>
<% if (property.original) { -%>
-@property (nonatomic<% if (!property.required) { %>, null_resettable<% } %>) MGLStyleValue<<%- propertyType(property, true) %>> *<%- camelizeWithLeadingLowercase(originalPropertyName(property)) %> __attribute__((unavailable("Use <%- camelizeWithLeadingLowercase(property.name) %> instead.")));
+@property (nonatomic<% if (!property.required) { %>, null_resettable<% } %>) NSExpression *<%- camelizeWithLeadingLowercase(originalPropertyName(property)) %> __attribute__((unavailable("Use <%- camelizeWithLeadingLowercase(property.name) %> instead.")));
<% } -%>
<% } -%>
diff --git a/platform/darwin/src/MGLStyleLayer.mm.ejs b/platform/darwin/src/MGLStyleLayer.mm.ejs
index da67cbd633..ac7676a1cc 100644
--- a/platform/darwin/src/MGLStyleLayer.mm.ejs
+++ b/platform/darwin/src/MGLStyleLayer.mm.ejs
@@ -83,7 +83,7 @@ namespace mbgl {
return @(self.rawLayer->getSourceID().c_str());
}
-<% if (type !== 'raster') { -%>
+<% if (type !== 'raster' && type !== 'hillshade') { -%>
- (NSString *)sourceLayerIdentifier
{
MGLAssertStyleLayerIsValid();
@@ -118,52 +118,32 @@ namespace mbgl {
#pragma mark - Accessing the Layout Attributes
<% for (const property of layoutProperties) { -%>
-- (void)set<%- camelize(property.name) %>:(MGLStyleValue<<%- propertyType(property, true) %>> *)<%- objCName(property) %> {
+- (void)set<%- camelize(property.name) %>:(NSExpression *)<%- objCName(property) %> {
MGLAssertStyleLayerIsValid();
<% if (property["property-function"]) { -%>
- auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toDataDrivenPropertyValue(<%- objCName(property) %>);
+ auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<<%- valueTransformerArguments(property)[0] %>>>(<%- objCName(property) %>);
<% } else { -%>
-<% if (property.type == "enum") { -%>
- auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toEnumPropertyValue(<%- objCName(property) %>);
-<% } else if (property.function == "piecewise-constant") { -%>
- auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toPropertyValue(<%- objCName(property) %>);
-<% } else { -%>
- auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toInterpolatablePropertyValue(<%- objCName(property) %>);
-<% } -%>
+ auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toPropertyValue<mbgl::style::PropertyValue<<%- valueTransformerArguments(property)[0] %>>>(<%- objCName(property) %>);
<% } -%>
self.rawLayer->set<%- camelize(originalPropertyName(property)) %>(mbglValue);
}
-- (MGLStyleValue<<%- propertyType(property, true) %>> *)<%- objCGetter(property) %> {
+- (NSExpression *)<%- objCGetter(property) %> {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->get<%- camelize(originalPropertyName(property)) %>();
-<% if (property["property-function"]) { -%>
- if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toDataDrivenStyleValue(self.rawLayer->getDefault<%- camelize(originalPropertyName(property)) %>());
- }
- return MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toDataDrivenStyleValue(propertyValue);
-<% } else { -%>
-<% if (property.type == "enum") { -%>
- if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<<%- mbglType(property) %>, NSValue *, <%- mbglType(property) %>, MGL<%- camelize(property.name) %>>().toEnumStyleValue(self.rawLayer->getDefault<%- camelize(originalPropertyName(property)) %>());
- }
- return MGLStyleValueTransformer<<%- mbglType(property) %>, NSValue *, <%- mbglType(property) %>, MGL<%- camelize(property.name) %>>().toEnumStyleValue(propertyValue);
-<% } else { -%>
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toStyleValue(self.rawLayer->getDefault<%- camelize(originalPropertyName(property)) %>());
+ propertyValue = self.rawLayer->getDefault<%- camelize(originalPropertyName(property)) %>();
}
- return MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toStyleValue(propertyValue);
-<% } -%>
-<% } -%>
+ return MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toExpression(propertyValue);
}
<% if (property.original) { -%>
-- (void)set<%- camelize(originalPropertyName(property)) %>:(MGLStyleValue<<%- propertyType(property, true) %>> *)<%- camelizeWithLeadingLowercase(originalPropertyName(property)) %> {
+- (void)set<%- camelize(originalPropertyName(property)) %>:(NSExpression *)<%- camelizeWithLeadingLowercase(originalPropertyName(property)) %> {
}
-- (MGLStyleValue<<%- propertyType(property, true) %>> *)<%- camelizeWithLeadingLowercase(originalPropertyName(property)) %> {
+- (NSExpression *)<%- camelizeWithLeadingLowercase(originalPropertyName(property)) %> {
return self.<%- objCGetter(property) %>;
}
@@ -174,45 +154,27 @@ namespace mbgl {
#pragma mark - Accessing the Paint Attributes
<% for (const property of paintProperties) { -%>
-- (void)set<%- camelize(property.name) %>:(MGLStyleValue<<%- propertyType(property, true) %>> *)<%- objCName(property) %> {
+- (void)set<%- camelize(property.name) %>:(NSExpression *)<%- objCName(property) %> {
MGLAssertStyleLayerIsValid();
-<% if (property["property-function"]) { -%>
- auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toDataDrivenPropertyValue(<%- objCName(property) %>);
+<% if (property.name === 'heatmap-color') { -%>
+ auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toPropertyValue<mbgl::style::HeatmapColorPropertyValue>(heatmapColor);
+<% } else if (property["property-function"]) { -%>
+ auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<<%- valueTransformerArguments(property)[0] %>>>(<%- objCName(property) %>);
<% } else { -%>
-<% if (property.type == "enum") { -%>
- auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toEnumPropertyValue(<%- objCName(property) %>);
-<% } else if (property.function == "piecewise-constant") { -%>
- auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toPropertyValue(<%- objCName(property) %>);
-<% } else { -%>
- auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toInterpolatablePropertyValue(<%- objCName(property) %>);
-<% } -%>
+ auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toPropertyValue<mbgl::style::PropertyValue<<%- valueTransformerArguments(property)[0] %>>>(<%- objCName(property) %>);
<% } -%>
self.rawLayer->set<%- camelize(originalPropertyName(property)) %>(mbglValue);
}
-- (MGLStyleValue<<%- propertyType(property, true) %>> *)<%- objCGetter(property) %> {
+- (NSExpression *)<%- objCGetter(property) %> {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->get<%- camelize(originalPropertyName(property)) %>();
-<% if (property["property-function"]) { -%>
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toDataDrivenStyleValue(self.rawLayer->getDefault<%- camelize(originalPropertyName(property)) %>());
+ propertyValue = self.rawLayer->getDefault<%- camelize(originalPropertyName(property)) %>();
}
- return MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toDataDrivenStyleValue(propertyValue);
-<% } else { -%>
-<% if (property.type == "enum") { -%>
- if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<<%- mbglType(property) %>, NSValue *, <%- mbglType(property) %>, MGL<%- camelize(property.name) %>>().toEnumStyleValue(self.rawLayer->getDefault<%- camelize(originalPropertyName(property)) %>());
- }
- return MGLStyleValueTransformer<<%- mbglType(property) %>, NSValue *, <%- mbglType(property) %>, MGL<%- camelize(property.name) %>>().toEnumStyleValue(propertyValue);
-<% } else { -%>
- if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toStyleValue(self.rawLayer->getDefault<%- camelize(originalPropertyName(property)) %>());
- }
- return MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toStyleValue(propertyValue);
-<% } -%>
-<% } -%>
+ return MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toExpression(propertyValue);
}
<% if (property["transition"]) { -%>
@@ -236,10 +198,10 @@ namespace mbgl {
<% } -%>
<% if (property.original) { -%>
-- (void)set<%- camelize(originalPropertyName(property)) %>:(MGLStyleValue<<%- propertyType(property, true) %>> *)<%- camelizeWithLeadingLowercase(originalPropertyName(property)) %> {
+- (void)set<%- camelize(originalPropertyName(property)) %>:(NSExpression *)<%- camelizeWithLeadingLowercase(originalPropertyName(property)) %> {
}
-- (MGLStyleValue<<%- propertyType(property, true) %>> *)<%- camelizeWithLeadingLowercase(originalPropertyName(property)) %> {
+- (NSExpression *)<%- camelizeWithLeadingLowercase(originalPropertyName(property)) %> {
return self.<%- objCGetter(property) %>;
}
<% } -%>
diff --git a/platform/darwin/src/MGLStyleValue.h b/platform/darwin/src/MGLStyleValue.h
index 9c9b1dc4d1..f6e9c51729 100644
--- a/platform/darwin/src/MGLStyleValue.h
+++ b/platform/darwin/src/MGLStyleValue.h
@@ -6,485 +6,43 @@
NS_ASSUME_NONNULL_BEGIN
-/**
- Options for `MGLStyleFunction` objects.
- */
-typedef NSString *MGLStyleFunctionOption NS_STRING_ENUM;
+typedef NSString *MGLStyleFunctionOption NS_STRING_ENUM NS_UNAVAILABLE;
-/**
- An `NSNumber` object containing an integer that determines the style function's
- exponential interpolation base.
-
- The exponential interpolation base controls the rate at which the function’s
- output values increase. A value of 1 causes the function to increase linearly
- based on zoom level or attribute value. A higher exponential interpolation base
- causes the function’s output values to vary exponentially, increasing more rapidly
- towards the high end of the function’s range. The default value of this property
- is 1, for a linear curve.
+extern MGL_EXPORT const MGLStyleFunctionOption MGLStyleFunctionOptionInterpolationBase __attribute__((unavailable("Use NSExpression instead, applying the mgl_interpolateWithCurveType:parameters:stops: function with a curve type of “exponential” and a non-nil parameter.")));
- This attribute corresponds to the
- <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#function-base"><code>base</code></a>
- function property in the Mapbox Style Specification.
+extern MGL_EXPORT const MGLStyleFunctionOption MGLStyleFunctionOptionDefaultValue __attribute__((unavailable("Use +[NSExpression expressionForConditional:trueExpression:falseExpression:] instead.")));
- This option only applies to functions that use an
- `MGLInterpolationModeExponential` interpolation mode that are assigned to
- interpolatable style layer properties.
- */
-extern MGL_EXPORT const MGLStyleFunctionOption MGLStyleFunctionOptionInterpolationBase;
-
-/**
- An `MGLConstantStyleValue` object that specifies a default value that a style
- function can use when it can't otherwise determine a value.
-
- A default value can be used to set the value of a style layer property that
- is not otherwise set by a function. For example, a source function with a
- `MGLInterpolationModeCategorical` interpolation mode with stops that specify
- color values to use based on a feature's attributes would set any feature
- that does not have attributes that match the stop key values to this
- default value.
-
- This option only applies to `MGLSourceStyleFunction` and
- `MGLCompositeStyleFunction` functions.
- */
-extern MGL_EXPORT const MGLStyleFunctionOption MGLStyleFunctionOptionDefaultValue;
-
-/**
- The modes used to interpolate property values between map zoom level changes
- or over a range of feature attribute values.
- */
typedef NS_ENUM(NSUInteger, MGLInterpolationMode) {
- /**
- Values between two stops are interpolated linearly, or exponentially based on
- the `MGLStyleFunctionOptionInterpolationBase`. A higher interpolation base
- causes the function’s output values to vary exponentially, increasing more rapidly
- towards the high end of the function’s range. The default interpolation base of 1
- creates a linear interpolation. Use exponential interpolation mode to show values
- relative to stop keys.
- */
- MGLInterpolationModeExponential = 0,
- /**
- Values between two stops are not interpolated. Instead, properties are set
- to the value of the stop just less than the function input. Use interval
- interpolation mode to show values that fall within a range.
- */
- MGLInterpolationModeInterval,
- /**
- Values between two stops are not interpolated. Instead, properties are set
- to the value of the stop equal to the function input's key value. Use
- categorical interpolation mode to show values that fit into categories.
- */
- MGLInterpolationModeCategorical,
- /**
- Values between two stops are not interpolated. Instead, for any given feature, the
- style value matches a value in that feature’s attributes dictionary. Use identity
- interpolation mode to show attribute values that can be used as style values.
- */
- MGLInterpolationModeIdentity
-};
-
-/**
- An `MGLStyleValue` object is a generic container for a style attribute value.
- The layout and paint attribute properties of `MGLStyleLayer` can be set to
- `MGLStyleValue` objects.
-
- The `MGLStyleValue` class itself represents a class cluster. Under the hood, a
- particular `MGLStyleValue` object may be either an `MGLConstantStyleValue` to
- represent a constant value or one of the concrete subclasses of
- `MGLStyleFunction` to represent a value function. Do not initialize an
- `MGLStyleValue` object directly; instead, use one of the class factory methods
- to create an `MGLStyleValue` object.
+ MGLInterpolationModeExponential __attribute__((unavailable("Use NSExpression instead, applying the mgl_interpolateWithCurveType:parameters:stops: function with a curve type of “exponential”."))) = 0,
+ MGLInterpolationModeInterval __attribute__((unavailable("Use NSExpression instead, calling the mgl_stepWithMinimum:stops: function."))),
+ MGLInterpolationModeCategorical __attribute__((unavailable("Use NSExpression instead."))),
+ MGLInterpolationModeIdentity __attribute__((unavailable("Use +[NSExpression expressionForKeyPath:] instead.")))
+} __attribute__((unavailable("Use NSExpression instead.")));
- The `MGLStyleValue` class takes a generic parameter `T` that indicates the
- Foundation class being wrapped by this class. Common values for `T` include:
-
- <ul>
- <li><code>NSNumber</code> (for Boolean values and floating-point numbers)</li>
- <li><code>NSValue</code> (for <code>CGVector</code>, <code>NSEdgeInsets</code>, <code>UIEdgeInsets</code>, and enumerations)</li>
- <li><code>NSString</code></li>
- <li><code>NSColor</code> or <code>UIColor</code></li>
- <li><code>NSArray</code></li>
- </ul>
- */
-MGL_EXPORT
+MGL_EXPORT __attribute__((unavailable("Use NSExpression instead.")))
@interface MGLStyleValue<T> : NSObject
-
-#pragma mark Creating a Style Value
-
-/**
- Creates and returns an `MGLConstantStyleValue` object containing a raw value.
-
- @param rawValue The constant value contained by the object.
- @return An `MGLConstantStyleValue` object containing `rawValue`, which is
- treated as a constant value.
- */
-+ (instancetype)valueWithRawValue:(T)rawValue;
-
-#pragma mark Function values
-
-/**
- Creates and returns an `MGLCameraStyleFunction` object representing a linear camera
- function with one or more stops.
-
- @param stops A dictionary associating zoom levels with style values.
- @return An `MGLCameraStyleFunction` object with the given stops.
- */
-+ (instancetype)valueWithStops:(NS_DICTIONARY_OF(NSNumber *, MGLStyleValue<T> *) *)stops __attribute__((deprecated("Use +[MGLStyleValue valueWithInterpolationMode:cameraStops:options:]")));
-
-/**
- Creates and returns an `MGLCameraStyleFunction` object representing a camera
- function with an exponential interpolation base and one or more stops.
-
- @param interpolationBase The exponential base of the interpolation curve.
- @param stops A dictionary associating zoom levels with style values.
- @return An `MGLCameraStyleFunction` object with the given interpolation base and stops.
- */
-+ (instancetype)valueWithInterpolationBase:(CGFloat)interpolationBase stops:(NS_DICTIONARY_OF(NSNumber *, MGLStyleValue<T> *) *)stops __attribute__((deprecated("Use +[MGLStyleValue valueWithInterpolationMode:cameraStops:options:]")));
-
-/**
- Creates and returns an `MGLCameraStyleFunction` object representing a camera function
- with one or more stops.
-
- @param interpolationMode The mode used to interpolate property values between
- map zoom level changes.
- @param cameraStops A dictionary associating zoom levels with style values.
- @param options A dictionary containing `MGLStyleFunctionOption` values that
- specify how a function is applied.
- @return An `MGLCameraStyleFunction` object with the given interpolation mode,
- camera stops, and options.
- */
-+ (instancetype)valueWithInterpolationMode:(MGLInterpolationMode)interpolationMode cameraStops:(NS_DICTIONARY_OF(id, MGLStyleValue<T> *) *)cameraStops options:(nullable NS_DICTIONARY_OF(MGLStyleFunctionOption, id) *)options;
-
-/**
- Creates and returns an `MGLSourceStyleFunction` object representing a source function.
-
- @param interpolationMode The mode used to interpolate property values over a
- range of feature attribute values.
- @param sourceStops A dictionary associating feature attributes with style values.
- @param attributeName Specifies the feature attribute to take as the function
- input.
- @param options A dictionary containing `MGLStyleFunctionOption` values that
- specify how a function is applied.
- @return An `MGLSourceStyleFunction` object with the given interpolation mode,
- source stops, attribute name, and options.
- */
-+ (instancetype)valueWithInterpolationMode:(MGLInterpolationMode)interpolationMode sourceStops:(nullable NS_DICTIONARY_OF(id, MGLStyleValue<T> *) *)sourceStops attributeName:(NSString *)attributeName options:(nullable NS_DICTIONARY_OF(MGLStyleFunctionOption, id) *)options;
-
-/**
- Creates and returns an `MGLCompositeStyleFunction` object representing a composite
- function.
-
- @param interpolationMode The mode used to interpolate property values over a
- range of feature attribute values for each outer zoom level.
- @param compositeStops A dictionary associating feature attributes with style
- values.
- @param attributeName Specifies the feature attribute to take as the function
- input.
- @param options A dictionary containing `MGLStyleFunctionOption` values that
- specify how a function is applied.
- @return An `MGLCompositeStyleFunction` object with the given interpolation mode,
- composite stops, attribute name, and options.
- */
-+ (instancetype)valueWithInterpolationMode:(MGLInterpolationMode)interpolationMode compositeStops:(NS_DICTIONARY_OF(id, NS_DICTIONARY_OF(id, MGLStyleValue<T> *) *) *)compositeStops attributeName:(NSString *)attributeName options:(nullable NS_DICTIONARY_OF(MGLStyleFunctionOption, id) *)options;
-
@end
-/**
- An `MGLConstantStyleValue` object is a generic container for a style attribute
- value that remains constant as the zoom level changes. The layout and paint
- attribute properties of `MGLStyleLayer` objects can be set to
- `MGLConstantStyleValue` objects.
-
- The `MGLConstantStyleValue` class takes a generic parameter `T` that indicates
- the Foundation class being wrapped by this class.
- */
-MGL_EXPORT
+MGL_EXPORT __attribute__((unavailable("Use +[NSExpression expressionForConstantValue:] instead.")))
@interface MGLConstantStyleValue<T> : MGLStyleValue<T>
-
-#pragma mark Creating a Style Constant Value
-
-/**
- Creates and returns an `MGLConstantStyleValue` object containing a raw value.
-
- @param rawValue The constant value contained by the object.
- @return An `MGLConstantStyleValue` object containing `rawValue`, which is
- treated as a constant value.
- */
-+ (instancetype)valueWithRawValue:(T)rawValue;
-
-#pragma mark Initializing a Style Constant Value
-
-- (instancetype)init NS_UNAVAILABLE;
-
-/**
- Returns an `MGLConstantStyleValue` object containing a raw value.
-
- @param rawValue The value contained by the receiver.
- @return An `MGLConstantStyleValue` object containing `rawValue`.
- */
-- (instancetype)initWithRawValue:(T)rawValue NS_DESIGNATED_INITIALIZER;
-
-#pragma mark Accessing the Underlying Value
-
-/**
- The raw value contained by the receiver.
- */
-@property (nonatomic) T rawValue;
-
@end
@compatibility_alias MGLStyleConstantValue MGLConstantStyleValue;
-/**
- An `MGLStyleFunction` is a is an abstract superclass for functions that are
- defined by an `MGLCameraStyleFunction`, `MGLSourceStyleFunction`, or
- `MGLCompositeStyleFunction` object.
-
- Create instances of `MGLCameraStyleFunction`, `MGLSourceStyleFunction`, and
- `MGLCompositeStyleFunction` in order to use `MGLStyleFunction`'s methods. Do
- not create instances of `MGLStyleFunction` directly, and do not create your
- own subclasses of this class.
-
- The `MGLStyleFunction` class takes a generic parameter `T` that indicates the
- Foundation class being wrapped by this class.
- */
-MGL_EXPORT
+MGL_EXPORT __attribute__((unavailable("Use NSExpression instead, calling the mgl_stepWithMinimum:stops: or mgl_interpolateWithCurveType:parameters:stops: function.")))
@interface MGLStyleFunction<T> : MGLStyleValue<T>
-
-#pragma mark Creating a Style Function
-
-/**
- Creates and returns an `MGLCameraStyleFunction` object representing a camera
- function with a linear interpolation curve.
-
- @note Do not create function instances using this method unless it is required
- for backwards compatiblity with your application code.
-
- @param stops A dictionary associating zoom levels with style values.
- @return An `MGLCameraStyleFunction` object with the given stops.
- */
-+ (instancetype)functionWithStops:(NS_DICTIONARY_OF(NSNumber *, MGLStyleValue<T> *) *)stops __attribute__((deprecated("Use +[MGLStyleValue valueWithInterpolationMode:cameraStops:options:]")));
-
-/**
- Creates and returns an `MGLCameraStyleFunction` object representing a camera
- function with an interpolation curve controlled by the provided interpolation
- base.
-
- @note Do not create function instances using this method unless it is required
- for backwards compatiblity with your application code.
-
- @param interpolationBase The exponential base of the interpolation curve.
- @param stops A dictionary associating zoom levels with style values.
- @return An `MGLCameraStyleFunction` object with the given interpolation base and stops.
- */
-+ (instancetype)functionWithInterpolationBase:(CGFloat)interpolationBase stops:(NS_DICTIONARY_OF(NSNumber *, MGLStyleValue<T> *) *)stops __attribute__((deprecated("Use +[MGLStyleValue valueWithInterpolationMode:cameraStops:options:]")));
-
-#pragma mark Initializing a Style Function
-
-/**
- Returns an `MGLStyleFunction` object representing a camera function. If the
- function is set as a style layer property value, it will be interpreted
- as a camera function with an interpolation curve controlled by the provided
- interpolation base.
-
- @note Do not create instances of `MGLStyleFunction` unless it is required for
- backwards compatiblity with your application code. You should create and use
- instances of `MGLCameraStyleFunction` to specify how properties will
- be visualized at different zoom levels.
-
- @param interpolationBase The exponential base of the interpolation curve.
- @param stops A dictionary associating zoom levels with style values.
- @return An `MGLStyleFunction` object with the given interpolation base and stops.
- */
-- (instancetype)initWithInterpolationBase:(CGFloat)interpolationBase stops:(NS_DICTIONARY_OF(NSNumber *, MGLStyleValue<T> *) *)stops __attribute__((deprecated("Use +[MGLStyleValue valueWithInterpolationMode:cameraStops:options:]")));
-
-#pragma mark Accessing the Parameters of a Function
-
-/**
- The modes used to interpolate property values between map zoom level changes or
- over a range of feature attribute values.
- */
-@property (nonatomic) MGLInterpolationMode interpolationMode;
-
-/**
- A dictionary associating zoom levels with style values.
- */
-@property (nonatomic, copy, nullable) NSDictionary *stops;
-
-/**
- The exponential interpolation base of the function’s interpolation curve.
-
- @note This property specifies the exponential base of the interpolation curve
- of `MGLCameraStyleFunction` and `MGLSourceStyleFunction` functions that use
- a `MGLInterpolationModeExponential` `interpolationMode`. Otherwise, it is
- ignored.
- */
-@property (nonatomic) CGFloat interpolationBase;
-
@end
-/**
- An `MGLCameraStyleFunction` is a value function defining a style value that changes
- as the zoom level changes. The layout and paint attribute properties of an
- `MGLStyleLayer` object can be set to `MGLCameraStyleFunction` objects. Use a camera
- function to create the illusion of depth and control data density.
-
- The `MGLCameraStyleFunction` class takes a generic parameter `T` that indicates the
- Foundation class being wrapped by this class.
- */
-MGL_EXPORT
+MGL_EXPORT __attribute__((unavailable("Use NSExpression instead, applying the mgl_stepWithMinimum:stops: or mgl_interpolateWithCurveType:parameters:stops: function to the $zoomLevel variable.")))
@interface MGLCameraStyleFunction<T> : MGLStyleFunction<T>
-
-#pragma mark Creating a Camera Function
-
-/**
- Creates and returns an `MGLCameraStyleFunction` object representing a camera
- function with one or more stops.
-
- @param interpolationMode The mode used to interpolate property values between
- map zoom level changes.
- @param stops A dictionary associating zoom levels with style values.
- @param options A dictionary containing `MGLStyleFunctionOption` values that
- specify how a function is applied.
- @return An `MGLCameraStyleFunction` object with the given interpolation mode,
- camera stops, and options.
- */
-+ (instancetype)functionWithInterpolationMode:(MGLInterpolationMode)interpolationMode stops:(NS_DICTIONARY_OF(id, MGLStyleValue<T> *) *)stops options:(nullable NS_DICTIONARY_OF(MGLStyleFunctionOption, id) *)options;
-
-#pragma mark Accessing the Parameters of a Camera Function
-
-/**
- A dictionary associating zoom levels with style values.
-
- Each of the function’s stops is represented by one key-value pair in the
- dictionary. Each key in the dictionary is an `NSNumber` object containing a
- floating-point zoom level. Each value in the dictionary is an `MGLStyleValue`
- object containing the value of the style attribute when the map is at the
- associated zoom level. An `MGLStyleFunction` object may not be used recursively
- as a stop value.
- */
-@property (nonatomic, copy) NS_DICTIONARY_OF(id, MGLStyleValue<T> *) *stops;
-
@end
-/**
- An `MGLSourceStyleFunction` is a value function defining a style value that
- changes with its properties. The layout and paint attribute properties of an
- `MGLStyleLayer` object can be set to `MGLSourceStyleFunction` objects.
- Use source functions to visually differentate types of features within the same
- layer or create data visualizations.
-
- The `MGLSourceStyleFunction` class takes a generic parameter `T` that indicates the
- Foundation class being wrapped by this class.
- */
-MGL_EXPORT
+MGL_EXPORT __attribute__((unavailable("Use NSExpression instead, applying the mgl_stepWithMinimum:stops: or mgl_interpolateWithCurveType:parameters:stops: function to a key path expression.")))
@interface MGLSourceStyleFunction<T> : MGLStyleFunction<T>
-
-#pragma mark Creating a Source Function
-
-/**
- Creates and returns an `MGLSourceStyleFunction` object representing a source
- function.
-
- @param interpolationMode The mode used to interpolate property values over a
- range of feature attribute values.
- @param stops A dictionary associating feature attributes with style values.
- @param attributeName Specifies the feature attribute to take as the function
- input.
- @param options A dictionary containing `MGLStyleFunctionOption` values that
- specify how a function is applied.
- @return An `MGLSourceStyleFunction` object with the given interpolation mode,
- source stops, attribute name, and options.
-*/
-+ (instancetype)functionWithInterpolationMode:(MGLInterpolationMode)interpolationMode stops:(nullable NS_DICTIONARY_OF(id, MGLStyleValue<T> *) *)stops attributeName:(NSString *)attributeName options:(nullable NS_DICTIONARY_OF(MGLStyleFunctionOption, id) *)options;
-
-#pragma mark Accessing the Parameters of a Source Function
-
-/**
- A string that specifies the feature attribute key whose value be used as the function
- input.
-*/
-@property (nonatomic, copy) NSString *attributeName;
-
-/**
- A dictionary associating attribute values with style values.
-
- Each of the function’s stops is represented by one key-value pair in the
- dictionary. Each key in the dictionary is an object representing a feature
- attribute key or interpolation stop. Each value in the dictionary is an
- `MGLStyleValue` object containing the value to use when the function is given
- the associated attribute key. An `MGLStyleFunction` object may not be used
- recursively as a stop value.
- */
-@property (nonatomic, copy, nullable) NS_DICTIONARY_OF(id, MGLStyleValue<T> *) *stops;
-
-/**
- An `MGLStyleValue` object containing the default value to use when there is
- no input to provide to the function.
- */
-@property (nonatomic, nullable) MGLStyleValue<T> *defaultValue;
-
@end
-/**
- An `MGLCompositeStyleFunction` is a value function defining a style value that
- changes with the feature attributes at each map zoom level. The layout and paint
- attribute properties of an `MGLStyleLayer` object can be set to
- `MGLCompositeStyleFunction` objects. Use composite functions to allow the
- appearance of a map feature to change with both its attributes and the map zoom
- level.
-
- The `MGLCompositeStyleFunction` class takes a generic parameter `T` that indicates the
- Foundation class being wrapped by this class.
- */
-MGL_EXPORT
+MGL_EXPORT __attribute__((unavailable("Use a NSExpression instead with nested mgl_stepWithMinimum:stops: or mgl_interpolateWithCurveType:parameters:stops: function calls.")))
@interface MGLCompositeStyleFunction<T> : MGLStyleFunction<T>
-
-#pragma mark Creating a Composite Function
-
-/**
- Creates and returns an `MGLCompositeStyleFunction` object representing a composite
- function.
-
- @param interpolationMode The mode used to interpolate property values over a
- range of feature attribute values for each outer zoom level.
- @param stops A dictionary associating feature attributes with style values.
- @param attributeName Specifies the feature attribute to take as the function
- input.
- @param options A dictionary containing `MGLStyleFunctionOption` values that
- specify how a function is applied.
- @return An `MGLCompositeStyleFunction` object with the given interpolation mode,
- composite stops, attribute name, and options.
- */
-+ (instancetype)functionWithInterpolationMode:(MGLInterpolationMode)interpolationMode stops:(NS_DICTIONARY_OF(id, NS_DICTIONARY_OF(id, MGLStyleValue<T> *) *) *)stops attributeName:(NSString *)attributeName options:(nullable NS_DICTIONARY_OF(MGLStyleFunctionOption, id) *)options;
-
-#pragma mark Accessing the Parameters of a Composite Function
-
-/**
- A string that specifies the feature attribute key whose value be used as the function
- input.
- */
-@property (nonatomic, copy) NSString *attributeName;
-
-/**
- A dictionary associating attribute values with style values.
-
- Each of the function’s stops is represented by one key-value pair in the
- dictionary. Each key in the dictionary is an `NSNumber` object containing a
- floating-point zoom level. Each value in the dictionary is an inner nested
- dictionary. Each key in the nested dictionary is an object representing a feature
- attribute key or interpolation stop. Each value in the nested dictionary is an
- `MGLStyleValue` object containing the value to use when the function is given
- the associated attribute key. An `MGLStyleFunction` object may not be used
- recursively as a value inside the nested dictionary.
- */
-@property (nonatomic, copy) NS_DICTIONARY_OF(id, NS_DICTIONARY_OF(id, MGLStyleValue<T> *) *) *stops;
-
-/**
- An `MGLStyleValue` object containing the default value to use when there is
- no input to provide to the function.
- */
-@property (nonatomic, nullable) MGLStyleValue<T> *defaultValue;
-
@end
NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLStyleValue.mm b/platform/darwin/src/MGLStyleValue.mm
index 4dd6b550d8..74e1926f79 100644
--- a/platform/darwin/src/MGLStyleValue.mm
+++ b/platform/darwin/src/MGLStyleValue.mm
@@ -1,308 +1,121 @@
#import "MGLStyleValue_Private.h"
+#include <mbgl/style/expression/expression.hpp>
+
const MGLStyleFunctionOption MGLStyleFunctionOptionInterpolationBase = @"MGLStyleFunctionOptionInterpolationBase";
const MGLStyleFunctionOption MGLStyleFunctionOptionDefaultValue = @"MGLStyleFunctionOptionDefaultValue";
-@implementation MGLStyleValue
-
-+ (instancetype)valueWithRawValue:(id)rawValue {
- return [MGLConstantStyleValue valueWithRawValue:rawValue];
-}
-
-+ (instancetype)valueWithInterpolationBase:(CGFloat)interpolationBase stops:(NSDictionary *)stops {
- return [MGLCameraStyleFunction functionWithInterpolationMode:MGLInterpolationModeExponential stops:stops options:@{MGLStyleFunctionOptionInterpolationBase: @(interpolationBase)}];
-}
-
-+ (instancetype)valueWithStops:(NSDictionary *)stops {
- return [MGLCameraStyleFunction functionWithInterpolationMode:MGLInterpolationModeExponential stops:stops options:nil];
-}
-
-+ (instancetype)valueWithInterpolationMode:(MGLInterpolationMode)interpolationMode cameraStops:(NSDictionary *)cameraStops options:(NSDictionary *)options {
- return [MGLCameraStyleFunction functionWithInterpolationMode:interpolationMode stops:cameraStops options:options];
-}
-
-+ (instancetype)valueWithInterpolationMode:(MGLInterpolationMode)interpolationMode sourceStops:(NSDictionary *)sourceStops attributeName:(NSString *)attributeName options:(NSDictionary *)options {
- return [MGLSourceStyleFunction functionWithInterpolationMode:interpolationMode stops:sourceStops attributeName:attributeName options:options];
-}
-
-+ (instancetype)valueWithInterpolationMode:(MGLInterpolationMode)interpolationMode compositeStops:(NSDictionary *)compositeStops attributeName:(NSString *)attributeName options:(NSDictionary *)options {
- return [MGLCompositeStyleFunction functionWithInterpolationMode:interpolationMode stops:compositeStops attributeName:attributeName options:options];
-}
-
-@end
-
-@implementation MGLConstantStyleValue
-
-+ (instancetype)valueWithRawValue:(id)rawValue {
- return [[self alloc] initWithRawValue:rawValue];
+id MGLJSONObjectFromMBGLValue(const mbgl::style::expression::Value &value) {
+ return value.match([](const mbgl::NullValue) -> id {
+ return [NSNull null];
+ }, [](const bool value) {
+ return @(value);
+ }, [](const float value) {
+ return @(value);
+ }, [](const int64_t value) {
+ return @(value);
+ }, [](const double value) {
+ return @(value);
+ }, [](const std::string &value) {
+ return @(value.c_str());
+ }, [](const mbgl::Color &value) {
+ return [MGLColor mgl_colorWithColor:value];
+ }, [](const mbgl::style::Position &value) {
+ std::array<float, 3> spherical = value.getSpherical();
+ MGLSphericalPosition position = MGLSphericalPositionMake(spherical[0], spherical[1], spherical[2]);
+ return [NSValue valueWithMGLSphericalPosition:position];
+ }, [&](const std::vector<mbgl::style::expression::Value> &vector) {
+ NSMutableArray *array = [NSMutableArray arrayWithCapacity:vector.size()];
+ for (auto value : vector) {
+ [array addObject:MGLJSONObjectFromMBGLValue(value)];
+ }
+ return @[@"literal", array];
+ }, [&](const std::unordered_map<std::string, mbgl::style::expression::Value> &map) {
+ NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:map.size()];
+ for (auto &item : map) {
+ dictionary[@(item.first.c_str())] = MGLJSONObjectFromMBGLValue(item.second);
+ }
+ return @[@"literal", dictionary];
+ }, [](const auto &) -> id {
+ return nil;
+ });
}
-- (instancetype)initWithRawValue:(id)rawValue {
- if (self = [super init]) {
- _rawValue = rawValue;
+id MGLJSONObjectFromMBGLExpression(const mbgl::style::expression::Expression &mbglExpression) {
+ using namespace mbgl::style::expression;
+ if (auto literalExpression = dynamic_cast<const Literal *>(&mbglExpression)) {
+ auto result = literalExpression->evaluate({ nullptr });
+ return result ? MGLJSONObjectFromMBGLValue(*result) : nil;
}
- return self;
-}
-
-- (NSString *)description {
- return [self.rawValue description];
-}
-
-- (NSString *)debugDescription {
- return [self.rawValue debugDescription];
-}
-
-- (BOOL)isEqual:(MGLConstantStyleValue *)other {
- return [other isKindOfClass:[self class]] && [other.rawValue isEqual:self.rawValue];
-}
-
-- (NSUInteger)hash {
- return [self.rawValue hash];
-}
-
-@end
-
-@implementation MGLStyleFunction
-
-+ (instancetype)functionWithStops:(NSDictionary *)stops {
- return [MGLCameraStyleFunction functionWithInterpolationMode:MGLInterpolationModeExponential stops:stops options:nil];
-}
-
-+ (instancetype)functionWithInterpolationBase:(CGFloat)interpolationBase stops:(NSDictionary *)stops {
- return [MGLCameraStyleFunction functionWithInterpolationMode:MGLInterpolationModeExponential stops:stops options:@{MGLStyleFunctionOptionInterpolationBase: @(interpolationBase)}];
-}
-
-- (instancetype)init {
- if (self = [super init]) {
- self.interpolationBase = 1.0;
- self.stops = @{};
+ if (auto assertExpression = dynamic_cast<const ArrayAssertion *>(&mbglExpression)) {
+ NSMutableArray *inputs = [NSMutableArray array];
+ assertExpression->eachChild([&](const Expression &child) {
+ [inputs addObject:MGLJSONObjectFromMBGLExpression(child)];
+ });
+ return @[@"literal", inputs.lastObject];
}
- return self;
-}
-
-- (instancetype)initWithInterpolationBase:(CGFloat)interpolationBase stops:(NSDictionary *)stops {
- if (self = [super init]) {
- self.interpolationBase = interpolationBase;
- self.stops = stops;
+ if (auto assertExpression = dynamic_cast<const Assertion *>(&mbglExpression)) {
+ NSMutableArray *inputs = [NSMutableArray array];
+ assertExpression->eachChild([&](const Expression &child) {
+ [inputs addObject:MGLJSONObjectFromMBGLExpression(child)];
+ });
+ return inputs.firstObject;
}
- return self;
-}
-
-- (NSString *)description {
- return [NSString stringWithFormat:@"<%@: %p, \
- stops = %@, \
- interpolationBase = %f>",
- NSStringFromClass([self class]), (void *)self,
- self.stops,
- self.interpolationBase];
-}
-
-- (BOOL)isEqual:(MGLStyleFunction *)other {
- return ([other isKindOfClass:[self class]]
- && [other.stops isEqualToDictionary:self.stops]
- && other.interpolationBase == self.interpolationBase);
-}
-
-- (NSUInteger)hash {
- return self.stops.hash + @(self.interpolationBase).hash;
-}
-
-@end
-
-@implementation MGLCameraStyleFunction
-
-@dynamic stops;
-
-+ (instancetype)functionWithInterpolationMode:(MGLInterpolationMode)interpolationMode stops:(NSDictionary *)stops options:(NSDictionary *)options {
- return [[self alloc] initWithInterpolationMode:interpolationMode stops:stops options:options];
-}
-
-- (instancetype)initWithInterpolationBase:(CGFloat)interpolationBase stops:(NSDictionary *)stops {
- return [self initWithInterpolationMode:MGLInterpolationModeExponential stops:stops options:@{MGLStyleFunctionOptionInterpolationBase: @(interpolationBase)}];
-}
-
-- (instancetype)initWithInterpolationMode:(MGLInterpolationMode)interpolationMode stops:(NSDictionary *)stops options:(NSDictionary *)options {
- if (![stops count]) {
- [NSException raise:NSInvalidArgumentException
- format:@"Camera functions must have at least one stop."];
- return {};
+ if (auto compoundExpression = dynamic_cast<const CompoundExpressionBase *>(&mbglExpression)) {
+ const std::string name = compoundExpression->getName();
+ mbgl::optional<std::size_t> parameterCount = compoundExpression->getParameterCount();
+ NSMutableArray *expressionObject = parameterCount ? [NSMutableArray arrayWithCapacity:*parameterCount + 1] : [NSMutableArray array];
+ [expressionObject addObject:@(name.c_str())];
+ compoundExpression->eachChild([&](const Expression &child) {
+ [expressionObject addObject:MGLJSONObjectFromMBGLExpression(child)];
+ });
+ return expressionObject;
}
-
- if (self = [super init]) {
- self.interpolationMode = interpolationMode;
- self.stops = stops;
-
- if ([options.allKeys containsObject:MGLStyleFunctionOptionInterpolationBase]) {
- if ([options[MGLStyleFunctionOptionInterpolationBase] isKindOfClass:[NSNumber class]]) {
- NSNumber *value = (NSNumber *)options[MGLStyleFunctionOptionInterpolationBase];
- self.interpolationBase = [value floatValue];
- } else {
- [NSException raise:NSInvalidArgumentException format:@"Interpolation base must be an NSNumber that represents a CGFloat."];
- }
+ if (auto stepExpression = dynamic_cast<const Step *>(&mbglExpression)) {
+ auto &input = stepExpression->getInput();
+ NSMutableArray *expressionObject = [NSMutableArray arrayWithObjects:@"step", MGLJSONObjectFromMBGLExpression(*input.get()), nil];
+ stepExpression->eachStop([&](double stop, const Expression &child) {
+ [expressionObject addObject:@(stop)];
+ [expressionObject addObject:MGLJSONObjectFromMBGLExpression(child)];
+ });
+ if ([expressionObject[2] isEqual:@(-INFINITY)]) {
+ [expressionObject removeObjectAtIndex:2];
}
+ return expressionObject;
}
- return self;
-}
-
-- (NSString *)description {
- return [NSString stringWithFormat:@"<%@: %p, \
- interpolationMode = %lu, \
- stops = %@, \
- interpolationBase = %f>",
- NSStringFromClass([self class]), (void *)self,
- (unsigned long)self.interpolationMode,
- self.stops,
- self.interpolationBase];
-}
-
-- (BOOL)isEqual:(MGLCameraStyleFunction *)other {
- return ([other isKindOfClass:[self class]]
- && other.interpolationMode == self.interpolationMode
- && [other.stops isEqualToDictionary:self.stops]
- && other.interpolationBase == self.interpolationBase);
-}
-
-- (NSUInteger)hash {
- return @(self.interpolationMode).hash + self.stops.hash + @(self.interpolationBase).hash;
-}
-
-@end
-
-@implementation MGLSourceStyleFunction
-
-@dynamic stops;
-
-+ (instancetype)functionWithInterpolationMode:(MGLInterpolationMode)interpolationMode stops:(NSDictionary *)stops attributeName:(NSString *)attributeName options:(NSDictionary *)options {
- return [[self alloc] initWithInterpolationMode:interpolationMode stops:stops attributeName:attributeName options:options];
-}
-
-- (instancetype)initWithInterpolationBase:(CGFloat)interpolationBase stops:(NSDictionary *)stops {
- return [self initWithInterpolationMode:MGLInterpolationModeExponential stops:stops attributeName:@"" options:@{MGLStyleFunctionOptionInterpolationBase: @(interpolationBase)}];
-}
-
-- (instancetype)initWithInterpolationMode:(MGLInterpolationMode)interpolationMode stops:(NSDictionary *)stops attributeName:(NSString *)attributeName options:(NSDictionary *)options {
- if (self = [super init]) {
- self.interpolationMode = interpolationMode;
- self.stops = stops;
- _attributeName = attributeName;
-
- if ([options.allKeys containsObject:MGLStyleFunctionOptionDefaultValue]) {
- if ([options[MGLStyleFunctionOptionDefaultValue] isKindOfClass:[MGLStyleValue class]]) {
- MGLStyleValue *value = (MGLStyleValue *)options[MGLStyleFunctionOptionDefaultValue];
- _defaultValue = value;
- } else {
- [NSException raise:NSInvalidArgumentException format:@"Default value must be an MGLStyleValue"];
- }
- }
-
- if ([options.allKeys containsObject:MGLStyleFunctionOptionInterpolationBase]) {
- if ([options[MGLStyleFunctionOptionInterpolationBase] isKindOfClass:[NSNumber class]]) {
- NSNumber *value = (NSNumber *)options[MGLStyleFunctionOptionInterpolationBase];
- self.interpolationBase = [value floatValue];
- } else {
- [NSException raise:NSInvalidArgumentException format:@"Interpolation base must be an NSNumber that represents a CGFloat."];
- }
+ if (auto interpolateExpression = dynamic_cast<const InterpolateBase *>(&mbglExpression)) {
+ auto &interpolator = interpolateExpression->getInterpolator();
+ auto &input = interpolateExpression->getInput();
+ NSArray *interpolatorObject;
+ if (interpolator.is<ExponentialInterpolator>()) {
+ auto exponentialInterpolator = interpolator.get<ExponentialInterpolator>();
+ interpolatorObject = exponentialInterpolator.base == 1 ? @[@"linear"] : @[@"exponential", @(exponentialInterpolator.base)];
+ } else if (interpolator.is<CubicBezierInterpolator>()) {
+ auto cubicBezierInterpolator = interpolator.get<CubicBezierInterpolator>();
+ auto bezier = cubicBezierInterpolator.ub;
+ interpolatorObject = @[
+ @"cubic-bezier",
+ @(bezier.getP1().first), @(bezier.getP1().second),
+ @(bezier.getP2().first), @(bezier.getP2().second),
+ ];
+ } else {
+ NSCAssert(NO, @"Unrecognized interpolator type.");
}
+ NSMutableArray *expressionObject = [NSMutableArray arrayWithObjects:@"interpolate", interpolatorObject, MGLJSONObjectFromMBGLExpression(*input.get()), nil];
+ interpolateExpression->eachStop([&](double stop, const Expression &child) {
+ [expressionObject addObject:@(stop)];
+ [expressionObject addObject:MGLJSONObjectFromMBGLExpression(child)];
+ });
+ return expressionObject;
}
- return self;
-}
-
-- (NSString *)description {
- return [NSString stringWithFormat:@"<%@: %p, \
- interpolationMode = %lu, \
- stops = %@, \
- attributeName = %@, \
- defaultValue = %@, \
- interpolationBase = %f>",
- NSStringFromClass([self class]),
- (void *)self,
- (unsigned long)self.interpolationMode,
- self.stops,
- self.attributeName,
- self.defaultValue,
- self.interpolationBase];
-}
-
-- (BOOL)isEqual:(MGLSourceStyleFunction *)other {
- return ([other isKindOfClass:[self class]]
- && other.interpolationMode == self.interpolationMode
- && ((self.stops && [other.stops isEqualToDictionary:self.stops]) || (!self.stops && !other.stops))
- && [other.attributeName isEqual:self.attributeName]
- && ((self.defaultValue && [other.defaultValue isEqual:self.defaultValue]) || (!self.defaultValue && !other.defaultValue))
- && other.interpolationBase == self.interpolationBase);
-}
-
-- (NSUInteger)hash {
- return @(self.interpolationMode).hash + self.stops.hash + self.attributeName.hash + self.defaultValue.hash + @(self.interpolationBase).hash;
-}
-
-@end
-
-@implementation MGLCompositeStyleFunction
-
-@dynamic stops;
-
-+ (instancetype)functionWithInterpolationMode:(MGLInterpolationMode)interpolationMode stops:(NSDictionary *)stops attributeName:(NSString *)attributeName options:(NSDictionary *)options {
- return [[self alloc] initWithInterpolationMode:interpolationMode stops:stops attributeName:attributeName options:options];
-}
-
-- (instancetype)initWithInterpolationBase:(CGFloat)interpolationBase stops:(NSDictionary *)stops {
- return [self initWithInterpolationMode:MGLInterpolationModeExponential stops:stops attributeName:@"" options:@{MGLStyleFunctionOptionInterpolationBase: @(interpolationBase)}];
-}
-
-- (instancetype)initWithInterpolationMode:(MGLInterpolationMode)interpolationMode stops:(NSDictionary *)stops attributeName:(NSString *)attributeName options:(NSDictionary *)options {
- if (self = [super init]) {
- self.interpolationMode = interpolationMode;
- self.stops = stops;
- _attributeName = attributeName;
-
- if ([options.allKeys containsObject:MGLStyleFunctionOptionDefaultValue]) {
- if ([options[MGLStyleFunctionOptionDefaultValue] isKindOfClass:[MGLStyleValue class]]) {
- MGLStyleValue *value = (MGLStyleValue *)options[MGLStyleFunctionOptionDefaultValue];
- _defaultValue = value;
- } else {
- [NSException raise:NSInvalidArgumentException format:@"Default value must be an MGLStyleValue"];
- }
- }
-
- if ([options.allKeys containsObject:MGLStyleFunctionOptionInterpolationBase]) {
- if ([options[MGLStyleFunctionOptionInterpolationBase] isKindOfClass:[NSNumber class]]) {
- NSNumber *value = (NSNumber *)options[MGLStyleFunctionOptionInterpolationBase];
- self.interpolationBase = [value floatValue];
- } else {
- [NSException raise:NSInvalidArgumentException format:@"Interpolation base must be an NSNumber that represents a CGFloat."];
- }
- }
+ if (auto caseExpression = dynamic_cast<const Case *>(&mbglExpression)) {
+ NSMutableArray *expressionObject = [NSMutableArray arrayWithObject:@"case"];
+ caseExpression->eachChild([&](const Expression &child) {
+ [expressionObject addObject:MGLJSONObjectFromMBGLExpression(child)];
+ });
+ return expressionObject;
}
- return self;
-}
-
-- (NSString *)description {
- return [NSString stringWithFormat:@"<%@: %p, \
- interpolationMode = %lu, \
- stops = %@, \
- attributeName = %@, \
- defaultValue = %@, \
- interpolationBase = %f>",
- NSStringFromClass([self class]), (void *)self,
- (unsigned long)self.interpolationMode,
- self.stops,
- self.attributeName,
- self.defaultValue,
- self.interpolationBase];
-}
-
-- (BOOL)isEqual:(MGLCompositeStyleFunction *)other {
- return ([other isKindOfClass:[self class]]
- && other.interpolationMode == self.interpolationMode
- && [other.stops isEqualToDictionary:self.stops]
- && [other.attributeName isEqual:self.attributeName]
- && ((self.defaultValue && [other.defaultValue isEqual:self.defaultValue]) || (!self.defaultValue && !other.defaultValue))
- && other.interpolationBase == self.interpolationBase);
-}
-
-- (NSUInteger)hash {
- return @(self.interpolationMode).hash + self.stops.hash + self.attributeName.hash + @(self.interpolationBase).hash;
+ NSCAssert(NO, @"Unrecognized expression type.");
+ return nil;
}
-@end
diff --git a/platform/darwin/src/MGLStyleValue_Private.h b/platform/darwin/src/MGLStyleValue_Private.h
index 2155c657bd..5124c29a90 100644
--- a/platform/darwin/src/MGLStyleValue_Private.h
+++ b/platform/darwin/src/MGLStyleValue_Private.h
@@ -4,11 +4,14 @@
#import "NSValue+MGLStyleAttributeAdditions.h"
#import "NSValue+MGLAdditions.h"
+#import "NSExpression+MGLPrivateAdditions.h"
#import "MGLTypes.h"
#import "MGLConversion.h"
+#include <mbgl/style/conversion/property_value.hpp>
#include <mbgl/style/conversion/data_driven_property_value.hpp>
-#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/heatmap_color_property_value.hpp>
+#include <mbgl/style/conversion/position.hpp>
#import <mbgl/style/types.hpp>
#import <mbgl/util/enum.hpp>
@@ -23,166 +26,101 @@
#import "NSColor+MGLAdditions.h"
#endif
+namespace mbgl {
+ namespace style {
+ namespace expression {
+ class Expression;
+ }
+ }
+}
+
+id MGLJSONObjectFromMBGLExpression(const mbgl::style::expression::Expression &mbglExpression);
+
template <typename MBGLType, typename ObjCType, typename MBGLElement = MBGLType, typename ObjCEnum = ObjCType>
class MGLStyleValueTransformer {
public:
-
- // Convert an mbgl property value into an mgl style value
- MGLStyleValue<ObjCType> *toStyleValue(const mbgl::style::PropertyValue<MBGLType> &mbglValue) {
- PropertyValueEvaluator evaluator;
+
+ /// Convert an mbgl property value into an mgl style value
+ NSExpression *toExpression(const mbgl::style::PropertyValue<MBGLType> &mbglValue) {
+ PropertyExpressionEvaluator evaluator;
return mbglValue.evaluate(evaluator);
}
-
- // Convert an mbgl data driven property value into an mgl style value
- MGLStyleValue<ObjCType> *toDataDrivenStyleValue(const mbgl::style::DataDrivenPropertyValue<MBGLType> &mbglValue) {
- PropertyValueEvaluator evaluator;
+
+ /// Convert an mbgl data driven property value into an mgl style value
+ template <typename MBGLEnum = MBGLType, typename MGLEnum = ObjCEnum>
+ NSExpression *toExpression(const mbgl::style::DataDrivenPropertyValue<MBGLEnum> &mbglValue) {
+ PropertyExpressionEvaluator evaluator;
return mbglValue.evaluate(evaluator);
}
-
- // Convert an mbgl property value containing an enum into an mgl style value
- template <typename MBGLEnum = MBGLType,
- class = typename std::enable_if<std::is_enum<MBGLEnum>::value>::type,
- typename MGLEnum = ObjCEnum,
- class = typename std::enable_if<std::is_enum<MGLEnum>::value>::type>
- MGLStyleValue<ObjCType> *toEnumStyleValue(const mbgl::style::PropertyValue<MBGLEnum> &mbglValue) {
- EnumPropertyValueEvaluator<MBGLEnum, ObjCEnum> evaluator;
- return mbglValue.evaluate(evaluator);
+
+ // Convert an mbgl heatmap color property value into an mgl style value
+ NSExpression *toExpression(const mbgl::style::HeatmapColorPropertyValue &mbglValue) {
+ if (mbglValue.isUndefined()) {
+ return nil;
+ }
+ return [NSExpression mgl_expressionWithJSONObject:MGLJSONObjectFromMBGLExpression(mbglValue.getExpression())];
}
- // Convert an mgl style value into a non interpolatable (camera with interval stops) mbgl property value
- mbgl::style::PropertyValue<MBGLType> toPropertyValue(MGLStyleValue<ObjCType> *value) {
- if ([value isKindOfClass:[MGLSourceStyleFunction class]] || [value isKindOfClass:[MGLCompositeStyleFunction class]]) {
- [NSException raise:NSInvalidArgumentException
- format:@"This property can only be set to camera functions. Use +[MGLStyleValue valueWithInterpolationMode:cameraStops:options:] instead."];
+ /**
+ Converts an NSExpression to an mbgl property value.
+ */
+ template <typename MBGLValue>
+ typename std::enable_if_t<!std::is_same<MBGLValue, mbgl::style::HeatmapColorPropertyValue>::value,
+ MBGLValue> toPropertyValue(NSExpression *expression) {
+ if (!expression) {
return {};
}
-
- if ([value isKindOfClass:[MGLConstantStyleValue class]]) {
- return toMBGLConstantValue((MGLConstantStyleValue<ObjCType> *)value);
- } else if ([value isKindOfClass:[MGLCameraStyleFunction class]]) {
- MGLCameraStyleFunction<ObjCType> *cameraStyleFunction = (MGLCameraStyleFunction<ObjCType> *)value;
- // Intentionally ignore the stop type set by the developer becuase non interpolatable property values
- // can only have interval stops. This also allows for backwards compatiblity when the developer uses
- // a deprecated MGLStyleValue method (that used to create an MGLStyleFunction) to create a function
- // for properties that are piecewise-constant (i.e. enum, bool, string)
- return toMBGLIntervalCameraFunction(cameraStyleFunction);
- } else if ([value isMemberOfClass:[MGLStyleFunction class]]) {
- MGLStyleFunction<ObjCType> *styleFunction = (MGLStyleFunction<ObjCType> *)value;
- return toMBGLIntervalCameraFunction(styleFunction);
- } else if (value) {
- [NSException raise:@"MGLAbstractClassException" format:
- @"The style value %@ cannot be applied to the style. "
- @"Make sure the style value was created as a member of a concrete subclass of MGLStyleValue.",
- NSStringFromClass([value class])];
- return {};
- } else {
- return {};
+
+ if (expression.expressionType == NSConstantValueExpressionType) {
+ MBGLType mbglValue;
+ getMBGLValue(expression.constantValue, mbglValue);
+ return mbglValue;
}
- }
-
- // Convert an mgl style value into a non interpolatable (camera with exponential or interval stops) mbgl property value
- mbgl::style::PropertyValue<MBGLType> toInterpolatablePropertyValue(MGLStyleValue<ObjCType> *value) {
- if ([value isKindOfClass:[MGLSourceStyleFunction class]] || [value isKindOfClass:[MGLCompositeStyleFunction class]]) {
- [NSException raise:NSInvalidArgumentException
- format:@"This property can only be set to camera functions. Use +[MGLStyleValue valueWithInterpolationMode:cameraStops:options:] instead."];
- return {};
+ if (expression.expressionType == NSAggregateExpressionType) {
+ MBGLType mbglValue;
+ getMBGLValue(expression.collection, mbglValue);
+ return mbglValue;
}
-
- if ([value isKindOfClass:[MGLConstantStyleValue class]]) {
- return toMBGLConstantValue((MGLConstantStyleValue<ObjCType> *)value);
- } else if ([value isMemberOfClass:[MGLStyleFunction class]]) {
- MGLStyleFunction<ObjCType> *styleFunction = (MGLStyleFunction<ObjCType> *)value;
- return toMBGLExponentialCameraFunction(styleFunction);
- } else if ([value isKindOfClass:[MGLCameraStyleFunction class]]) {
- MGLCameraStyleFunction<ObjCType> *cameraStyleFunction = (MGLCameraStyleFunction<ObjCType> *)value;
- switch (cameraStyleFunction.interpolationMode) {
- case MGLInterpolationModeExponential:
- return toMBGLExponentialCameraFunction(cameraStyleFunction);
- break;
- case MGLInterpolationModeInterval:
- return toMBGLIntervalCameraFunction(cameraStyleFunction);
- break;
- default:
- [NSException raise:NSInvalidArgumentException
- format:@"A camera function must use either exponential or interval stops."];
- break;
- }
- return {};
- } else if (value) {
- [NSException raise:@"MGLAbstractClassException" format:
- @"The style value %@ cannot be applied to the style. "
- @"Make sure the style value was created as a member of a concrete subclass of MGLStyleValue.",
- NSStringFromClass([value class])];
- return {};
- } else {
+
+ NSArray *jsonExpression = expression.mgl_jsonExpressionObject;
+
+ mbgl::style::conversion::Error valueError;
+ auto value = mbgl::style::conversion::convert<MBGLValue>(
+ mbgl::style::conversion::makeConvertible(jsonExpression), valueError);
+ if (!value) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Invalid property value: %@", @(valueError.message.c_str())];
return {};
}
+
+ return *value;
}
-
- // Convert an mgl style value into a mbgl data driven property value
- mbgl::style::DataDrivenPropertyValue<MBGLType> toDataDrivenPropertyValue(MGLStyleValue<ObjCType> *value) {
- if ([value isKindOfClass:[MGLConstantStyleValue class]]) {
- return toMBGLConstantValue((MGLConstantStyleValue<ObjCType> *)value);
- } else if ([value isKindOfClass:[MGLStyleFunction class]]) {
- auto rawValue = toRawStyleSpecValue((MGLStyleFunction<ObjCType> *) value);
- mbgl::style::conversion::Error error;
- auto result = mbgl::style::conversion::convert<mbgl::style::DataDrivenPropertyValue<MBGLType>>(rawValue, error);
- NSCAssert(result, @(error.message.c_str()));
- return *result;
- } else {
+
+ /**
+ Converts an NSExpression to an mbgl property value.
+ */
+ template <typename MBGLValue>
+ typename std::enable_if_t<std::is_same<MBGLValue, mbgl::style::HeatmapColorPropertyValue>::value,
+ MBGLValue> toPropertyValue(NSExpression *expression) {
+ if (!expression) {
return {};
}
- }
-
- // Convert an mgl style value containing an enum into a mbgl property value containing an enum
- template <typename MBGLEnum = MBGLType,
- class = typename std::enable_if<std::is_enum<MBGLEnum>::value>::type,
- typename MGLEnum = ObjCEnum,
- class = typename std::enable_if<std::is_enum<MGLEnum>::value>::type>
- mbgl::style::PropertyValue<MBGLEnum> toEnumPropertyValue(MGLStyleValue<ObjCType> *value) {
- if ([value isKindOfClass:[MGLSourceStyleFunction class]] || [value isKindOfClass:[MGLCompositeStyleFunction class]]) {
+
+ NSArray *jsonExpression = expression.mgl_jsonExpressionObject;
+
+ mbgl::style::conversion::Error valueError;
+ auto value = mbgl::style::conversion::convert<mbgl::style::HeatmapColorPropertyValue>(
+ mbgl::style::conversion::makeConvertible(jsonExpression), valueError);
+ if (!value) {
[NSException raise:NSInvalidArgumentException
- format:@"This property can only be set to camera functions. Use +[MGLStyleValue valueWithInterpolationMode:cameraStops:options:] instead."];
- return {};
- }
-
- if ([value isKindOfClass:[MGLConstantStyleValue class]]) {
- MBGLEnum mbglValue;
- getMBGLValue([(MGLConstantStyleValue<ObjCType> *)value rawValue], mbglValue);
- return mbglValue;
- } else if ([value isKindOfClass:[MGLCameraStyleFunction class]]) {
- MGLCameraStyleFunction<NSValue *> *cameraStyleFunction = (MGLCameraStyleFunction<NSValue *> *)value;
- __block std::map<float, MBGLType> stops = {};
- [cameraStyleFunction.stops enumerateKeysAndObjectsUsingBlock:^(NSNumber * _Nonnull zoomKey, MGLStyleValue<NSValue *> * _Nonnull stopValue, BOOL * _Nonnull stop) {
- NSCAssert([stopValue isKindOfClass:[MGLStyleValue class]], @"Stops should be MGLStyleValues");
- auto mbglStopValue = toEnumPropertyValue(stopValue);
- NSCAssert(mbglStopValue.isConstant(), @"Stops must be constant");
- stops[zoomKey.floatValue] = mbglStopValue.asConstant();
- }];
-
- // Enumerations can only ever use interval stops.
- mbgl::style::IntervalStops<MBGLType> intervalStops = {stops};
-
- mbgl::style::CameraFunction<MBGLType> cameraFunction = {intervalStops};
- return cameraFunction;
- } else if (value) {
- [NSException raise:@"MGLAbstractClassException" format:
- @"The style value %@ cannot be applied to the style. "
- @"Make sure the style value was created as a member of a concrete subclass of MGLStyleValue.",
- NSStringFromClass([value class])];
- return {};
- } else {
+ format:@"Invalid property value: %@", @(valueError.message.c_str())];
return {};
}
+
+ return *value;
}
private: // Private utilities for converting from mgl to mbgl values
-
- MBGLType toMBGLConstantValue(MGLConstantStyleValue<ObjCType> *value) {
- MBGLType mbglValue;
- getMBGLValue(value.rawValue, mbglValue);
- return mbglValue;
- }
/**
As hack to allow converting enum => string values, we accept a second, dummy parameter in
@@ -220,164 +158,6 @@ private: // Private utilities for converting from mgl to mbgl values
return @(color.mgl_color.stringify().c_str());
}
-
- NSObject* toRawStyleSpecValue(MGLStyleFunction<ObjCType>* styleFunction) {
- NSMutableDictionary * rawFunction = [NSMutableDictionary new];
- // interpolationMode => type
- switch (styleFunction.interpolationMode) {
- case MGLInterpolationModeExponential:
- rawFunction[@"type"] = @"exponential";
- break;
- case MGLInterpolationModeInterval:
- rawFunction[@"type"] = @"interval";
- break;
- case MGLInterpolationModeCategorical:
- rawFunction[@"type"] = @"categorical";
- break;
- case MGLInterpolationModeIdentity:
- rawFunction[@"type"] = @"identity";
- break;
- }
-
- // interpolationBase => base
- if (styleFunction.interpolationBase) {
- rawFunction[@"base"] = @(styleFunction.interpolationBase);
- }
-
- // stops and default value
- if ([styleFunction isKindOfClass:[MGLCameraStyleFunction class]]) {
- // zoom-only function (no default value)
- __block NSMutableArray *stops = [[NSMutableArray alloc] init];
- [styleFunction.stops enumerateKeysAndObjectsUsingBlock:^(NSNumber * _Nonnull zoomKey, MGLConstantStyleValue<ObjCType> * _Nonnull outputValue, BOOL * _Nonnull stop) {
- MBGLType dummyMbglValue;
- NSArray *rawStop = @[zoomKey, toRawStyleSpecValue([outputValue rawValue], dummyMbglValue)];
- [stops addObject:rawStop];
- }];
- rawFunction[@"stops"] = stops;
-
- } else if ([styleFunction isKindOfClass:[MGLSourceStyleFunction class]]) {
- auto sourceStyleFunction = (MGLSourceStyleFunction<ObjCType> *)styleFunction;
- rawFunction[@"property"] = sourceStyleFunction.attributeName;
- // property-only function
- __block NSMutableArray *stops = [[NSMutableArray alloc] init];
- [styleFunction.stops enumerateKeysAndObjectsUsingBlock:^(NSObject * _Nonnull propertyKey, MGLConstantStyleValue<ObjCType> * _Nonnull outputValue, BOOL * _Nonnull stop) {
- MBGLType dummyMbglValue;
- NSArray *rawStop = @[propertyKey, toRawStyleSpecValue([outputValue rawValue], dummyMbglValue)];
- [stops addObject:rawStop];
- }];
- rawFunction[@"stops"] = stops;
-
- // defaultValue => default
- if (sourceStyleFunction.defaultValue) {
- NSCAssert([sourceStyleFunction.defaultValue isKindOfClass:[MGLConstantStyleValue class]], @"Default value must be constant");
- MBGLType dummyMbglValue;
- rawFunction[@"default"] = toRawStyleSpecValue([(MGLConstantStyleValue<ObjCType> *)sourceStyleFunction.defaultValue rawValue], dummyMbglValue);
- }
- } else if ([styleFunction isKindOfClass:[MGLCompositeStyleFunction class]]) {
- // zoom-and-property function
- auto compositeStyleFunction = (MGLCompositeStyleFunction<ObjCType> *)styleFunction;
- rawFunction[@"property"] = compositeStyleFunction.attributeName;
-
- __block NSMutableArray *stops = [[NSMutableArray alloc] init];
- [compositeStyleFunction.stops enumerateKeysAndObjectsUsingBlock:^(NSNumber * _Nonnull zoomKey, NSDictionary * _Nonnull stopValue, BOOL * _Nonnull stop) {
- for (NSObject *valueKey in stopValue.allKeys) {
- NSDictionary *stopKey = @{
- @"zoom": zoomKey,
- @"value": valueKey
- };
- MGLConstantStyleValue<ObjCType> *outputValue = stopValue[valueKey];
- NSCAssert([outputValue isKindOfClass:[MGLConstantStyleValue<ObjCType> class]], @"Stop outputs should be MGLConstantStyleValues");
- MBGLType dummyMbglValue;
- NSArray *rawStop = @[stopKey, toRawStyleSpecValue([outputValue rawValue], dummyMbglValue)];
- [stops addObject:rawStop];
- }
- }];
- rawFunction[@"stops"] = stops;
-
- // defaultValue => default
- if (compositeStyleFunction.defaultValue) {
- NSCAssert([compositeStyleFunction.defaultValue isKindOfClass:[MGLConstantStyleValue class]], @"Default value must be constant");
- MBGLType dummyMbglValue;
- rawFunction[@"default"] = toRawStyleSpecValue([(MGLConstantStyleValue<ObjCType> *)compositeStyleFunction.defaultValue rawValue], dummyMbglValue);
- }
- }
-
- return rawFunction;
- }
-
- mbgl::style::CameraFunction<MBGLType> toMBGLExponentialCameraFunction(MGLStyleFunction<ObjCType> *styleFunction) {
- __block std::map<float, MBGLType> stops = {};
- [styleFunction.stops enumerateKeysAndObjectsUsingBlock:^(NSNumber * _Nonnull zoomKey, MGLStyleValue<ObjCType> * _Nonnull stopValue, BOOL * _Nonnull stop) {
- NSCAssert([stopValue isKindOfClass:[MGLStyleValue class]], @"Stops should be MGLStyleValues");
- auto mbglStopValue = toPropertyValue(stopValue);
- NSCAssert(mbglStopValue.isConstant(), @"Stops must be constant");
- stops[zoomKey.floatValue] = mbglStopValue.asConstant();
- }];
-
- // Camera function with Exponential stops
- mbgl::style::ExponentialStops<MBGLType> exponentialStops = {stops, (float)styleFunction.interpolationBase};
- mbgl::style::CameraFunction<MBGLType> cameraFunction = {exponentialStops};
-
- return cameraFunction;
- }
-
- mbgl::style::CameraFunction<MBGLType> toMBGLIntervalCameraFunction(MGLStyleFunction<ObjCType> *styleFunction) {
- __block std::map<float, MBGLType> stops = {};
- [styleFunction.stops enumerateKeysAndObjectsUsingBlock:^(NSNumber * _Nonnull zoomKey, MGLStyleValue<ObjCType> * _Nonnull stopValue, BOOL * _Nonnull stop) {
- NSCAssert([stopValue isKindOfClass:[MGLStyleValue class]], @"Stops should be MGLStyleValues");
- auto mbglStopValue = toPropertyValue(stopValue);
- NSCAssert(mbglStopValue.isConstant(), @"Stops must be constant");
- stops[zoomKey.floatValue] = mbglStopValue.asConstant();
- }];
-
- // Camera function with Interval stops
- mbgl::style::IntervalStops<MBGLType> intervalStops = {stops};
- mbgl::style::CameraFunction<MBGLType> cameraFunction = {intervalStops};
-
- return cameraFunction;
- }
-
- mbgl::style::SourceFunction<MBGLType> toMBGLCategoricalSourceFunction(MGLSourceStyleFunction<ObjCType> *sourceStyleFunction) {
- __block std::map<mbgl::style::CategoricalValue, MBGLType> stops = {};
- [sourceStyleFunction.stops enumerateKeysAndObjectsUsingBlock:^(id categoryKey, MGLStyleValue<ObjCType> *stopValue, BOOL *stop) {
- NSCAssert([stopValue isKindOfClass:[MGLStyleValue class]], @"Stops should be MGLStyleValues");
- auto mbglStopValue = toPropertyValue(stopValue);
- NSCAssert(mbglStopValue.isConstant(), @"Stops must be constant");
-
- if ([categoryKey isKindOfClass:[NSString class]]) {
- const std::string& convertedValueKey = [((NSString *)categoryKey) UTF8String];
- stops[mbgl::style::CategoricalValue(convertedValueKey)] = mbglStopValue.asConstant();
- } else if ([categoryKey isKindOfClass:[NSNumber class]]) {
- NSNumber *key = (NSNumber *)categoryKey;
- if ((strcmp([key objCType], @encode(char)) == 0) ||
- (strcmp([key objCType], @encode(BOOL)) == 0)) {
- stops[mbgl::style::CategoricalValue((bool)[key boolValue])] = mbglStopValue.asConstant();
- } else if (strcmp([key objCType], @encode(double)) == 0 ||
- strcmp([key objCType], @encode(float)) == 0) {
- NSCAssert(mbglStopValue.isConstant(), @"Categorical stop keys must be strings, booleans, or integers");
- } else if ([key compare:@(0)] == NSOrderedDescending ||
- [key compare:@(0)] == NSOrderedSame ||
- [key compare:@(0)] == NSOrderedAscending) {
- stops[mbgl::style::CategoricalValue((int64_t)[key integerValue])] = mbglStopValue.asConstant();
- }
- }
- }];
- mbgl::style::CategoricalStops<MBGLType> categoricalStops = {stops};
- mbgl::style::SourceFunction<MBGLType> sourceFunction = {sourceStyleFunction.attributeName.UTF8String, categoricalStops};
- setDefaultMBGLValue(sourceStyleFunction, sourceFunction);
- return sourceFunction;
- }
-
- void setDefaultMBGLValue(MGLSourceStyleFunction<ObjCType> *sourceStyleFunction, mbgl::style::SourceFunction<MBGLType> &sourceFunction) {
- if (sourceStyleFunction.defaultValue) {
- NSCAssert([sourceStyleFunction.defaultValue isKindOfClass:[MGLConstantStyleValue class]], @"Default value must be constant");
- MBGLType mbglValue;
- id mglValue = [(MGLConstantStyleValue<ObjCType> *)sourceStyleFunction.defaultValue rawValue];
- getMBGLValue(mglValue, mbglValue);
- sourceFunction.defaultValue = mbglValue;
- }
- }
-
// Bool
void getMBGLValue(NSNumber *rawValue, bool &mbglValue) {
mbglValue = !!rawValue.boolValue;
@@ -394,13 +174,28 @@ private: // Private utilities for converting from mgl to mbgl values
}
// Offsets
- void getMBGLValue(NSValue *rawValue, std::array<float, 2> &mbglValue) {
- mbglValue = rawValue.mgl_offsetArrayValue;
+ void getMBGLValue(id rawValue, std::array<float, 2> &mbglValue) {
+ if ([rawValue isKindOfClass:[NSValue class]]) {
+ mbglValue = [rawValue mgl_offsetArrayValue];
+ } else if ([rawValue isKindOfClass:[NSArray class]]) {
+ NSArray *array = (NSArray *)rawValue;
+ getMBGLValue(array[0], mbglValue[0]);
+ getMBGLValue(array[1], mbglValue[1]);
+ }
}
// Padding
- void getMBGLValue(NSValue *rawValue, std::array<float, 4> &mbglValue) {
- mbglValue = rawValue.mgl_paddingArrayValue;
+ void getMBGLValue(id rawValue, std::array<float, 4> &mbglValue) {
+ if ([rawValue isKindOfClass:[NSValue class]]) {
+ mbglValue = [rawValue mgl_paddingArrayValue];
+ } else if ([rawValue isKindOfClass:[NSArray class]]) {
+ NSArray *array = (NSArray *)rawValue;
+ getMBGLValue(array[0], mbglValue[0]);
+ getMBGLValue(array[1], mbglValue[1]);
+ getMBGLValue(array[2], mbglValue[2]);
+ getMBGLValue(array[3], mbglValue[3]);
+ getMBGLValue(array[4], mbglValue[4]);
+ }
}
// Color
@@ -412,8 +207,12 @@ private: // Private utilities for converting from mgl to mbgl values
void getMBGLValue(ObjCType rawValue, std::vector<MBGLElement> &mbglValue) {
mbglValue.reserve(rawValue.count);
for (id obj in rawValue) {
+ id constantObject = obj;
+ if ([obj isKindOfClass:[NSExpression class]] && [obj expressionType] == NSConstantValueExpressionType) {
+ constantObject = [constantObject constantValue];
+ }
MBGLElement mbglElement;
- getMBGLValue(obj, mbglElement);
+ getMBGLValue(constantObject, mbglElement);
mbglValue.push_back(mbglElement);
}
}
@@ -429,11 +228,15 @@ private: // Private utilities for converting from mgl to mbgl values
class = typename std::enable_if<std::is_enum<MBGLEnum>::value>::type,
typename MGLEnum = ObjCEnum,
class = typename std::enable_if<std::is_enum<MGLEnum>::value>::type>
- void getMBGLValue(ObjCType rawValue, MBGLEnum &mbglValue) {
- MGLEnum mglEnum;
- [rawValue getValue:&mglEnum];
- auto str = mbgl::Enum<MGLEnum>::toString(mglEnum);
- mbglValue = *mbgl::Enum<MBGLEnum>::toEnum(str);
+ void getMBGLValue(id rawValue, MBGLEnum &mbglValue) {
+ if ([rawValue isKindOfClass:[NSString class]]) {
+ mbglValue = *mbgl::Enum<MBGLEnum>::toEnum([(NSString *)rawValue UTF8String]);
+ } else {
+ MGLEnum mglEnum;
+ [(NSValue *)rawValue getValue:&mglEnum];
+ auto str = mbgl::Enum<MGLEnum>::toString(mglEnum);
+ mbglValue = *mbgl::Enum<MBGLEnum>::toEnum(str);
+ }
}
private: // Private utilities for converting from mbgl to mgl values
@@ -477,7 +280,7 @@ private: // Private utilities for converting from mbgl to mgl values
static ObjCType toMGLRawStyleValue(const std::vector<MBGLElement> &mbglStopValue) {
NSMutableArray *array = [NSMutableArray arrayWithCapacity:mbglStopValue.size()];
for (const auto &mbglElement: mbglStopValue) {
- [array addObject:toMGLRawStyleValue(mbglElement)];
+ [array addObject:[NSExpression expressionForConstantValue:toMGLRawStyleValue(mbglElement)]];
}
return array;
}
@@ -496,212 +299,49 @@ private: // Private utilities for converting from mbgl to mgl values
return [NSValue value:&mglType withObjCType:@encode(MGLEnum)];
}
- // Converts mbgl stops to an equivilent NSDictionary for mgl
- static NSMutableDictionary *toConvertedStops(const std::map<float, MBGLType> &mbglStops) {
- NSMutableDictionary *stops = [NSMutableDictionary dictionaryWithCapacity:mbglStops.size()];
- for (const auto &mbglStop : mbglStops) {
- auto rawValue = toMGLRawStyleValue(mbglStop.second);
- stops[@(mbglStop.first)] = [MGLStyleValue valueWithRawValue:rawValue];
- }
- return stops;
- }
-
- // Converts mbgl interval stop categorical values to an equivilant object for mgl
- class CategoricalValueVisitor {
- public:
- id operator()(const bool value) {
- return toMGLRawStyleValue(value);
- }
-
- id operator()(const int64_t value) {
- return toMGLRawStyleValue(value);
- }
-
- id operator()(const std::string value) {
- return toMGLRawStyleValue(value);
- }
- };
-
- // Converts all types of mbgl property values containing enumerations into an equivilant mgl style value
- template <typename MBGLEnum = MBGLType, typename MGLEnum = ObjCEnum>
- class EnumPropertyValueEvaluator {
+ /// Converts all types of mbgl property values into an equivalent NSExpression.
+ class PropertyExpressionEvaluator {
public:
- id operator()(const mbgl::style::Undefined) const {
+ NSExpression *operator()(const mbgl::style::Undefined) const {
return nil;
}
- id operator()(const MBGLEnum &value) const {
- auto str = mbgl::Enum<MBGLEnum>::toString(value);
- MGLEnum mglType = *mbgl::Enum<MGLEnum>::toEnum(str);
- return [MGLConstantStyleValue<ObjCType> valueWithRawValue:[NSValue value:&mglType withObjCType:@encode(MGLEnum)]];
- }
-
- id operator()(const mbgl::style::CameraFunction<MBGLEnum> &mbglValue) const {
- CameraFunctionStopsVisitor visitor;
- return apply_visitor(visitor, mbglValue.stops);
- }
- };
-
- // Converts all possible mbgl camera function stops into an equivilant mgl style value
- class CameraFunctionStopsVisitor {
- public:
- id operator()(const mbgl::style::ExponentialStops<MBGLType> &mbglStops) {
- return [MGLCameraStyleFunction functionWithInterpolationMode:MGLInterpolationModeExponential
- stops:toConvertedStops(mbglStops.stops)
- options:@{MGLStyleFunctionOptionInterpolationBase: @(mbglStops.base)}];
- }
-
- id operator()(const mbgl::style::IntervalStops<MBGLType> &mbglStops) {
- return [MGLCameraStyleFunction functionWithInterpolationMode:MGLInterpolationModeInterval
- stops:toConvertedStops(mbglStops.stops)
- options:nil];
- }
- };
-
- // Converts a source function and all possible mbgl source function stops into an equivilant mgl style value
- class SourceFunctionStopsVisitor {
- public:
- id operator()(const mbgl::style::ExponentialStops<MBGLType> &mbglStops) {
- MGLSourceStyleFunction *sourceFunction = [MGLSourceStyleFunction functionWithInterpolationMode:MGLInterpolationModeExponential
- stops:toConvertedStops(mbglStops.stops)
- attributeName:@(mbglFunction.property.c_str())
- options:@{MGLStyleFunctionOptionInterpolationBase: @(mbglStops.base)}];
- if (mbglFunction.defaultValue) {
- sourceFunction.defaultValue = [MGLStyleValue valueWithRawValue:toMGLRawStyleValue(*mbglFunction.defaultValue)];
- }
- return sourceFunction;
- }
-
- id operator()(const mbgl::style::IntervalStops<MBGLType> &mbglStops) {
- MGLSourceStyleFunction *sourceFunction = [MGLSourceStyleFunction functionWithInterpolationMode:MGLInterpolationModeInterval
- stops:toConvertedStops(mbglStops.stops)
- attributeName:@(mbglFunction.property.c_str())
- options:nil];
- if (mbglFunction.defaultValue) {
- sourceFunction.defaultValue = [MGLStyleValue valueWithRawValue:toMGLRawStyleValue(*mbglFunction.defaultValue)];
- }
- return sourceFunction;
- }
-
- id operator()(const mbgl::style::CategoricalStops<MBGLType> &mbglStops) {
- NSMutableDictionary *stops = [NSMutableDictionary dictionaryWithCapacity:mbglStops.stops.size()];
- for (const auto &mbglStop : mbglStops.stops) {
- auto categoricalValue = mbglStop.first;
- auto rawValue = toMGLRawStyleValue(mbglStop.second);
- CategoricalValueVisitor categoricalValueVisitor;
- id stopKey = apply_visitor(categoricalValueVisitor, categoricalValue);
- stops[stopKey] = [MGLStyleValue valueWithRawValue:rawValue];
- }
-
- MGLSourceStyleFunction *sourceFunction = [MGLSourceStyleFunction functionWithInterpolationMode:MGLInterpolationModeCategorical
- stops:stops
- attributeName:@(mbglFunction.property.c_str())
- options:nil];
- if (mbglFunction.defaultValue) {
- sourceFunction.defaultValue = [MGLStyleValue valueWithRawValue:toMGLRawStyleValue(*mbglFunction.defaultValue)];
- }
- return sourceFunction;
-
- }
-
- id operator()(const mbgl::style::IdentityStops<MBGLType> &mbglStops) {
- MGLSourceStyleFunction *sourceFunction = [MGLSourceStyleFunction functionWithInterpolationMode:MGLInterpolationModeIdentity
- stops:nil
- attributeName:@(mbglFunction.property.c_str()) options:nil];
- if (mbglFunction.defaultValue) {
- sourceFunction.defaultValue = [MGLStyleValue valueWithRawValue:toMGLRawStyleValue(*mbglFunction.defaultValue)];
- }
- return sourceFunction;
- }
-
- const mbgl::style::SourceFunction<MBGLType> &mbglFunction;
- };
-
- // Converts a composite function and all possible mbgl stops into an equivilant mgl style value
- class CompositeFunctionStopsVisitor {
- public:
- id operator()(const mbgl::style::CompositeExponentialStops<MBGLType> &mbglStops) {
- NSMutableDictionary *stops = [NSMutableDictionary dictionaryWithCapacity:mbglStops.stops.size()];
- for (auto const& outerStop: mbglStops.stops) {
- stops[@(outerStop.first)] = toConvertedStops(outerStop.second);
- }
- MGLCompositeStyleFunction *compositeFunction = [MGLCompositeStyleFunction functionWithInterpolationMode:MGLInterpolationModeExponential
- stops:stops
- attributeName:@(mbglFunction.property.c_str())
- options:@{MGLStyleFunctionOptionInterpolationBase: @(mbglStops.base)}];
- if (mbglFunction.defaultValue) {
- compositeFunction.defaultValue = [MGLStyleValue valueWithRawValue:toMGLRawStyleValue(*mbglFunction.defaultValue)];
- }
- return compositeFunction;
- }
-
- id operator()(const mbgl::style::CompositeIntervalStops<MBGLType> &mbglStops) {
- NSMutableDictionary *stops = [NSMutableDictionary dictionaryWithCapacity:mbglStops.stops.size()];
- for (auto const& outerStop: mbglStops.stops) {
- stops[@(outerStop.first)] = toConvertedStops(outerStop.second);
- }
- MGLCompositeStyleFunction *compositeFunction = [MGLCompositeStyleFunction functionWithInterpolationMode:MGLInterpolationModeInterval
- stops:stops
- attributeName:@(mbglFunction.property.c_str())
- options:nil];
- if (mbglFunction.defaultValue) {
- compositeFunction.defaultValue = [MGLStyleValue valueWithRawValue:toMGLRawStyleValue(*mbglFunction.defaultValue)];
- }
- return compositeFunction;
- }
-
- id operator()(const mbgl::style::CompositeCategoricalStops<MBGLType> &mbglStops) {
- NSMutableDictionary *stops = [NSMutableDictionary dictionaryWithCapacity:mbglStops.stops.size()];
- for (auto const& outerStop: mbglStops.stops) {
- NSMutableDictionary *innerStops = [NSMutableDictionary dictionaryWithCapacity:outerStop.second.size()];
- for (const auto &mbglStop : outerStop.second) {
- auto categoricalValue = mbglStop.first;
- auto rawValue = toMGLRawStyleValue(mbglStop.second);
- CategoricalValueVisitor categoricalValueVisitor;
- id stopKey = apply_visitor(categoricalValueVisitor, categoricalValue);
- innerStops[stopKey] = [MGLStyleValue valueWithRawValue:rawValue];
- }
- stops[@(outerStop.first)] = innerStops;
- }
-
- MGLCompositeStyleFunction *compositeFunction = [MGLCompositeStyleFunction functionWithInterpolationMode:MGLInterpolationModeCategorical
- stops:stops attributeName:@(mbglFunction.property.c_str())
- options:nil];
- if (mbglFunction.defaultValue) {
- compositeFunction.defaultValue = [MGLStyleValue valueWithRawValue:toMGLRawStyleValue(*mbglFunction.defaultValue)];
+ /**
+ As hack to allow converting enum => string values, we accept a second, dummy parameter in
+ the toRawStyleSpecValue() methods for converting 'atomic' (non-style-function) values.
+ This allows us to use `std::enable_if` to test (at compile time) whether or not MBGLType is an Enum.
+ */
+ template <typename MBGLEnum = MBGLType,
+ class = typename std::enable_if<!std::is_enum<MBGLEnum>::value>::type,
+ typename MGLEnum = ObjCEnum,
+ class = typename std::enable_if<!std::is_enum<MGLEnum>::value>::type>
+ NSExpression *operator()(const MBGLType &value) const {
+ id constantValue = toMGLRawStyleValue(value);
+ if ([constantValue isKindOfClass:[NSArray class]]) {
+ return [NSExpression expressionForAggregate:constantValue];
}
- return compositeFunction;
+ return [NSExpression expressionForConstantValue:constantValue];
}
-
- const mbgl::style::CompositeFunction<MBGLType> &mbglFunction;
- };
-
-
- // Converts all types of mbgl property values that don't contain enumerations into an equivilant mgl style value
- class PropertyValueEvaluator {
- public:
- id operator()(const mbgl::style::Undefined) const {
- return nil;
- }
-
- id operator()(const MBGLType &value) const {
- auto rawValue = toMGLRawStyleValue(value);
- return [MGLConstantStyleValue<ObjCType> valueWithRawValue:rawValue];
+
+ template <typename MBGLEnum = MBGLType,
+ class = typename std::enable_if<std::is_enum<MBGLEnum>::value>::type,
+ typename MGLEnum = ObjCEnum,
+ class = typename std::enable_if<std::is_enum<MGLEnum>::value>::type>
+ NSExpression *operator()(const MBGLEnum &value) const {
+ NSString *constantValue = @(mbgl::Enum<MBGLEnum>::toString(value));
+ return [NSExpression expressionForConstantValue:constantValue];
}
- id operator()(const mbgl::style::CameraFunction<MBGLType> &mbglValue) const {
- CameraFunctionStopsVisitor visitor;
- return apply_visitor(visitor, mbglValue.stops);
+ NSExpression *operator()(const mbgl::style::CameraFunction<MBGLType> &mbglValue) const {
+ return [NSExpression mgl_expressionWithJSONObject:MGLJSONObjectFromMBGLExpression(mbglValue.getExpression())];
}
- id operator()(const mbgl::style::SourceFunction<MBGLType> &mbglValue) const {
- SourceFunctionStopsVisitor visitor { mbglValue };
- return apply_visitor(visitor, mbglValue.stops);
+ NSExpression *operator()(const mbgl::style::SourceFunction<MBGLType> &mbglValue) const {
+ return [NSExpression mgl_expressionWithJSONObject:MGLJSONObjectFromMBGLExpression(mbglValue.getExpression())];
}
- MGLCompositeStyleFunction<ObjCType> * operator()(const mbgl::style::CompositeFunction<MBGLType> &mbglValue) const {
- CompositeFunctionStopsVisitor visitor { mbglValue };
- return apply_visitor(visitor, mbglValue.stops);
+ NSExpression *operator()(const mbgl::style::CompositeFunction<MBGLType> &mbglValue) const {
+ return [NSExpression mgl_expressionWithJSONObject:MGLJSONObjectFromMBGLExpression(mbglValue.getExpression())];
}
};
};
diff --git a/platform/darwin/src/MGLSymbolStyleLayer.h b/platform/darwin/src/MGLSymbolStyleLayer.h
index ffb95dfc73..1017db5442 100644
--- a/platform/darwin/src/MGLSymbolStyleLayer.h
+++ b/platform/darwin/src/MGLSymbolStyleLayer.h
@@ -2,7 +2,6 @@
// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
#import "MGLFoundation.h"
-#import "MGLStyleValue.h"
#import "MGLVectorStyleLayer.h"
NS_ASSUME_NONNULL_BEGIN
@@ -68,14 +67,15 @@ typedef NS_ENUM(NSUInteger, MGLIconPitchAlignment) {
*/
MGLIconPitchAlignmentViewport,
/**
- Automatically matches the value of `iconRotationAlignment`.
+ Automatically matches the value of
+ `MGLSymbolStyleLayer.iconRotationAlignment`.
*/
MGLIconPitchAlignmentAuto,
};
/**
- In combination with `symbolPlacement`, determines the rotation behavior of
- icons.
+ In combination with `MGLSymbolStyleLayer.symbolPlacement`, determines the
+ rotation behavior of icons.
Values of this type are used in the `MGLSymbolStyleLayer.iconRotationAlignment`
property.
@@ -89,7 +89,7 @@ typedef NS_ENUM(NSUInteger, MGLIconRotationAlignment) {
MGLIconRotationAlignmentMap,
/**
Produces icons whose x-axes are aligned with the x-axis of the viewport,
- regardless of the value of `symbolPlacement`.
+ regardless of the value of `MGLSymbolStyleLayer.symbolPlacement`.
*/
MGLIconRotationAlignmentViewport,
/**
@@ -226,14 +226,15 @@ typedef NS_ENUM(NSUInteger, MGLTextPitchAlignment) {
*/
MGLTextPitchAlignmentViewport,
/**
- Automatically matches the value of `textRotationAlignment`.
+ Automatically matches the value of
+ `MGLSymbolStyleLayer.textRotationAlignment`.
*/
MGLTextPitchAlignmentAuto,
};
/**
- In combination with `symbolPlacement`, determines the rotation behavior of the
- individual glyphs forming the text.
+ In combination with `MGLSymbolStyleLayer.symbolPlacement`, determines the
+ rotation behavior of the individual glyphs forming the text.
Values of this type are used in the `MGLSymbolStyleLayer.textRotationAlignment`
property.
@@ -247,7 +248,7 @@ typedef NS_ENUM(NSUInteger, MGLTextRotationAlignment) {
MGLTextRotationAlignmentMap,
/**
Produces glyphs whose x-axes are aligned with the x-axis of the viewport,
- regardless of the value of `symbolPlacement`.
+ regardless of the value of `MGLSymbolStyleLayer.symbolPlacement`.
*/
MGLTextRotationAlignmentViewport,
/**
@@ -281,7 +282,7 @@ typedef NS_ENUM(NSUInteger, MGLTextTransform) {
};
/**
- Controls the translation reference point.
+ Controls the frame of reference for `MGLSymbolStyleLayer.iconTranslation`.
Values of this type are used in the `MGLSymbolStyleLayer.iconTranslationAnchor`
property.
@@ -298,7 +299,7 @@ typedef NS_ENUM(NSUInteger, MGLIconTranslationAnchor) {
};
/**
- Controls the translation reference point.
+ Controls the frame of reference for `MGLSymbolStyleLayer.textTranslation`.
Values of this type are used in the `MGLSymbolStyleLayer.textTranslationAnchor`
property.
@@ -333,12 +334,12 @@ typedef NS_ENUM(NSUInteger, MGLTextTranslationAnchor) {
```swift
let layer = MGLSymbolStyleLayer(identifier: "coffeeshops", source: pois)
layer.sourceLayerIdentifier = "pois"
- layer.iconImageName = MGLStyleValue(rawValue: "coffee")
- layer.iconScale = MGLStyleValue(rawValue: 0.5)
- layer.text = MGLStyleValue(rawValue: "{name}")
- layer.textTranslation = MGLStyleValue(rawValue: NSValue(cgVector: CGVector(dx: 10, dy: 0)))
- layer.textJustification = MGLStyleValue(rawValue: NSValue(mglTextJustification: .left))
- layer.textAnchor = MGLStyleValue(rawValue: NSValue(mglTextAnchor: .left))
+ layer.iconImageName = NSExpression(forConstantValue: "coffee")
+ layer.iconScale = NSExpression(forConstantValue: 0.5)
+ layer.text = NSExpression(forKeyPath: "name")
+ layer.textTranslation = NSExpression(forConstantValue: NSValue(cgVector: CGVector(dx: 10, dy: 0)))
+ layer.textJustification = NSExpression(forConstantValue: "left")
+ layer.textAnchor = NSExpression(forConstantValue: "left")
layer.predicate = NSPredicate(format: "%K == %@", "venue-type", "coffee")
mapView.style?.addLayer(layer)
```
@@ -367,9 +368,8 @@ MGL_EXPORT
If true, the icon will be visible even if it collides with other previously
drawn symbols.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing `NO`. Set this property to `nil` to reset it to
- the default value.
+ The default value of this property is an expression that evaluates to `NO`. Set
+ this property to `nil` to reset it to the default value.
This property is only applied to the style if `iconImageName` is non-`nil`.
Otherwise, it is ignored.
@@ -378,51 +378,62 @@ MGL_EXPORT
href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-icon-allow-overlap"><code>icon-allow-overlap</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of
- `MGLInterpolationModeInterval`
+ * Constant Boolean values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Step functions applied to the `$zoomLevel` variable
+
+ This property does not support applying interpolation functions to the
+ `$zoomLevel` variable or applying interpolation or step functions to feature
+ attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconAllowsOverlap;
+@property (nonatomic, null_resettable) NSExpression *iconAllowsOverlap;
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconAllowOverlap __attribute__((unavailable("Use iconAllowsOverlap instead.")));
+@property (nonatomic, null_resettable) NSExpression *iconAllowOverlap __attribute__((unavailable("Use iconAllowsOverlap instead.")));
/**
Part of the icon placed closest to the anchor.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSValue` object containing `MGLIconAnchorCenter`. Set this property to `nil`
- to reset it to the default value.
+ The default value of this property is an expression that evaluates to `center`.
+ Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `iconImageName` is non-`nil`.
Otherwise, it is ignored.
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- */
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconAnchor;
+ You can set this property to an expression containing any of the following:
+
+ * Constant `MGLIconAnchor` values
+ * Any of the following constant string values:
+ * `center`: The center of the icon is placed closest to the anchor.
+ * `left`: The left side of the icon is placed closest to the anchor.
+ * `right`: The right side of the icon is placed closest to the anchor.
+ * `top`: The top of the icon is placed closest to the anchor.
+ * `bottom`: The bottom of the icon is placed closest to the anchor.
+ * `top-left`: The top left corner of the icon is placed closest to the
+ anchor.
+ * `top-right`: The top right corner of the icon is placed closest to the
+ anchor.
+ * `bottom-left`: The bottom left corner of the icon is placed closest to the
+ anchor.
+ * `bottom-right`: The bottom right corner of the icon is placed closest to
+ the anchor.
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
+ */
+@property (nonatomic, null_resettable) NSExpression *iconAnchor;
/**
If true, other symbols can be visible even if they collide with the icon.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing `NO`. Set this property to `nil` to reset it to
- the default value.
+ The default value of this property is an expression that evaluates to `NO`. Set
+ this property to `nil` to reset it to the default value.
This property is only applied to the style if `iconImageName` is non-`nil`.
Otherwise, it is ignored.
@@ -431,122 +442,115 @@ MGL_EXPORT
href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-icon-ignore-placement"><code>icon-ignore-placement</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of
- `MGLInterpolationModeInterval`
+ * Constant Boolean values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Step functions applied to the `$zoomLevel` variable
+
+ This property does not support applying interpolation functions to the
+ `$zoomLevel` variable or applying interpolation or step functions to feature
+ attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconIgnoresPlacement;
+@property (nonatomic, null_resettable) NSExpression *iconIgnoresPlacement;
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconIgnorePlacement __attribute__((unavailable("Use iconIgnoresPlacement instead.")));
+@property (nonatomic, null_resettable) NSExpression *iconIgnorePlacement __attribute__((unavailable("Use iconIgnoresPlacement instead.")));
/**
- Name of image in sprite to use for drawing an image background. A string with
- {tokens} replaced, referencing the data property to pull from.
+ Name of image in sprite to use for drawing an image background. Within literal
+ values, attribute names enclosed in curly brackets (e.g. `{token}`) are
+ replaced with the value of the named attribute. Expressions do not support this
+ syntax; for equivalent functionality in expressions, use
+ `stringByAppendingString:` and key path expressions.
This attribute corresponds to the <a
href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-icon-image"><code>icon-image</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
+ * Constant string values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSString *> *iconImageName;
+@property (nonatomic, null_resettable) NSExpression *iconImageName;
-@property (nonatomic, null_resettable) MGLStyleValue<NSString *> *iconImage __attribute__((unavailable("Use iconImageName instead.")));
+@property (nonatomic, null_resettable) NSExpression *iconImage __attribute__((unavailable("Use iconImageName instead.")));
#if TARGET_OS_IPHONE
/**
Offset distance of icon from its anchor.
- The default value of this property is an `MGLStyleValue` object containing an
+ The default value of this property is an expression that evaluates to an
`NSValue` object containing a `CGVector` struct set to 0 rightward and 0
downward. Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `iconImageName` is non-`nil`.
Otherwise, it is ignored.
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- */
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconOffset;
+ You can set this property to an expression containing any of the following:
+
+ * Constant `CGVector` values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
+ */
+@property (nonatomic, null_resettable) NSExpression *iconOffset;
#else
/**
Offset distance of icon from its anchor.
- The default value of this property is an `MGLStyleValue` object containing an
+ The default value of this property is an expression that evaluates to an
`NSValue` object containing a `CGVector` struct set to 0 rightward and 0
upward. Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `iconImageName` is non-`nil`.
Otherwise, it is ignored.
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- */
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconOffset;
+ You can set this property to an expression containing any of the following:
+
+ * Constant `CGVector` values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
+ */
+@property (nonatomic, null_resettable) NSExpression *iconOffset;
#endif
/**
If true, text will display without their corresponding icons when the icon
collides with other symbols and the text does not.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing `NO`. Set this property to `nil` to reset it to
- the default value.
+ The default value of this property is an expression that evaluates to `NO`. Set
+ this property to `nil` to reset it to the default value.
This property is only applied to the style if `iconImageName` is non-`nil`, and
`text` is non-`nil`. Otherwise, it is ignored.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
+
+ * Constant Boolean values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Step functions applied to the `$zoomLevel` variable
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of
- `MGLInterpolationModeInterval`
+ This property does not support applying interpolation functions to the
+ `$zoomLevel` variable or applying interpolation or step functions to feature
+ attributes.
*/
-@property (nonatomic, null_resettable, getter=isIconOptional) MGLStyleValue<NSNumber *> *iconOptional;
+@property (nonatomic, null_resettable, getter=isIconOptional) NSExpression *iconOptional;
/**
Size of the additional area around the icon bounding box used for detecting
@@ -554,48 +558,59 @@ MGL_EXPORT
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `2`. Set this property to `nil` to reset
- it to the default value.
+ The default value of this property is an expression that evaluates to the float
+ `2`. Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `iconImageName` is non-`nil`.
Otherwise, it is ignored.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
+
+ This property does not support applying interpolation or step functions to
+ feature attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconPadding;
+@property (nonatomic, null_resettable) NSExpression *iconPadding;
/**
Orientation of icon when map is pitched.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSValue` object containing `MGLIconPitchAlignmentAuto`. Set this property to
- `nil` to reset it to the default value.
+ The default value of this property is an expression that evaluates to `auto`.
+ Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `iconImageName` is non-`nil`.
Otherwise, it is ignored.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
+
+ * Constant `MGLIconPitchAlignment` values
+ * Any of the following constant string values:
+ * `map`: The icon is aligned to the plane of the map.
+ * `viewport`: The icon is aligned to the plane of the viewport.
+ * `auto`: Automatically matches the value of `icon-rotation-alignment`.
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Step functions applied to the `$zoomLevel` variable
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of
- `MGLInterpolationModeInterval`
+ This property does not support applying interpolation functions to the
+ `$zoomLevel` variable or applying interpolation or step functions to feature
+ attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconPitchAlignment;
+@property (nonatomic, null_resettable) NSExpression *iconPitchAlignment;
/**
Rotates the icon clockwise.
This property is measured in degrees.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `0`. Set this property to `nil` to reset
- it to the default value.
+ The default value of this property is an expression that evaluates to the float
+ `0`. Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `iconImageName` is non-`nil`.
Otherwise, it is ignored.
@@ -604,56 +619,61 @@ MGL_EXPORT
href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-icon-rotate"><code>icon-rotate</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconRotation;
+@property (nonatomic, null_resettable) NSExpression *iconRotation;
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconRotate __attribute__((unavailable("Use iconRotation instead.")));
+@property (nonatomic, null_resettable) NSExpression *iconRotate __attribute__((unavailable("Use iconRotation instead.")));
/**
In combination with `symbolPlacement`, determines the rotation behavior of
icons.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSValue` object containing `MGLIconRotationAlignmentAuto`. Set this property
- to `nil` to reset it to the default value.
+ The default value of this property is an expression that evaluates to `auto`.
+ Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `iconImageName` is non-`nil`.
Otherwise, it is ignored.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of
- `MGLInterpolationModeInterval`
+ * Constant `MGLIconRotationAlignment` values
+ * Any of the following constant string values:
+ * `map`: When `symbol-placement` is set to `point`, aligns icons east-west.
+ When `symbol-placement` is set to `line`, aligns icon x-axes with the line.
+ * `viewport`: Produces icons whose x-axes are aligned with the x-axis of the
+ viewport, regardless of the value of `symbol-placement`.
+ * `auto`: When `symbol-placement` is set to `point`, this is equivalent to
+ `viewport`. When `symbol-placement` is set to `line`, this is equivalent to
+ `map`.
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Step functions applied to the `$zoomLevel` variable
+
+ This property does not support applying interpolation functions to the
+ `$zoomLevel` variable or applying interpolation or step functions to feature
+ attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconRotationAlignment;
+@property (nonatomic, null_resettable) NSExpression *iconRotationAlignment;
/**
Scales the original size of the icon by the provided factor. The new point size
- of the image will be the original point size multiplied by `iconSize`. 1 is the
- original size; 3 triples the size of the image.
+ of the image will be the original point size multiplied by `iconScale`. 1 is
+ the original size; 3 triples the size of the image.
This property is measured in factor of the original icon sizes.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `1`. Set this property to `nil` to reset
- it to the default value.
+ The default value of this property is an expression that evaluates to the float
+ `1`. Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `iconImageName` is non-`nil`.
Otherwise, it is ignored.
@@ -662,44 +682,49 @@ MGL_EXPORT
href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-icon-size"><code>icon-size</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconScale;
+@property (nonatomic, null_resettable) NSExpression *iconScale;
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconSize __attribute__((unavailable("Use iconScale instead.")));
+@property (nonatomic, null_resettable) NSExpression *iconSize __attribute__((unavailable("Use iconScale instead.")));
/**
Scales the icon to fit around the associated text.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSValue` object containing `MGLIconTextFitNone`. Set this property to `nil` to
- reset it to the default value.
+ The default value of this property is an expression that evaluates to `none`.
+ Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `iconImageName` is non-`nil`, and
`text` is non-`nil`. Otherwise, it is ignored.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of
- `MGLInterpolationModeInterval`
+ * Constant `MGLIconTextFit` values
+ * Any of the following constant string values:
+ * `none`: The icon is displayed at its intrinsic aspect ratio.
+ * `width`: The icon is scaled in the x-dimension to fit the width of the
+ text.
+ * `height`: The icon is scaled in the y-dimension to fit the height of the
+ text.
+ * `both`: The icon is scaled in both x- and y-dimensions.
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Step functions applied to the `$zoomLevel` variable
+
+ This property does not support applying interpolation functions to the
+ `$zoomLevel` variable or applying interpolation or step functions to feature
+ attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconTextFit;
+@property (nonatomic, null_resettable) NSExpression *iconTextFit;
#if TARGET_OS_IPHONE
/**
@@ -707,142 +732,160 @@ MGL_EXPORT
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an
+ The default value of this property is an expression that evaluates to an
`NSValue` object containing `UIEdgeInsetsZero`. Set this property to `nil` to
reset it to the default value.
This property is only applied to the style if `iconImageName` is non-`nil`, and
- `text` is non-`nil`, and `iconTextFit` is set to an `MGLStyleValue` object
- containing an `NSValue` object containing `MGLIconTextFitBoth`,
- `MGLIconTextFitWidth`, or `MGLIconTextFitHeight`. Otherwise, it is ignored.
+ `text` is non-`nil`, and `iconTextFit` is set to an expression that evaluates
+ to `MGLIconTextFitBoth`, `MGLIconTextFitWidth`, or `MGLIconTextFitHeight`.
+ Otherwise, it is ignored.
+
+ You can set this property to an expression containing any of the following:
- You can set this property to an instance of:
+ * Constant `UIEdgeInsets` values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
+ This property does not support applying interpolation or step functions to
+ feature attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconTextFitPadding;
+@property (nonatomic, null_resettable) NSExpression *iconTextFitPadding;
#else
/**
Size of the additional area added to dimensions determined by `iconTextFit`.
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an
+ The default value of this property is an expression that evaluates to an
`NSValue` object containing `NSEdgeInsetsZero`. Set this property to `nil` to
reset it to the default value.
This property is only applied to the style if `iconImageName` is non-`nil`, and
- `text` is non-`nil`, and `iconTextFit` is set to an `MGLStyleValue` object
- containing an `NSValue` object containing `MGLIconTextFitBoth`,
- `MGLIconTextFitWidth`, or `MGLIconTextFitHeight`. Otherwise, it is ignored.
+ `text` is non-`nil`, and `iconTextFit` is set to an expression that evaluates
+ to `MGLIconTextFitBoth`, `MGLIconTextFitWidth`, or `MGLIconTextFitHeight`.
+ Otherwise, it is ignored.
+
+ You can set this property to an expression containing any of the following:
- You can set this property to an instance of:
+ * Constant `NSEdgeInsets` values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
+ This property does not support applying interpolation or step functions to
+ feature attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconTextFitPadding;
+@property (nonatomic, null_resettable) NSExpression *iconTextFitPadding;
#endif
/**
If true, the icon may be flipped to prevent it from being rendered upside-down.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing `NO`. Set this property to `nil` to reset it to
- the default value.
+ The default value of this property is an expression that evaluates to `NO`. Set
+ this property to `nil` to reset it to the default value.
This property is only applied to the style if `iconImageName` is non-`nil`, and
- `iconRotationAlignment` is set to an `MGLStyleValue` object containing an
- `NSValue` object containing `MGLIconRotationAlignmentMap`, and
- `symbolPlacement` is set to an `MGLStyleValue` object containing an `NSValue`
- object containing `MGLSymbolPlacementLine`. Otherwise, it is ignored.
+ `iconRotationAlignment` is set to an expression that evaluates to `map`, and
+ `symbolPlacement` is set to an expression that evaluates to `line`. Otherwise,
+ it is ignored.
This attribute corresponds to the <a
href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-icon-keep-upright"><code>icon-keep-upright</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of
- `MGLInterpolationModeInterval`
+ * Constant Boolean values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Step functions applied to the `$zoomLevel` variable
+
+ This property does not support applying interpolation functions to the
+ `$zoomLevel` variable or applying interpolation or step functions to feature
+ attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *keepsIconUpright;
+@property (nonatomic, null_resettable) NSExpression *keepsIconUpright;
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconKeepUpright __attribute__((unavailable("Use keepsIconUpright instead.")));
+@property (nonatomic, null_resettable) NSExpression *iconKeepUpright __attribute__((unavailable("Use keepsIconUpright instead.")));
/**
If true, the text may be flipped vertically to prevent it from being rendered
upside-down.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing `YES`. Set this property to `nil` to reset it to
- the default value.
+ The default value of this property is an expression that evaluates to `YES`.
+ Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `text` is non-`nil`, and
- `textRotationAlignment` is set to an `MGLStyleValue` object containing an
- `NSValue` object containing `MGLTextRotationAlignmentMap`, and
- `symbolPlacement` is set to an `MGLStyleValue` object containing an `NSValue`
- object containing `MGLSymbolPlacementLine`. Otherwise, it is ignored.
+ `textRotationAlignment` is set to an expression that evaluates to `map`, and
+ `symbolPlacement` is set to an expression that evaluates to `line`. Otherwise,
+ it is ignored.
This attribute corresponds to the <a
href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-text-keep-upright"><code>text-keep-upright</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
+
+ * Constant Boolean values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Step functions applied to the `$zoomLevel` variable
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of
- `MGLInterpolationModeInterval`
+ This property does not support applying interpolation functions to the
+ `$zoomLevel` variable or applying interpolation or step functions to feature
+ attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *keepsTextUpright;
+@property (nonatomic, null_resettable) NSExpression *keepsTextUpright;
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textKeepUpright __attribute__((unavailable("Use keepsTextUpright instead.")));
+@property (nonatomic, null_resettable) NSExpression *textKeepUpright __attribute__((unavailable("Use keepsTextUpright instead.")));
/**
Maximum angle change between adjacent characters.
This property is measured in degrees.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `45`. Set this property to `nil` to
- reset it to the default value.
+ The default value of this property is an expression that evaluates to the float
+ `45`. Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `text` is non-`nil`, and
- `symbolPlacement` is set to an `MGLStyleValue` object containing an `NSValue`
- object containing `MGLSymbolPlacementLine`. Otherwise, it is ignored.
+ `symbolPlacement` is set to an expression that evaluates to `line`. Otherwise,
+ it is ignored.
This attribute corresponds to the <a
href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-text-max-angle"><code>text-max-angle</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
+
+ This property does not support applying interpolation or step functions to
+ feature attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *maximumTextAngle;
+@property (nonatomic, null_resettable) NSExpression *maximumTextAngle;
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textMaxAngle __attribute__((unavailable("Use maximumTextAngle instead.")));
+@property (nonatomic, null_resettable) NSExpression *textMaxAngle __attribute__((unavailable("Use maximumTextAngle instead.")));
/**
The maximum line width for text wrapping.
This property is measured in ems.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `10`. Set this property to `nil` to
- reset it to the default value.
+ The default value of this property is an expression that evaluates to the float
+ `10`. Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `text` is non-`nil`. Otherwise,
it is ignored.
@@ -851,26 +894,19 @@ MGL_EXPORT
href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-text-max-width"><code>text-max-width</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *maximumTextWidth;
+@property (nonatomic, null_resettable) NSExpression *maximumTextWidth;
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textMaxWidth __attribute__((unavailable("Use maximumTextWidth instead.")));
+@property (nonatomic, null_resettable) NSExpression *textMaxWidth __attribute__((unavailable("Use maximumTextWidth instead.")));
/**
If true, the symbols will not cross tile edges to avoid mutual collisions.
@@ -878,102 +914,111 @@ MGL_EXPORT
prevent collisions, or if it is a point symbol layer placed after a line symbol
layer.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing `NO`. Set this property to `nil` to reset it to
- the default value.
+ The default value of this property is an expression that evaluates to `NO`. Set
+ this property to `nil` to reset it to the default value.
This attribute corresponds to the <a
href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-symbol-avoid-edges"><code>symbol-avoid-edges</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of
- `MGLInterpolationModeInterval`
+ * Constant Boolean values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Step functions applied to the `$zoomLevel` variable
+
+ This property does not support applying interpolation functions to the
+ `$zoomLevel` variable or applying interpolation or step functions to feature
+ attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *symbolAvoidsEdges;
+@property (nonatomic, null_resettable) NSExpression *symbolAvoidsEdges;
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *symbolAvoidEdges __attribute__((unavailable("Use symbolAvoidsEdges instead.")));
+@property (nonatomic, null_resettable) NSExpression *symbolAvoidEdges __attribute__((unavailable("Use symbolAvoidsEdges instead.")));
/**
Label placement relative to its geometry.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSValue` object containing `MGLSymbolPlacementPoint`. Set this property to
- `nil` to reset it to the default value.
+ The default value of this property is an expression that evaluates to `point`.
+ Set this property to `nil` to reset it to the default value.
+
+ You can set this property to an expression containing any of the following:
- You can set this property to an instance of:
+ * Constant `MGLSymbolPlacement` values
+ * Any of the following constant string values:
+ * `point`: The label is placed at the point where the geometry is located.
+ * `line`: The label is placed along the line of the geometry. Can only be
+ used on `LineString` and `Polygon` geometries.
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Step functions applied to the `$zoomLevel` variable
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of
- `MGLInterpolationModeInterval`
+ This property does not support applying interpolation functions to the
+ `$zoomLevel` variable or applying interpolation or step functions to feature
+ attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *symbolPlacement;
+@property (nonatomic, null_resettable) NSExpression *symbolPlacement;
/**
Distance between two symbol anchors.
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `250`. Set this property to `nil` to
- reset it to the default value.
+ The default value of this property is an expression that evaluates to the float
+ `250`. Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `symbolPlacement` is set to an
- `MGLStyleValue` object containing an `NSValue` object containing
- `MGLSymbolPlacementLine`. Otherwise, it is ignored.
+ expression that evaluates to `line`. Otherwise, it is ignored.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
+
+ This property does not support applying interpolation or step functions to
+ feature attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *symbolSpacing;
+@property (nonatomic, null_resettable) NSExpression *symbolSpacing;
/**
- Value to use for a text label. Feature properties are specified using tokens
- like {field_name}. (Token replacement is only supported for literal
- `textField` values--not for property functions.)
+ Value to use for a text label. Within literal values, attribute names enclosed
+ in curly brackets (e.g. `{token}`) are replaced with the value of the named
+ attribute. Expressions do not support this syntax; for equivalent functionality
+ in expressions, use `stringByAppendingString:` and key path expressions.
- The default value of this property is an `MGLStyleValue` object containing the
- empty string. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an expression that evaluates to the empty
+ string. Set this property to `nil` to reset it to the default value.
This attribute corresponds to the <a
href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-text-field"><code>text-field</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
+ * Constant string values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSString *> *text;
+@property (nonatomic, null_resettable) NSExpression *text;
-@property (nonatomic, null_resettable) MGLStyleValue<NSString *> *textField __attribute__((unavailable("Use text instead.")));
+@property (nonatomic, null_resettable) NSExpression *textField __attribute__((unavailable("Use text instead.")));
/**
If true, the text will be visible even if it collides with other previously
drawn symbols.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing `NO`. Set this property to `nil` to reset it to
- the default value.
+ The default value of this property is an expression that evaluates to `NO`. Set
+ this property to `nil` to reset it to the default value.
This property is only applied to the style if `text` is non-`nil`. Otherwise,
it is ignored.
@@ -982,44 +1027,56 @@ MGL_EXPORT
href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-text-allow-overlap"><code>text-allow-overlap</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
+
+ * Constant Boolean values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Step functions applied to the `$zoomLevel` variable
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of
- `MGLInterpolationModeInterval`
+ This property does not support applying interpolation functions to the
+ `$zoomLevel` variable or applying interpolation or step functions to feature
+ attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textAllowsOverlap;
+@property (nonatomic, null_resettable) NSExpression *textAllowsOverlap;
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textAllowOverlap __attribute__((unavailable("Use textAllowsOverlap instead.")));
+@property (nonatomic, null_resettable) NSExpression *textAllowOverlap __attribute__((unavailable("Use textAllowsOverlap instead.")));
/**
Part of the text placed closest to the anchor.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSValue` object containing `MGLTextAnchorCenter`. Set this property to `nil`
- to reset it to the default value.
+ The default value of this property is an expression that evaluates to `center`.
+ Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `text` is non-`nil`. Otherwise,
it is ignored.
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- */
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textAnchor;
+ You can set this property to an expression containing any of the following:
+
+ * Constant `MGLTextAnchor` values
+ * Any of the following constant string values:
+ * `center`: The center of the text is placed closest to the anchor.
+ * `left`: The left side of the text is placed closest to the anchor.
+ * `right`: The right side of the text is placed closest to the anchor.
+ * `top`: The top of the text is placed closest to the anchor.
+ * `bottom`: The bottom of the text is placed closest to the anchor.
+ * `top-left`: The top left corner of the text is placed closest to the
+ anchor.
+ * `top-right`: The top right corner of the text is placed closest to the
+ anchor.
+ * `bottom-left`: The bottom left corner of the text is placed closest to the
+ anchor.
+ * `bottom-right`: The bottom right corner of the text is placed closest to
+ the anchor.
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
+ */
+@property (nonatomic, null_resettable) NSExpression *textAnchor;
/**
An array of font face names used to display the text.
@@ -1034,9 +1091,9 @@ MGL_EXPORT
the text, if the first font lacks a glyph for the character, the next font is
applied as a fallback, and so on.
- The default value of this property is an `MGLStyleValue` object containing the
- array `Open Sans Regular`, `Arial Unicode MS Regular`. Set this property to
- `nil` to reset it to the default value.
+ The default value of this property is an expression that evaluates to the array
+ `Open Sans Regular`, `Arial Unicode MS Regular`. Set this property to `nil` to
+ reset it to the default value.
This property is only applied to the style if `text` is non-`nil`. Otherwise,
it is ignored.
@@ -1045,25 +1102,27 @@ MGL_EXPORT
href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-text-font"><code>text-font</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of
- `MGLInterpolationModeInterval`
+ * Constant array values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSArray<NSString *> *> *textFontNames;
+@property (nonatomic, null_resettable) NSExpression *textFontNames;
-@property (nonatomic, null_resettable) MGLStyleValue<NSArray<NSString *> *> *textFont __attribute__((unavailable("Use textFontNames instead.")));
+@property (nonatomic, null_resettable) NSExpression *textFont __attribute__((unavailable("Use textFontNames instead.")));
/**
Font size.
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `16`. Set this property to `nil` to
- reset it to the default value.
+ The default value of this property is an expression that evaluates to the float
+ `16`. Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `text` is non-`nil`. Otherwise,
it is ignored.
@@ -1072,33 +1131,25 @@ MGL_EXPORT
href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-text-size"><code>text-size</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textFontSize;
+@property (nonatomic, null_resettable) NSExpression *textFontSize;
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textSize __attribute__((unavailable("Use textFontSize instead.")));
+@property (nonatomic, null_resettable) NSExpression *textSize __attribute__((unavailable("Use textFontSize instead.")));
/**
If true, other symbols can be visible even if they collide with the text.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing `NO`. Set this property to `nil` to reset it to
- the default value.
+ The default value of this property is an expression that evaluates to `NO`. Set
+ this property to `nil` to reset it to the default value.
This property is only applied to the style if `text` is non-`nil`. Otherwise,
it is ignored.
@@ -1107,23 +1158,28 @@ MGL_EXPORT
href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-text-ignore-placement"><code>text-ignore-placement</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of
- `MGLInterpolationModeInterval`
+ * Constant Boolean values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Step functions applied to the `$zoomLevel` variable
+
+ This property does not support applying interpolation functions to the
+ `$zoomLevel` variable or applying interpolation or step functions to feature
+ attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textIgnoresPlacement;
+@property (nonatomic, null_resettable) NSExpression *textIgnoresPlacement;
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textIgnorePlacement __attribute__((unavailable("Use textIgnoresPlacement instead.")));
+@property (nonatomic, null_resettable) NSExpression *textIgnorePlacement __attribute__((unavailable("Use textIgnoresPlacement instead.")));
/**
Text justification options.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSValue` object containing `MGLTextJustificationCenter`. Set this property to
- `nil` to reset it to the default value.
+ The default value of this property is an expression that evaluates to `center`.
+ Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `text` is non-`nil`. Otherwise,
it is ignored.
@@ -1132,77 +1188,69 @@ MGL_EXPORT
href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-text-justify"><code>text-justify</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
+ * Constant `MGLTextJustification` values
+ * Any of the following constant string values:
+ * `left`: The text is aligned to the left.
+ * `center`: The text is centered.
+ * `right`: The text is aligned to the right.
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textJustification;
+@property (nonatomic, null_resettable) NSExpression *textJustification;
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textJustify __attribute__((unavailable("Use textJustification instead.")));
+@property (nonatomic, null_resettable) NSExpression *textJustify __attribute__((unavailable("Use textJustification instead.")));
/**
Text tracking amount.
This property is measured in ems.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `0`. Set this property to `nil` to reset
- it to the default value.
+ The default value of this property is an expression that evaluates to the float
+ `0`. Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `text` is non-`nil`. Otherwise,
it is ignored.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textLetterSpacing;
+@property (nonatomic, null_resettable) NSExpression *textLetterSpacing;
/**
Text leading value for multi-line text.
This property is measured in ems.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `1.2`. Set this property to `nil` to
- reset it to the default value.
+ The default value of this property is an expression that evaluates to the float
+ `1.2`. Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `text` is non-`nil`. Otherwise,
it is ignored.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
+
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
+ This property does not support applying interpolation or step functions to
+ feature attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textLineHeight;
+@property (nonatomic, null_resettable) NSExpression *textLineHeight;
#if TARGET_OS_IPHONE
/**
@@ -1210,80 +1258,71 @@ MGL_EXPORT
This property is measured in ems.
- The default value of this property is an `MGLStyleValue` object containing an
+ The default value of this property is an expression that evaluates to an
`NSValue` object containing a `CGVector` struct set to 0 ems rightward and 0
ems downward. Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `text` is non-`nil`. Otherwise,
it is ignored.
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- */
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textOffset;
+ You can set this property to an expression containing any of the following:
+
+ * Constant `CGVector` values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
+ */
+@property (nonatomic, null_resettable) NSExpression *textOffset;
#else
/**
Offset distance of text from its anchor.
This property is measured in ems.
- The default value of this property is an `MGLStyleValue` object containing an
+ The default value of this property is an expression that evaluates to an
`NSValue` object containing a `CGVector` struct set to 0 ems rightward and 0
ems upward. Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `text` is non-`nil`. Otherwise,
it is ignored.
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- */
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textOffset;
+ You can set this property to an expression containing any of the following:
+
+ * Constant `CGVector` values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
+ */
+@property (nonatomic, null_resettable) NSExpression *textOffset;
#endif
/**
If true, icons will display without their corresponding text when the text
collides with other symbols and the icon does not.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing `NO`. Set this property to `nil` to reset it to
- the default value.
+ The default value of this property is an expression that evaluates to `NO`. Set
+ this property to `nil` to reset it to the default value.
This property is only applied to the style if `text` is non-`nil`, and
`iconImageName` is non-`nil`. Otherwise, it is ignored.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
+
+ * Constant Boolean values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Step functions applied to the `$zoomLevel` variable
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of
- `MGLInterpolationModeInterval`
+ This property does not support applying interpolation functions to the
+ `$zoomLevel` variable or applying interpolation or step functions to feature
+ attributes.
*/
-@property (nonatomic, null_resettable, getter=isTextOptional) MGLStyleValue<NSNumber *> *textOptional;
+@property (nonatomic, null_resettable, getter=isTextOptional) NSExpression *textOptional;
/**
Size of the additional area around the text bounding box used for detecting
@@ -1291,48 +1330,59 @@ MGL_EXPORT
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `2`. Set this property to `nil` to reset
- it to the default value.
+ The default value of this property is an expression that evaluates to the float
+ `2`. Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `text` is non-`nil`. Otherwise,
it is ignored.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
+
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
+ This property does not support applying interpolation or step functions to
+ feature attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textPadding;
+@property (nonatomic, null_resettable) NSExpression *textPadding;
/**
Orientation of text when map is pitched.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSValue` object containing `MGLTextPitchAlignmentAuto`. Set this property to
- `nil` to reset it to the default value.
+ The default value of this property is an expression that evaluates to `auto`.
+ Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `text` is non-`nil`. Otherwise,
it is ignored.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of
- `MGLInterpolationModeInterval`
+ * Constant `MGLTextPitchAlignment` values
+ * Any of the following constant string values:
+ * `map`: The text is aligned to the plane of the map.
+ * `viewport`: The text is aligned to the plane of the viewport.
+ * `auto`: Automatically matches the value of `text-rotation-alignment`.
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Step functions applied to the `$zoomLevel` variable
+
+ This property does not support applying interpolation functions to the
+ `$zoomLevel` variable or applying interpolation or step functions to feature
+ attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textPitchAlignment;
+@property (nonatomic, null_resettable) NSExpression *textPitchAlignment;
/**
Rotates the text clockwise.
This property is measured in degrees.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `0`. Set this property to `nil` to reset
- it to the default value.
+ The default value of this property is an expression that evaluates to the float
+ `0`. Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `text` is non-`nil`. Otherwise,
it is ignored.
@@ -1341,135 +1391,99 @@ MGL_EXPORT
href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-text-rotate"><code>text-rotate</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textRotation;
+@property (nonatomic, null_resettable) NSExpression *textRotation;
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textRotate __attribute__((unavailable("Use textRotation instead.")));
+@property (nonatomic, null_resettable) NSExpression *textRotate __attribute__((unavailable("Use textRotation instead.")));
/**
In combination with `symbolPlacement`, determines the rotation behavior of the
individual glyphs forming the text.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSValue` object containing `MGLTextRotationAlignmentAuto`. Set this property
- to `nil` to reset it to the default value.
+ The default value of this property is an expression that evaluates to `auto`.
+ Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `text` is non-`nil`. Otherwise,
it is ignored.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
+
+ * Constant `MGLTextRotationAlignment` values
+ * Any of the following constant string values:
+ * `map`: When `symbol-placement` is set to `point`, aligns text east-west.
+ When `symbol-placement` is set to `line`, aligns text x-axes with the line.
+ * `viewport`: Produces glyphs whose x-axes are aligned with the x-axis of the
+ viewport, regardless of the value of `symbol-placement`.
+ * `auto`: When `symbol-placement` is set to `point`, this is equivalent to
+ `viewport`. When `symbol-placement` is set to `line`, this is equivalent to
+ `map`.
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Step functions applied to the `$zoomLevel` variable
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of
- `MGLInterpolationModeInterval`
+ This property does not support applying interpolation functions to the
+ `$zoomLevel` variable or applying interpolation or step functions to feature
+ attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textRotationAlignment;
+@property (nonatomic, null_resettable) NSExpression *textRotationAlignment;
/**
Specifies how to capitalize text.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSValue` object containing `MGLTextTransformNone`. Set this property to `nil`
- to reset it to the default value.
+ The default value of this property is an expression that evaluates to `none`.
+ Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `text` is non-`nil`. Otherwise,
it is ignored.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
+ * Constant `MGLTextTransform` values
+ * Any of the following constant string values:
+ * `none`: The text is not altered.
+ * `uppercase`: Forces all letters to be displayed in uppercase.
+ * `lowercase`: Forces all letters to be displayed in lowercase.
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textTransform;
+@property (nonatomic, null_resettable) NSExpression *textTransform;
#pragma mark - Accessing the Paint Attributes
-#if TARGET_OS_IPHONE
/**
The tint color to apply to the icon. The `iconImageName` property must be set
to a template image.
- The default value of this property is an `MGLStyleValue` object containing
+ The default value of this property is an expression that evaluates to
`UIColor.blackColor`. Set this property to `nil` to reset it to the default
value.
This property is only applied to the style if `iconImageName` is non-`nil`.
Otherwise, it is ignored.
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- */
-@property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *iconColor;
-#else
-/**
- The tint color to apply to the icon. The `iconImageName` property must be set
- to a template image.
+ You can set this property to an expression containing any of the following:
- The default value of this property is an `MGLStyleValue` object containing
- `NSColor.blackColor`. Set this property to `nil` to reset it to the default
- value.
-
- This property is only applied to the style if `iconImageName` is non-`nil`.
- Otherwise, it is ignored.
-
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- */
-@property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *iconColor;
-#endif
+ * Constant `UIColor` values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
+ */
+@property (nonatomic, null_resettable) NSExpression *iconColor;
/**
The transition affecting any changes to this layer’s `iconColor` property.
@@ -1483,30 +1497,22 @@ MGL_EXPORT
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `0`. Set this property to `nil` to reset
- it to the default value.
+ The default value of this property is an expression that evaluates to the float
+ `0`. Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `iconImageName` is non-`nil`.
Otherwise, it is ignored.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconHaloBlur;
+@property (nonatomic, null_resettable) NSExpression *iconHaloBlur;
/**
The transition affecting any changes to this layer’s `iconHaloBlur` property.
@@ -1515,65 +1521,27 @@ MGL_EXPORT
*/
@property (nonatomic) MGLTransition iconHaloBlurTransition;
-#if TARGET_OS_IPHONE
/**
The color of the icon’s halo. The `iconImageName` property must be set to a
template image.
- The default value of this property is an `MGLStyleValue` object containing
+ The default value of this property is an expression that evaluates to
`UIColor.clearColor`. Set this property to `nil` to reset it to the default
value.
This property is only applied to the style if `iconImageName` is non-`nil`.
Otherwise, it is ignored.
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- */
-@property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *iconHaloColor;
-#else
-/**
- The color of the icon’s halo. The `iconImageName` property must be set to a
- template image.
-
- The default value of this property is an `MGLStyleValue` object containing
- `NSColor.clearColor`. Set this property to `nil` to reset it to the default
- value.
+ You can set this property to an expression containing any of the following:
- This property is only applied to the style if `iconImageName` is non-`nil`.
- Otherwise, it is ignored.
-
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- */
-@property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *iconHaloColor;
-#endif
+ * Constant `UIColor` values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
+ */
+@property (nonatomic, null_resettable) NSExpression *iconHaloColor;
/**
The transition affecting any changes to this layer’s `iconHaloColor` property.
@@ -1587,30 +1555,22 @@ MGL_EXPORT
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `0`. Set this property to `nil` to reset
- it to the default value.
+ The default value of this property is an expression that evaluates to the float
+ `0`. Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `iconImageName` is non-`nil`.
Otherwise, it is ignored.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconHaloWidth;
+@property (nonatomic, null_resettable) NSExpression *iconHaloWidth;
/**
The transition affecting any changes to this layer’s `iconHaloWidth` property.
@@ -1622,30 +1582,22 @@ MGL_EXPORT
/**
The opacity at which the icon will be drawn.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `1`. Set this property to `nil` to reset
- it to the default value.
+ The default value of this property is an expression that evaluates to the float
+ `1`. Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `iconImageName` is non-`nil`.
Otherwise, it is ignored.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconOpacity;
+@property (nonatomic, null_resettable) NSExpression *iconOpacity;
/**
The transition affecting any changes to this layer’s `iconOpacity` property.
@@ -1660,7 +1612,7 @@ MGL_EXPORT
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an
+ The default value of this property is an expression that evaluates to an
`NSValue` object containing a `CGVector` struct set to 0 points rightward and 0
points downward. Set this property to `nil` to reset it to the default value.
@@ -1671,21 +1623,25 @@ MGL_EXPORT
href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-icon-translate"><code>icon-translate</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
+ * Constant `CGVector` values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
+
+ This property does not support applying interpolation or step functions to
+ feature attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconTranslation;
+@property (nonatomic, null_resettable) NSExpression *iconTranslation;
#else
/**
Distance that the icon's anchor is moved from its original placement.
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an
+ The default value of this property is an expression that evaluates to an
`NSValue` object containing a `CGVector` struct set to 0 points rightward and 0
points upward. Set this property to `nil` to reset it to the default value.
@@ -1696,14 +1652,18 @@ MGL_EXPORT
href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-icon-translate"><code>icon-translate</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
+
+ * Constant `CGVector` values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
+ This property does not support applying interpolation or step functions to
+ feature attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconTranslation;
+@property (nonatomic, null_resettable) NSExpression *iconTranslation;
#endif
/**
@@ -1713,14 +1673,13 @@ MGL_EXPORT
*/
@property (nonatomic) MGLTransition iconTranslationTransition;
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconTranslate __attribute__((unavailable("Use iconTranslation instead.")));
+@property (nonatomic, null_resettable) NSExpression *iconTranslate __attribute__((unavailable("Use iconTranslation instead.")));
/**
- Controls the translation reference point.
+ Controls the frame of reference for `iconTranslation`.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSValue` object containing `MGLIconTranslationAnchorMap`. Set this property to
- `nil` to reset it to the default value.
+ The default value of this property is an expression that evaluates to `map`.
+ Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `iconImageName` is non-`nil`, and
`iconTranslation` is non-`nil`. Otherwise, it is ignored.
@@ -1729,73 +1688,45 @@ MGL_EXPORT
href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-icon-translate-anchor"><code>icon-translate-anchor</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
+
+ * Constant `MGLIconTranslationAnchor` values
+ * Any of the following constant string values:
+ * `map`: Icons are translated relative to the map.
+ * `viewport`: Icons are translated relative to the viewport.
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Step functions applied to the `$zoomLevel` variable
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of
- `MGLInterpolationModeInterval`
+ This property does not support applying interpolation functions to the
+ `$zoomLevel` variable or applying interpolation or step functions to feature
+ attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconTranslationAnchor;
+@property (nonatomic, null_resettable) NSExpression *iconTranslationAnchor;
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconTranslateAnchor __attribute__((unavailable("Use iconTranslationAnchor instead.")));
+@property (nonatomic, null_resettable) NSExpression *iconTranslateAnchor __attribute__((unavailable("Use iconTranslationAnchor instead.")));
-#if TARGET_OS_IPHONE
/**
The color with which the text will be drawn.
- The default value of this property is an `MGLStyleValue` object containing
+ The default value of this property is an expression that evaluates to
`UIColor.blackColor`. Set this property to `nil` to reset it to the default
value.
This property is only applied to the style if `text` is non-`nil`. Otherwise,
it is ignored.
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- */
-@property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *textColor;
-#else
-/**
- The color with which the text will be drawn.
+ You can set this property to an expression containing any of the following:
- The default value of this property is an `MGLStyleValue` object containing
- `NSColor.blackColor`. Set this property to `nil` to reset it to the default
- value.
-
- This property is only applied to the style if `text` is non-`nil`. Otherwise,
- it is ignored.
-
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- */
-@property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *textColor;
-#endif
+ * Constant `UIColor` values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
+ */
+@property (nonatomic, null_resettable) NSExpression *textColor;
/**
The transition affecting any changes to this layer’s `textColor` property.
@@ -1809,30 +1740,22 @@ MGL_EXPORT
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `0`. Set this property to `nil` to reset
- it to the default value.
+ The default value of this property is an expression that evaluates to the float
+ `0`. Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `text` is non-`nil`. Otherwise,
it is ignored.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textHaloBlur;
+@property (nonatomic, null_resettable) NSExpression *textHaloBlur;
/**
The transition affecting any changes to this layer’s `textHaloBlur` property.
@@ -1841,63 +1764,26 @@ MGL_EXPORT
*/
@property (nonatomic) MGLTransition textHaloBlurTransition;
-#if TARGET_OS_IPHONE
/**
The color of the text's halo, which helps it stand out from backgrounds.
- The default value of this property is an `MGLStyleValue` object containing
+ The default value of this property is an expression that evaluates to
`UIColor.clearColor`. Set this property to `nil` to reset it to the default
value.
This property is only applied to the style if `text` is non-`nil`. Otherwise,
it is ignored.
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- */
-@property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *textHaloColor;
-#else
-/**
- The color of the text's halo, which helps it stand out from backgrounds.
-
- The default value of this property is an `MGLStyleValue` object containing
- `NSColor.clearColor`. Set this property to `nil` to reset it to the default
- value.
-
- This property is only applied to the style if `text` is non-`nil`. Otherwise,
- it is ignored.
+ You can set this property to an expression containing any of the following:
- You can set this property to an instance of:
-
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- */
-@property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *textHaloColor;
-#endif
+ * Constant `UIColor` values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
+ */
+@property (nonatomic, null_resettable) NSExpression *textHaloColor;
/**
The transition affecting any changes to this layer’s `textHaloColor` property.
@@ -1912,30 +1798,22 @@ MGL_EXPORT
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `0`. Set this property to `nil` to reset
- it to the default value.
+ The default value of this property is an expression that evaluates to the float
+ `0`. Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `text` is non-`nil`. Otherwise,
it is ignored.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textHaloWidth;
+@property (nonatomic, null_resettable) NSExpression *textHaloWidth;
/**
The transition affecting any changes to this layer’s `textHaloWidth` property.
@@ -1947,30 +1825,22 @@ MGL_EXPORT
/**
The opacity at which the text will be drawn.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSNumber` object containing the float `1`. Set this property to `nil` to reset
- it to the default value.
+ The default value of this property is an expression that evaluates to the float
+ `1`. Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `text` is non-`nil`. Otherwise,
it is ignored.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLSourceStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
- * `MGLInterpolationModeIdentity`
- * `MGLCompositeStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
- * `MGLInterpolationModeCategorical`
+ * Constant numeric values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable and/or
+ feature attributes
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textOpacity;
+@property (nonatomic, null_resettable) NSExpression *textOpacity;
/**
The transition affecting any changes to this layer’s `textOpacity` property.
@@ -1985,7 +1855,7 @@ MGL_EXPORT
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an
+ The default value of this property is an expression that evaluates to an
`NSValue` object containing a `CGVector` struct set to 0 points rightward and 0
points downward. Set this property to `nil` to reset it to the default value.
@@ -1996,21 +1866,25 @@ MGL_EXPORT
href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-text-translate"><code>text-translate</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
+ * Constant `CGVector` values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
+
+ This property does not support applying interpolation or step functions to
+ feature attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textTranslation;
+@property (nonatomic, null_resettable) NSExpression *textTranslation;
#else
/**
Distance that the text's anchor is moved from its original placement.
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an
+ The default value of this property is an expression that evaluates to an
`NSValue` object containing a `CGVector` struct set to 0 points rightward and 0
points upward. Set this property to `nil` to reset it to the default value.
@@ -2021,14 +1895,18 @@ MGL_EXPORT
href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-text-translate"><code>text-translate</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
+
+ * Constant `CGVector` values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$zoomLevel` variable
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of:
- * `MGLInterpolationModeExponential`
- * `MGLInterpolationModeInterval`
+ This property does not support applying interpolation or step functions to
+ feature attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textTranslation;
+@property (nonatomic, null_resettable) NSExpression *textTranslation;
#endif
/**
@@ -2038,14 +1916,13 @@ MGL_EXPORT
*/
@property (nonatomic) MGLTransition textTranslationTransition;
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textTranslate __attribute__((unavailable("Use textTranslation instead.")));
+@property (nonatomic, null_resettable) NSExpression *textTranslate __attribute__((unavailable("Use textTranslation instead.")));
/**
- Controls the translation reference point.
+ Controls the frame of reference for `textTranslation`.
- The default value of this property is an `MGLStyleValue` object containing an
- `NSValue` object containing `MGLTextTranslationAnchorMap`. Set this property to
- `nil` to reset it to the default value.
+ The default value of this property is an expression that evaluates to `map`.
+ Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `text` is non-`nil`, and
`textTranslation` is non-`nil`. Otherwise, it is ignored.
@@ -2054,15 +1931,24 @@ MGL_EXPORT
href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-text-translate-anchor"><code>text-translate-anchor</code></a>
layout property in the Mapbox Style Specification.
- You can set this property to an instance of:
+ You can set this property to an expression containing any of the following:
+
+ * Constant `MGLTextTranslationAnchor` values
+ * Any of the following constant string values:
+ * `map`: The text is translated relative to the map.
+ * `viewport`: The text is translated relative to the viewport.
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Step functions applied to the `$zoomLevel` variable
- * `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of
- `MGLInterpolationModeInterval`
+ This property does not support applying interpolation functions to the
+ `$zoomLevel` variable or applying interpolation or step functions to feature
+ attributes.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textTranslationAnchor;
+@property (nonatomic, null_resettable) NSExpression *textTranslationAnchor;
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textTranslateAnchor __attribute__((unavailable("Use textTranslationAnchor instead.")));
+@property (nonatomic, null_resettable) NSExpression *textTranslateAnchor __attribute__((unavailable("Use textTranslationAnchor instead.")));
@end
diff --git a/platform/darwin/src/MGLSymbolStyleLayer.mm b/platform/darwin/src/MGLSymbolStyleLayer.mm
index 1990c82669..24d6643e6c 100644
--- a/platform/darwin/src/MGLSymbolStyleLayer.mm
+++ b/platform/darwin/src/MGLSymbolStyleLayer.mm
@@ -154,754 +154,754 @@ namespace mbgl {
#pragma mark - Accessing the Layout Attributes
-- (void)setIconAllowsOverlap:(MGLStyleValue<NSNumber *> *)iconAllowsOverlap {
+- (void)setIconAllowsOverlap:(NSExpression *)iconAllowsOverlap {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue(iconAllowsOverlap);
+ auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<bool>>(iconAllowsOverlap);
self.rawLayer->setIconAllowOverlap(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)iconAllowsOverlap {
+- (NSExpression *)iconAllowsOverlap {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getIconAllowOverlap();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(self.rawLayer->getDefaultIconAllowOverlap());
+ propertyValue = self.rawLayer->getDefaultIconAllowOverlap();
}
- return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<bool, NSNumber *>().toExpression(propertyValue);
}
-- (void)setIconAllowOverlap:(MGLStyleValue<NSNumber *> *)iconAllowOverlap {
+- (void)setIconAllowOverlap:(NSExpression *)iconAllowOverlap {
}
-- (MGLStyleValue<NSNumber *> *)iconAllowOverlap {
+- (NSExpression *)iconAllowOverlap {
return self.iconAllowsOverlap;
}
-- (void)setIconAnchor:(MGLStyleValue<NSValue *> *)iconAnchor {
+- (void)setIconAnchor:(NSExpression *)iconAnchor {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::style::SymbolAnchorType, NSValue *, mbgl::style::SymbolAnchorType, MGLIconAnchor>().toDataDrivenPropertyValue(iconAnchor);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::style::SymbolAnchorType, NSValue *, mbgl::style::SymbolAnchorType, MGLIconAnchor>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<mbgl::style::SymbolAnchorType>>(iconAnchor);
self.rawLayer->setIconAnchor(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)iconAnchor {
+- (NSExpression *)iconAnchor {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getIconAnchor();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<mbgl::style::SymbolAnchorType, NSValue *, mbgl::style::SymbolAnchorType, MGLIconAnchor>().toDataDrivenStyleValue(self.rawLayer->getDefaultIconAnchor());
+ propertyValue = self.rawLayer->getDefaultIconAnchor();
}
- return MGLStyleValueTransformer<mbgl::style::SymbolAnchorType, NSValue *, mbgl::style::SymbolAnchorType, MGLIconAnchor>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::style::SymbolAnchorType, NSValue *, mbgl::style::SymbolAnchorType, MGLIconAnchor>().toExpression(propertyValue);
}
-- (void)setIconIgnoresPlacement:(MGLStyleValue<NSNumber *> *)iconIgnoresPlacement {
+- (void)setIconIgnoresPlacement:(NSExpression *)iconIgnoresPlacement {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue(iconIgnoresPlacement);
+ auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<bool>>(iconIgnoresPlacement);
self.rawLayer->setIconIgnorePlacement(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)iconIgnoresPlacement {
+- (NSExpression *)iconIgnoresPlacement {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getIconIgnorePlacement();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(self.rawLayer->getDefaultIconIgnorePlacement());
+ propertyValue = self.rawLayer->getDefaultIconIgnorePlacement();
}
- return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<bool, NSNumber *>().toExpression(propertyValue);
}
-- (void)setIconIgnorePlacement:(MGLStyleValue<NSNumber *> *)iconIgnorePlacement {
+- (void)setIconIgnorePlacement:(NSExpression *)iconIgnorePlacement {
}
-- (MGLStyleValue<NSNumber *> *)iconIgnorePlacement {
+- (NSExpression *)iconIgnorePlacement {
return self.iconIgnoresPlacement;
}
-- (void)setIconImageName:(MGLStyleValue<NSString *> *)iconImageName {
+- (void)setIconImageName:(NSExpression *)iconImageName {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<std::string, NSString *>().toDataDrivenPropertyValue(iconImageName);
+ auto mbglValue = MGLStyleValueTransformer<std::string, NSString *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<std::string>>(iconImageName);
self.rawLayer->setIconImage(mbglValue);
}
-- (MGLStyleValue<NSString *> *)iconImageName {
+- (NSExpression *)iconImageName {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getIconImage();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<std::string, NSString *>().toDataDrivenStyleValue(self.rawLayer->getDefaultIconImage());
+ propertyValue = self.rawLayer->getDefaultIconImage();
}
- return MGLStyleValueTransformer<std::string, NSString *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
}
-- (void)setIconImage:(MGLStyleValue<NSString *> *)iconImage {
+- (void)setIconImage:(NSExpression *)iconImage {
}
-- (MGLStyleValue<NSString *> *)iconImage {
+- (NSExpression *)iconImage {
return self.iconImageName;
}
-- (void)setIconOffset:(MGLStyleValue<NSValue *> *)iconOffset {
+- (void)setIconOffset:(NSExpression *)iconOffset {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toDataDrivenPropertyValue(iconOffset);
+ auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<std::array<float, 2>>>(iconOffset);
self.rawLayer->setIconOffset(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)iconOffset {
+- (NSExpression *)iconOffset {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getIconOffset();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toDataDrivenStyleValue(self.rawLayer->getDefaultIconOffset());
+ propertyValue = self.rawLayer->getDefaultIconOffset();
}
- return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toExpression(propertyValue);
}
-- (void)setIconOptional:(MGLStyleValue<NSNumber *> *)iconOptional {
+- (void)setIconOptional:(NSExpression *)iconOptional {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue(iconOptional);
+ auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<bool>>(iconOptional);
self.rawLayer->setIconOptional(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)isIconOptional {
+- (NSExpression *)isIconOptional {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getIconOptional();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(self.rawLayer->getDefaultIconOptional());
+ propertyValue = self.rawLayer->getDefaultIconOptional();
}
- return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<bool, NSNumber *>().toExpression(propertyValue);
}
-- (void)setIconPadding:(MGLStyleValue<NSNumber *> *)iconPadding {
+- (void)setIconPadding:(NSExpression *)iconPadding {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(iconPadding);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(iconPadding);
self.rawLayer->setIconPadding(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)iconPadding {
+- (NSExpression *)iconPadding {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getIconPadding();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultIconPadding());
+ propertyValue = self.rawLayer->getDefaultIconPadding();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
-- (void)setIconPitchAlignment:(MGLStyleValue<NSValue *> *)iconPitchAlignment {
+- (void)setIconPitchAlignment:(NSExpression *)iconPitchAlignment {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLIconPitchAlignment>().toEnumPropertyValue(iconPitchAlignment);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLIconPitchAlignment>().toPropertyValue<mbgl::style::PropertyValue<mbgl::style::AlignmentType>>(iconPitchAlignment);
self.rawLayer->setIconPitchAlignment(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)iconPitchAlignment {
+- (NSExpression *)iconPitchAlignment {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getIconPitchAlignment();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLIconPitchAlignment>().toEnumStyleValue(self.rawLayer->getDefaultIconPitchAlignment());
+ propertyValue = self.rawLayer->getDefaultIconPitchAlignment();
}
- return MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLIconPitchAlignment>().toEnumStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLIconPitchAlignment>().toExpression(propertyValue);
}
-- (void)setIconRotation:(MGLStyleValue<NSNumber *> *)iconRotation {
+- (void)setIconRotation:(NSExpression *)iconRotation {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(iconRotation);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(iconRotation);
self.rawLayer->setIconRotate(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)iconRotation {
+- (NSExpression *)iconRotation {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getIconRotate();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultIconRotate());
+ propertyValue = self.rawLayer->getDefaultIconRotate();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
-- (void)setIconRotate:(MGLStyleValue<NSNumber *> *)iconRotate {
+- (void)setIconRotate:(NSExpression *)iconRotate {
}
-- (MGLStyleValue<NSNumber *> *)iconRotate {
+- (NSExpression *)iconRotate {
return self.iconRotation;
}
-- (void)setIconRotationAlignment:(MGLStyleValue<NSValue *> *)iconRotationAlignment {
+- (void)setIconRotationAlignment:(NSExpression *)iconRotationAlignment {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLIconRotationAlignment>().toEnumPropertyValue(iconRotationAlignment);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLIconRotationAlignment>().toPropertyValue<mbgl::style::PropertyValue<mbgl::style::AlignmentType>>(iconRotationAlignment);
self.rawLayer->setIconRotationAlignment(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)iconRotationAlignment {
+- (NSExpression *)iconRotationAlignment {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getIconRotationAlignment();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLIconRotationAlignment>().toEnumStyleValue(self.rawLayer->getDefaultIconRotationAlignment());
+ propertyValue = self.rawLayer->getDefaultIconRotationAlignment();
}
- return MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLIconRotationAlignment>().toEnumStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLIconRotationAlignment>().toExpression(propertyValue);
}
-- (void)setIconScale:(MGLStyleValue<NSNumber *> *)iconScale {
+- (void)setIconScale:(NSExpression *)iconScale {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(iconScale);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(iconScale);
self.rawLayer->setIconSize(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)iconScale {
+- (NSExpression *)iconScale {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getIconSize();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultIconSize());
+ propertyValue = self.rawLayer->getDefaultIconSize();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
-- (void)setIconSize:(MGLStyleValue<NSNumber *> *)iconSize {
+- (void)setIconSize:(NSExpression *)iconSize {
}
-- (MGLStyleValue<NSNumber *> *)iconSize {
+- (NSExpression *)iconSize {
return self.iconScale;
}
-- (void)setIconTextFit:(MGLStyleValue<NSValue *> *)iconTextFit {
+- (void)setIconTextFit:(NSExpression *)iconTextFit {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::style::IconTextFitType, NSValue *, mbgl::style::IconTextFitType, MGLIconTextFit>().toEnumPropertyValue(iconTextFit);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::style::IconTextFitType, NSValue *, mbgl::style::IconTextFitType, MGLIconTextFit>().toPropertyValue<mbgl::style::PropertyValue<mbgl::style::IconTextFitType>>(iconTextFit);
self.rawLayer->setIconTextFit(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)iconTextFit {
+- (NSExpression *)iconTextFit {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getIconTextFit();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<mbgl::style::IconTextFitType, NSValue *, mbgl::style::IconTextFitType, MGLIconTextFit>().toEnumStyleValue(self.rawLayer->getDefaultIconTextFit());
+ propertyValue = self.rawLayer->getDefaultIconTextFit();
}
- return MGLStyleValueTransformer<mbgl::style::IconTextFitType, NSValue *, mbgl::style::IconTextFitType, MGLIconTextFit>().toEnumStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::style::IconTextFitType, NSValue *, mbgl::style::IconTextFitType, MGLIconTextFit>().toExpression(propertyValue);
}
-- (void)setIconTextFitPadding:(MGLStyleValue<NSValue *> *)iconTextFitPadding {
+- (void)setIconTextFitPadding:(NSExpression *)iconTextFitPadding {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<std::array<float, 4>, NSValue *>().toInterpolatablePropertyValue(iconTextFitPadding);
+ auto mbglValue = MGLStyleValueTransformer<std::array<float, 4>, NSValue *>().toPropertyValue<mbgl::style::PropertyValue<std::array<float, 4>>>(iconTextFitPadding);
self.rawLayer->setIconTextFitPadding(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)iconTextFitPadding {
+- (NSExpression *)iconTextFitPadding {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getIconTextFitPadding();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<std::array<float, 4>, NSValue *>().toStyleValue(self.rawLayer->getDefaultIconTextFitPadding());
+ propertyValue = self.rawLayer->getDefaultIconTextFitPadding();
}
- return MGLStyleValueTransformer<std::array<float, 4>, NSValue *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<std::array<float, 4>, NSValue *>().toExpression(propertyValue);
}
-- (void)setKeepsIconUpright:(MGLStyleValue<NSNumber *> *)keepsIconUpright {
+- (void)setKeepsIconUpright:(NSExpression *)keepsIconUpright {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue(keepsIconUpright);
+ auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<bool>>(keepsIconUpright);
self.rawLayer->setIconKeepUpright(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)keepsIconUpright {
+- (NSExpression *)keepsIconUpright {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getIconKeepUpright();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(self.rawLayer->getDefaultIconKeepUpright());
+ propertyValue = self.rawLayer->getDefaultIconKeepUpright();
}
- return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<bool, NSNumber *>().toExpression(propertyValue);
}
-- (void)setIconKeepUpright:(MGLStyleValue<NSNumber *> *)iconKeepUpright {
+- (void)setIconKeepUpright:(NSExpression *)iconKeepUpright {
}
-- (MGLStyleValue<NSNumber *> *)iconKeepUpright {
+- (NSExpression *)iconKeepUpright {
return self.keepsIconUpright;
}
-- (void)setKeepsTextUpright:(MGLStyleValue<NSNumber *> *)keepsTextUpright {
+- (void)setKeepsTextUpright:(NSExpression *)keepsTextUpright {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue(keepsTextUpright);
+ auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<bool>>(keepsTextUpright);
self.rawLayer->setTextKeepUpright(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)keepsTextUpright {
+- (NSExpression *)keepsTextUpright {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getTextKeepUpright();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(self.rawLayer->getDefaultTextKeepUpright());
+ propertyValue = self.rawLayer->getDefaultTextKeepUpright();
}
- return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<bool, NSNumber *>().toExpression(propertyValue);
}
-- (void)setTextKeepUpright:(MGLStyleValue<NSNumber *> *)textKeepUpright {
+- (void)setTextKeepUpright:(NSExpression *)textKeepUpright {
}
-- (MGLStyleValue<NSNumber *> *)textKeepUpright {
+- (NSExpression *)textKeepUpright {
return self.keepsTextUpright;
}
-- (void)setMaximumTextAngle:(MGLStyleValue<NSNumber *> *)maximumTextAngle {
+- (void)setMaximumTextAngle:(NSExpression *)maximumTextAngle {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(maximumTextAngle);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(maximumTextAngle);
self.rawLayer->setTextMaxAngle(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)maximumTextAngle {
+- (NSExpression *)maximumTextAngle {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getTextMaxAngle();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultTextMaxAngle());
+ propertyValue = self.rawLayer->getDefaultTextMaxAngle();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
-- (void)setTextMaxAngle:(MGLStyleValue<NSNumber *> *)textMaxAngle {
+- (void)setTextMaxAngle:(NSExpression *)textMaxAngle {
}
-- (MGLStyleValue<NSNumber *> *)textMaxAngle {
+- (NSExpression *)textMaxAngle {
return self.maximumTextAngle;
}
-- (void)setMaximumTextWidth:(MGLStyleValue<NSNumber *> *)maximumTextWidth {
+- (void)setMaximumTextWidth:(NSExpression *)maximumTextWidth {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(maximumTextWidth);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(maximumTextWidth);
self.rawLayer->setTextMaxWidth(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)maximumTextWidth {
+- (NSExpression *)maximumTextWidth {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getTextMaxWidth();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextMaxWidth());
+ propertyValue = self.rawLayer->getDefaultTextMaxWidth();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
-- (void)setTextMaxWidth:(MGLStyleValue<NSNumber *> *)textMaxWidth {
+- (void)setTextMaxWidth:(NSExpression *)textMaxWidth {
}
-- (MGLStyleValue<NSNumber *> *)textMaxWidth {
+- (NSExpression *)textMaxWidth {
return self.maximumTextWidth;
}
-- (void)setSymbolAvoidsEdges:(MGLStyleValue<NSNumber *> *)symbolAvoidsEdges {
+- (void)setSymbolAvoidsEdges:(NSExpression *)symbolAvoidsEdges {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue(symbolAvoidsEdges);
+ auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<bool>>(symbolAvoidsEdges);
self.rawLayer->setSymbolAvoidEdges(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)symbolAvoidsEdges {
+- (NSExpression *)symbolAvoidsEdges {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getSymbolAvoidEdges();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(self.rawLayer->getDefaultSymbolAvoidEdges());
+ propertyValue = self.rawLayer->getDefaultSymbolAvoidEdges();
}
- return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<bool, NSNumber *>().toExpression(propertyValue);
}
-- (void)setSymbolAvoidEdges:(MGLStyleValue<NSNumber *> *)symbolAvoidEdges {
+- (void)setSymbolAvoidEdges:(NSExpression *)symbolAvoidEdges {
}
-- (MGLStyleValue<NSNumber *> *)symbolAvoidEdges {
+- (NSExpression *)symbolAvoidEdges {
return self.symbolAvoidsEdges;
}
-- (void)setSymbolPlacement:(MGLStyleValue<NSValue *> *)symbolPlacement {
+- (void)setSymbolPlacement:(NSExpression *)symbolPlacement {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::style::SymbolPlacementType, NSValue *, mbgl::style::SymbolPlacementType, MGLSymbolPlacement>().toEnumPropertyValue(symbolPlacement);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::style::SymbolPlacementType, NSValue *, mbgl::style::SymbolPlacementType, MGLSymbolPlacement>().toPropertyValue<mbgl::style::PropertyValue<mbgl::style::SymbolPlacementType>>(symbolPlacement);
self.rawLayer->setSymbolPlacement(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)symbolPlacement {
+- (NSExpression *)symbolPlacement {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getSymbolPlacement();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<mbgl::style::SymbolPlacementType, NSValue *, mbgl::style::SymbolPlacementType, MGLSymbolPlacement>().toEnumStyleValue(self.rawLayer->getDefaultSymbolPlacement());
+ propertyValue = self.rawLayer->getDefaultSymbolPlacement();
}
- return MGLStyleValueTransformer<mbgl::style::SymbolPlacementType, NSValue *, mbgl::style::SymbolPlacementType, MGLSymbolPlacement>().toEnumStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::style::SymbolPlacementType, NSValue *, mbgl::style::SymbolPlacementType, MGLSymbolPlacement>().toExpression(propertyValue);
}
-- (void)setSymbolSpacing:(MGLStyleValue<NSNumber *> *)symbolSpacing {
+- (void)setSymbolSpacing:(NSExpression *)symbolSpacing {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(symbolSpacing);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(symbolSpacing);
self.rawLayer->setSymbolSpacing(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)symbolSpacing {
+- (NSExpression *)symbolSpacing {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getSymbolSpacing();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultSymbolSpacing());
+ propertyValue = self.rawLayer->getDefaultSymbolSpacing();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
-- (void)setText:(MGLStyleValue<NSString *> *)text {
+- (void)setText:(NSExpression *)text {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<std::string, NSString *>().toDataDrivenPropertyValue(text);
+ auto mbglValue = MGLStyleValueTransformer<std::string, NSString *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<std::string>>(text);
self.rawLayer->setTextField(mbglValue);
}
-- (MGLStyleValue<NSString *> *)text {
+- (NSExpression *)text {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getTextField();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<std::string, NSString *>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextField());
+ propertyValue = self.rawLayer->getDefaultTextField();
}
- return MGLStyleValueTransformer<std::string, NSString *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
}
-- (void)setTextField:(MGLStyleValue<NSString *> *)textField {
+- (void)setTextField:(NSExpression *)textField {
}
-- (MGLStyleValue<NSString *> *)textField {
+- (NSExpression *)textField {
return self.text;
}
-- (void)setTextAllowsOverlap:(MGLStyleValue<NSNumber *> *)textAllowsOverlap {
+- (void)setTextAllowsOverlap:(NSExpression *)textAllowsOverlap {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue(textAllowsOverlap);
+ auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<bool>>(textAllowsOverlap);
self.rawLayer->setTextAllowOverlap(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)textAllowsOverlap {
+- (NSExpression *)textAllowsOverlap {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getTextAllowOverlap();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(self.rawLayer->getDefaultTextAllowOverlap());
+ propertyValue = self.rawLayer->getDefaultTextAllowOverlap();
}
- return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<bool, NSNumber *>().toExpression(propertyValue);
}
-- (void)setTextAllowOverlap:(MGLStyleValue<NSNumber *> *)textAllowOverlap {
+- (void)setTextAllowOverlap:(NSExpression *)textAllowOverlap {
}
-- (MGLStyleValue<NSNumber *> *)textAllowOverlap {
+- (NSExpression *)textAllowOverlap {
return self.textAllowsOverlap;
}
-- (void)setTextAnchor:(MGLStyleValue<NSValue *> *)textAnchor {
+- (void)setTextAnchor:(NSExpression *)textAnchor {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::style::SymbolAnchorType, NSValue *, mbgl::style::SymbolAnchorType, MGLTextAnchor>().toDataDrivenPropertyValue(textAnchor);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::style::SymbolAnchorType, NSValue *, mbgl::style::SymbolAnchorType, MGLTextAnchor>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<mbgl::style::SymbolAnchorType>>(textAnchor);
self.rawLayer->setTextAnchor(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)textAnchor {
+- (NSExpression *)textAnchor {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getTextAnchor();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<mbgl::style::SymbolAnchorType, NSValue *, mbgl::style::SymbolAnchorType, MGLTextAnchor>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextAnchor());
+ propertyValue = self.rawLayer->getDefaultTextAnchor();
}
- return MGLStyleValueTransformer<mbgl::style::SymbolAnchorType, NSValue *, mbgl::style::SymbolAnchorType, MGLTextAnchor>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::style::SymbolAnchorType, NSValue *, mbgl::style::SymbolAnchorType, MGLTextAnchor>().toExpression(propertyValue);
}
-- (void)setTextFontNames:(MGLStyleValue<NSArray<NSString *> *> *)textFontNames {
+- (void)setTextFontNames:(NSExpression *)textFontNames {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<std::vector<std::string>, NSArray<NSString *> *, std::string>().toPropertyValue(textFontNames);
+ auto mbglValue = MGLStyleValueTransformer<std::vector<std::string>, NSArray<NSString *> *, std::string>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<std::vector<std::string>>>(textFontNames);
self.rawLayer->setTextFont(mbglValue);
}
-- (MGLStyleValue<NSArray<NSString *> *> *)textFontNames {
+- (NSExpression *)textFontNames {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getTextFont();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<std::vector<std::string>, NSArray<NSString *> *, std::string>().toStyleValue(self.rawLayer->getDefaultTextFont());
+ propertyValue = self.rawLayer->getDefaultTextFont();
}
- return MGLStyleValueTransformer<std::vector<std::string>, NSArray<NSString *> *, std::string>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<std::vector<std::string>, NSArray<NSString *> *, std::string>().toExpression(propertyValue);
}
-- (void)setTextFont:(MGLStyleValue<NSArray<NSString *> *> *)textFont {
+- (void)setTextFont:(NSExpression *)textFont {
}
-- (MGLStyleValue<NSArray<NSString *> *> *)textFont {
+- (NSExpression *)textFont {
return self.textFontNames;
}
-- (void)setTextFontSize:(MGLStyleValue<NSNumber *> *)textFontSize {
+- (void)setTextFontSize:(NSExpression *)textFontSize {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(textFontSize);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(textFontSize);
self.rawLayer->setTextSize(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)textFontSize {
+- (NSExpression *)textFontSize {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getTextSize();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextSize());
+ propertyValue = self.rawLayer->getDefaultTextSize();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
-- (void)setTextSize:(MGLStyleValue<NSNumber *> *)textSize {
+- (void)setTextSize:(NSExpression *)textSize {
}
-- (MGLStyleValue<NSNumber *> *)textSize {
+- (NSExpression *)textSize {
return self.textFontSize;
}
-- (void)setTextIgnoresPlacement:(MGLStyleValue<NSNumber *> *)textIgnoresPlacement {
+- (void)setTextIgnoresPlacement:(NSExpression *)textIgnoresPlacement {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue(textIgnoresPlacement);
+ auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<bool>>(textIgnoresPlacement);
self.rawLayer->setTextIgnorePlacement(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)textIgnoresPlacement {
+- (NSExpression *)textIgnoresPlacement {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getTextIgnorePlacement();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(self.rawLayer->getDefaultTextIgnorePlacement());
+ propertyValue = self.rawLayer->getDefaultTextIgnorePlacement();
}
- return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<bool, NSNumber *>().toExpression(propertyValue);
}
-- (void)setTextIgnorePlacement:(MGLStyleValue<NSNumber *> *)textIgnorePlacement {
+- (void)setTextIgnorePlacement:(NSExpression *)textIgnorePlacement {
}
-- (MGLStyleValue<NSNumber *> *)textIgnorePlacement {
+- (NSExpression *)textIgnorePlacement {
return self.textIgnoresPlacement;
}
-- (void)setTextJustification:(MGLStyleValue<NSValue *> *)textJustification {
+- (void)setTextJustification:(NSExpression *)textJustification {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::style::TextJustifyType, NSValue *, mbgl::style::TextJustifyType, MGLTextJustification>().toDataDrivenPropertyValue(textJustification);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::style::TextJustifyType, NSValue *, mbgl::style::TextJustifyType, MGLTextJustification>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<mbgl::style::TextJustifyType>>(textJustification);
self.rawLayer->setTextJustify(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)textJustification {
+- (NSExpression *)textJustification {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getTextJustify();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<mbgl::style::TextJustifyType, NSValue *, mbgl::style::TextJustifyType, MGLTextJustification>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextJustify());
+ propertyValue = self.rawLayer->getDefaultTextJustify();
}
- return MGLStyleValueTransformer<mbgl::style::TextJustifyType, NSValue *, mbgl::style::TextJustifyType, MGLTextJustification>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::style::TextJustifyType, NSValue *, mbgl::style::TextJustifyType, MGLTextJustification>().toExpression(propertyValue);
}
-- (void)setTextJustify:(MGLStyleValue<NSValue *> *)textJustify {
+- (void)setTextJustify:(NSExpression *)textJustify {
}
-- (MGLStyleValue<NSValue *> *)textJustify {
+- (NSExpression *)textJustify {
return self.textJustification;
}
-- (void)setTextLetterSpacing:(MGLStyleValue<NSNumber *> *)textLetterSpacing {
+- (void)setTextLetterSpacing:(NSExpression *)textLetterSpacing {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(textLetterSpacing);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(textLetterSpacing);
self.rawLayer->setTextLetterSpacing(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)textLetterSpacing {
+- (NSExpression *)textLetterSpacing {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getTextLetterSpacing();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextLetterSpacing());
+ propertyValue = self.rawLayer->getDefaultTextLetterSpacing();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
-- (void)setTextLineHeight:(MGLStyleValue<NSNumber *> *)textLineHeight {
+- (void)setTextLineHeight:(NSExpression *)textLineHeight {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(textLineHeight);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(textLineHeight);
self.rawLayer->setTextLineHeight(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)textLineHeight {
+- (NSExpression *)textLineHeight {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getTextLineHeight();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultTextLineHeight());
+ propertyValue = self.rawLayer->getDefaultTextLineHeight();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
-- (void)setTextOffset:(MGLStyleValue<NSValue *> *)textOffset {
+- (void)setTextOffset:(NSExpression *)textOffset {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toDataDrivenPropertyValue(textOffset);
+ auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<std::array<float, 2>>>(textOffset);
self.rawLayer->setTextOffset(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)textOffset {
+- (NSExpression *)textOffset {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getTextOffset();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextOffset());
+ propertyValue = self.rawLayer->getDefaultTextOffset();
}
- return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toExpression(propertyValue);
}
-- (void)setTextOptional:(MGLStyleValue<NSNumber *> *)textOptional {
+- (void)setTextOptional:(NSExpression *)textOptional {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue(textOptional);
+ auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<bool>>(textOptional);
self.rawLayer->setTextOptional(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)isTextOptional {
+- (NSExpression *)isTextOptional {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getTextOptional();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(self.rawLayer->getDefaultTextOptional());
+ propertyValue = self.rawLayer->getDefaultTextOptional();
}
- return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<bool, NSNumber *>().toExpression(propertyValue);
}
-- (void)setTextPadding:(MGLStyleValue<NSNumber *> *)textPadding {
+- (void)setTextPadding:(NSExpression *)textPadding {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(textPadding);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(textPadding);
self.rawLayer->setTextPadding(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)textPadding {
+- (NSExpression *)textPadding {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getTextPadding();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultTextPadding());
+ propertyValue = self.rawLayer->getDefaultTextPadding();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
-- (void)setTextPitchAlignment:(MGLStyleValue<NSValue *> *)textPitchAlignment {
+- (void)setTextPitchAlignment:(NSExpression *)textPitchAlignment {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLTextPitchAlignment>().toEnumPropertyValue(textPitchAlignment);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLTextPitchAlignment>().toPropertyValue<mbgl::style::PropertyValue<mbgl::style::AlignmentType>>(textPitchAlignment);
self.rawLayer->setTextPitchAlignment(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)textPitchAlignment {
+- (NSExpression *)textPitchAlignment {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getTextPitchAlignment();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLTextPitchAlignment>().toEnumStyleValue(self.rawLayer->getDefaultTextPitchAlignment());
+ propertyValue = self.rawLayer->getDefaultTextPitchAlignment();
}
- return MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLTextPitchAlignment>().toEnumStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLTextPitchAlignment>().toExpression(propertyValue);
}
-- (void)setTextRotation:(MGLStyleValue<NSNumber *> *)textRotation {
+- (void)setTextRotation:(NSExpression *)textRotation {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(textRotation);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(textRotation);
self.rawLayer->setTextRotate(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)textRotation {
+- (NSExpression *)textRotation {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getTextRotate();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextRotate());
+ propertyValue = self.rawLayer->getDefaultTextRotate();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
-- (void)setTextRotate:(MGLStyleValue<NSNumber *> *)textRotate {
+- (void)setTextRotate:(NSExpression *)textRotate {
}
-- (MGLStyleValue<NSNumber *> *)textRotate {
+- (NSExpression *)textRotate {
return self.textRotation;
}
-- (void)setTextRotationAlignment:(MGLStyleValue<NSValue *> *)textRotationAlignment {
+- (void)setTextRotationAlignment:(NSExpression *)textRotationAlignment {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLTextRotationAlignment>().toEnumPropertyValue(textRotationAlignment);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLTextRotationAlignment>().toPropertyValue<mbgl::style::PropertyValue<mbgl::style::AlignmentType>>(textRotationAlignment);
self.rawLayer->setTextRotationAlignment(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)textRotationAlignment {
+- (NSExpression *)textRotationAlignment {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getTextRotationAlignment();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLTextRotationAlignment>().toEnumStyleValue(self.rawLayer->getDefaultTextRotationAlignment());
+ propertyValue = self.rawLayer->getDefaultTextRotationAlignment();
}
- return MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLTextRotationAlignment>().toEnumStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLTextRotationAlignment>().toExpression(propertyValue);
}
-- (void)setTextTransform:(MGLStyleValue<NSValue *> *)textTransform {
+- (void)setTextTransform:(NSExpression *)textTransform {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::style::TextTransformType, NSValue *, mbgl::style::TextTransformType, MGLTextTransform>().toDataDrivenPropertyValue(textTransform);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::style::TextTransformType, NSValue *, mbgl::style::TextTransformType, MGLTextTransform>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<mbgl::style::TextTransformType>>(textTransform);
self.rawLayer->setTextTransform(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)textTransform {
+- (NSExpression *)textTransform {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getTextTransform();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<mbgl::style::TextTransformType, NSValue *, mbgl::style::TextTransformType, MGLTextTransform>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextTransform());
+ propertyValue = self.rawLayer->getDefaultTextTransform();
}
- return MGLStyleValueTransformer<mbgl::style::TextTransformType, NSValue *, mbgl::style::TextTransformType, MGLTextTransform>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::style::TextTransformType, NSValue *, mbgl::style::TextTransformType, MGLTextTransform>().toExpression(propertyValue);
}
#pragma mark - Accessing the Paint Attributes
-- (void)setIconColor:(MGLStyleValue<MGLColor *> *)iconColor {
+- (void)setIconColor:(NSExpression *)iconColor {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenPropertyValue(iconColor);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<mbgl::Color>>(iconColor);
self.rawLayer->setIconColor(mbglValue);
}
-- (MGLStyleValue<MGLColor *> *)iconColor {
+- (NSExpression *)iconColor {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getIconColor();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(self.rawLayer->getDefaultIconColor());
+ propertyValue = self.rawLayer->getDefaultIconColor();
}
- return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toExpression(propertyValue);
}
- (void)setIconColorTransition:(MGLTransition )transition {
@@ -922,21 +922,21 @@ namespace mbgl {
return transition;
}
-- (void)setIconHaloBlur:(MGLStyleValue<NSNumber *> *)iconHaloBlur {
+- (void)setIconHaloBlur:(NSExpression *)iconHaloBlur {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(iconHaloBlur);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(iconHaloBlur);
self.rawLayer->setIconHaloBlur(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)iconHaloBlur {
+- (NSExpression *)iconHaloBlur {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getIconHaloBlur();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultIconHaloBlur());
+ propertyValue = self.rawLayer->getDefaultIconHaloBlur();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
- (void)setIconHaloBlurTransition:(MGLTransition )transition {
@@ -957,21 +957,21 @@ namespace mbgl {
return transition;
}
-- (void)setIconHaloColor:(MGLStyleValue<MGLColor *> *)iconHaloColor {
+- (void)setIconHaloColor:(NSExpression *)iconHaloColor {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenPropertyValue(iconHaloColor);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<mbgl::Color>>(iconHaloColor);
self.rawLayer->setIconHaloColor(mbglValue);
}
-- (MGLStyleValue<MGLColor *> *)iconHaloColor {
+- (NSExpression *)iconHaloColor {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getIconHaloColor();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(self.rawLayer->getDefaultIconHaloColor());
+ propertyValue = self.rawLayer->getDefaultIconHaloColor();
}
- return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toExpression(propertyValue);
}
- (void)setIconHaloColorTransition:(MGLTransition )transition {
@@ -992,21 +992,21 @@ namespace mbgl {
return transition;
}
-- (void)setIconHaloWidth:(MGLStyleValue<NSNumber *> *)iconHaloWidth {
+- (void)setIconHaloWidth:(NSExpression *)iconHaloWidth {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(iconHaloWidth);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(iconHaloWidth);
self.rawLayer->setIconHaloWidth(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)iconHaloWidth {
+- (NSExpression *)iconHaloWidth {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getIconHaloWidth();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultIconHaloWidth());
+ propertyValue = self.rawLayer->getDefaultIconHaloWidth();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
- (void)setIconHaloWidthTransition:(MGLTransition )transition {
@@ -1027,21 +1027,21 @@ namespace mbgl {
return transition;
}
-- (void)setIconOpacity:(MGLStyleValue<NSNumber *> *)iconOpacity {
+- (void)setIconOpacity:(NSExpression *)iconOpacity {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(iconOpacity);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(iconOpacity);
self.rawLayer->setIconOpacity(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)iconOpacity {
+- (NSExpression *)iconOpacity {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getIconOpacity();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultIconOpacity());
+ propertyValue = self.rawLayer->getDefaultIconOpacity();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
- (void)setIconOpacityTransition:(MGLTransition )transition {
@@ -1062,21 +1062,21 @@ namespace mbgl {
return transition;
}
-- (void)setIconTranslation:(MGLStyleValue<NSValue *> *)iconTranslation {
+- (void)setIconTranslation:(NSExpression *)iconTranslation {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toInterpolatablePropertyValue(iconTranslation);
+ auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toPropertyValue<mbgl::style::PropertyValue<std::array<float, 2>>>(iconTranslation);
self.rawLayer->setIconTranslate(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)iconTranslation {
+- (NSExpression *)iconTranslation {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getIconTranslate();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toStyleValue(self.rawLayer->getDefaultIconTranslate());
+ propertyValue = self.rawLayer->getDefaultIconTranslate();
}
- return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toExpression(propertyValue);
}
- (void)setIconTranslationTransition:(MGLTransition )transition {
@@ -1097,52 +1097,52 @@ namespace mbgl {
return transition;
}
-- (void)setIconTranslate:(MGLStyleValue<NSValue *> *)iconTranslate {
+- (void)setIconTranslate:(NSExpression *)iconTranslate {
}
-- (MGLStyleValue<NSValue *> *)iconTranslate {
+- (NSExpression *)iconTranslate {
return self.iconTranslation;
}
-- (void)setIconTranslationAnchor:(MGLStyleValue<NSValue *> *)iconTranslationAnchor {
+- (void)setIconTranslationAnchor:(NSExpression *)iconTranslationAnchor {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLIconTranslationAnchor>().toEnumPropertyValue(iconTranslationAnchor);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLIconTranslationAnchor>().toPropertyValue<mbgl::style::PropertyValue<mbgl::style::TranslateAnchorType>>(iconTranslationAnchor);
self.rawLayer->setIconTranslateAnchor(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)iconTranslationAnchor {
+- (NSExpression *)iconTranslationAnchor {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getIconTranslateAnchor();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLIconTranslationAnchor>().toEnumStyleValue(self.rawLayer->getDefaultIconTranslateAnchor());
+ propertyValue = self.rawLayer->getDefaultIconTranslateAnchor();
}
- return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLIconTranslationAnchor>().toEnumStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLIconTranslationAnchor>().toExpression(propertyValue);
}
-- (void)setIconTranslateAnchor:(MGLStyleValue<NSValue *> *)iconTranslateAnchor {
+- (void)setIconTranslateAnchor:(NSExpression *)iconTranslateAnchor {
}
-- (MGLStyleValue<NSValue *> *)iconTranslateAnchor {
+- (NSExpression *)iconTranslateAnchor {
return self.iconTranslationAnchor;
}
-- (void)setTextColor:(MGLStyleValue<MGLColor *> *)textColor {
+- (void)setTextColor:(NSExpression *)textColor {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenPropertyValue(textColor);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<mbgl::Color>>(textColor);
self.rawLayer->setTextColor(mbglValue);
}
-- (MGLStyleValue<MGLColor *> *)textColor {
+- (NSExpression *)textColor {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getTextColor();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextColor());
+ propertyValue = self.rawLayer->getDefaultTextColor();
}
- return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toExpression(propertyValue);
}
- (void)setTextColorTransition:(MGLTransition )transition {
@@ -1163,21 +1163,21 @@ namespace mbgl {
return transition;
}
-- (void)setTextHaloBlur:(MGLStyleValue<NSNumber *> *)textHaloBlur {
+- (void)setTextHaloBlur:(NSExpression *)textHaloBlur {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(textHaloBlur);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(textHaloBlur);
self.rawLayer->setTextHaloBlur(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)textHaloBlur {
+- (NSExpression *)textHaloBlur {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getTextHaloBlur();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextHaloBlur());
+ propertyValue = self.rawLayer->getDefaultTextHaloBlur();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
- (void)setTextHaloBlurTransition:(MGLTransition )transition {
@@ -1198,21 +1198,21 @@ namespace mbgl {
return transition;
}
-- (void)setTextHaloColor:(MGLStyleValue<MGLColor *> *)textHaloColor {
+- (void)setTextHaloColor:(NSExpression *)textHaloColor {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenPropertyValue(textHaloColor);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<mbgl::Color>>(textHaloColor);
self.rawLayer->setTextHaloColor(mbglValue);
}
-- (MGLStyleValue<MGLColor *> *)textHaloColor {
+- (NSExpression *)textHaloColor {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getTextHaloColor();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextHaloColor());
+ propertyValue = self.rawLayer->getDefaultTextHaloColor();
}
- return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toExpression(propertyValue);
}
- (void)setTextHaloColorTransition:(MGLTransition )transition {
@@ -1233,21 +1233,21 @@ namespace mbgl {
return transition;
}
-- (void)setTextHaloWidth:(MGLStyleValue<NSNumber *> *)textHaloWidth {
+- (void)setTextHaloWidth:(NSExpression *)textHaloWidth {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(textHaloWidth);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(textHaloWidth);
self.rawLayer->setTextHaloWidth(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)textHaloWidth {
+- (NSExpression *)textHaloWidth {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getTextHaloWidth();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextHaloWidth());
+ propertyValue = self.rawLayer->getDefaultTextHaloWidth();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
- (void)setTextHaloWidthTransition:(MGLTransition )transition {
@@ -1268,21 +1268,21 @@ namespace mbgl {
return transition;
}
-- (void)setTextOpacity:(MGLStyleValue<NSNumber *> *)textOpacity {
+- (void)setTextOpacity:(NSExpression *)textOpacity {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(textOpacity);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(textOpacity);
self.rawLayer->setTextOpacity(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)textOpacity {
+- (NSExpression *)textOpacity {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getTextOpacity();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextOpacity());
+ propertyValue = self.rawLayer->getDefaultTextOpacity();
}
- return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
- (void)setTextOpacityTransition:(MGLTransition )transition {
@@ -1303,21 +1303,21 @@ namespace mbgl {
return transition;
}
-- (void)setTextTranslation:(MGLStyleValue<NSValue *> *)textTranslation {
+- (void)setTextTranslation:(NSExpression *)textTranslation {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toInterpolatablePropertyValue(textTranslation);
+ auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toPropertyValue<mbgl::style::PropertyValue<std::array<float, 2>>>(textTranslation);
self.rawLayer->setTextTranslate(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)textTranslation {
+- (NSExpression *)textTranslation {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getTextTranslate();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toStyleValue(self.rawLayer->getDefaultTextTranslate());
+ propertyValue = self.rawLayer->getDefaultTextTranslate();
}
- return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toExpression(propertyValue);
}
- (void)setTextTranslationTransition:(MGLTransition )transition {
@@ -1338,34 +1338,34 @@ namespace mbgl {
return transition;
}
-- (void)setTextTranslate:(MGLStyleValue<NSValue *> *)textTranslate {
+- (void)setTextTranslate:(NSExpression *)textTranslate {
}
-- (MGLStyleValue<NSValue *> *)textTranslate {
+- (NSExpression *)textTranslate {
return self.textTranslation;
}
-- (void)setTextTranslationAnchor:(MGLStyleValue<NSValue *> *)textTranslationAnchor {
+- (void)setTextTranslationAnchor:(NSExpression *)textTranslationAnchor {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLTextTranslationAnchor>().toEnumPropertyValue(textTranslationAnchor);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLTextTranslationAnchor>().toPropertyValue<mbgl::style::PropertyValue<mbgl::style::TranslateAnchorType>>(textTranslationAnchor);
self.rawLayer->setTextTranslateAnchor(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)textTranslationAnchor {
+- (NSExpression *)textTranslationAnchor {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getTextTranslateAnchor();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLTextTranslationAnchor>().toEnumStyleValue(self.rawLayer->getDefaultTextTranslateAnchor());
+ propertyValue = self.rawLayer->getDefaultTextTranslateAnchor();
}
- return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLTextTranslationAnchor>().toEnumStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLTextTranslationAnchor>().toExpression(propertyValue);
}
-- (void)setTextTranslateAnchor:(MGLStyleValue<NSValue *> *)textTranslateAnchor {
+- (void)setTextTranslateAnchor:(NSExpression *)textTranslateAnchor {
}
-- (MGLStyleValue<NSValue *> *)textTranslateAnchor {
+- (NSExpression *)textTranslateAnchor {
return self.textTranslationAnchor;
}
diff --git a/platform/darwin/src/MGLVectorSource+MGLAdditions.h b/platform/darwin/src/MGLVectorSource+MGLAdditions.h
deleted file mode 100644
index 43b0aba747..0000000000
--- a/platform/darwin/src/MGLVectorSource+MGLAdditions.h
+++ /dev/null
@@ -1,15 +0,0 @@
-#import <Mapbox/Mapbox.h>
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface MGLVectorSource (MGLAdditions)
-
-+ (NSString *)preferredMapboxStreetsLanguage;
-
-- (NS_DICTIONARY_OF(NSString *, NSString *) *)localizedKeysByKeyForPreferredLanguage:(nullable NSString *)preferredLanguage;
-
-@property (nonatomic, readonly, getter=isMapboxStreets) BOOL mapboxStreets;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLVectorSource+MGLAdditions.m b/platform/darwin/src/MGLVectorSource+MGLAdditions.m
deleted file mode 100644
index a305388117..0000000000
--- a/platform/darwin/src/MGLVectorSource+MGLAdditions.m
+++ /dev/null
@@ -1,53 +0,0 @@
-#import "MGLVectorSource+MGLAdditions.h"
-
-@implementation MGLVectorSource (MGLAdditions)
-
-+ (NS_SET_OF(NSString *) *)mapboxStreetsLanguages {
- // https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview
- static dispatch_once_t onceToken;
- static NS_SET_OF(NSString *) *mapboxStreetsLanguages;
- dispatch_once(&onceToken, ^{
- // https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview
- mapboxStreetsLanguages = [NSSet setWithObjects:@"ar", @"de", @"en", @"es", @"fr", @"pt", @"ru", @"zh", @"zh-Hans", nil];
- });
- return mapboxStreetsLanguages;
-}
-
-+ (NSString *)preferredMapboxStreetsLanguage {
- NSArray<NSString *> *supportedLanguages = [MGLVectorSource mapboxStreetsLanguages].allObjects;
- NSArray<NSString *> *preferredLanguages = [NSBundle preferredLocalizationsFromArray:supportedLanguages
- forPreferences:[NSLocale preferredLanguages]];
- NSString *mostSpecificLanguage;
- for (NSString *language in preferredLanguages) {
- if (language.length > mostSpecificLanguage.length) {
- mostSpecificLanguage = language;
- }
- }
- return mostSpecificLanguage ?: @"en";
-}
-
-- (BOOL)isMapboxStreets {
- NSURL *url = self.configurationURL;
- if (![url.scheme isEqualToString:@"mapbox"]) {
- return NO;
- }
- NSArray *identifiers = [url.host componentsSeparatedByString:@","];
- return [identifiers containsObject:@"mapbox.mapbox-streets-v7"] || [identifiers containsObject:@"mapbox.mapbox-streets-v6"];
-}
-
-- (NS_DICTIONARY_OF(NSString *, NSString *) *)localizedKeysByKeyForPreferredLanguage:(nullable NSString *)preferredLanguage {
- if (!self.mapboxStreets) {
- return @{};
- }
-
- // Replace {name} and {name_*} with the matching localized name tag.
- NSString *localizedKey = preferredLanguage ? [NSString stringWithFormat:@"name_%@", preferredLanguage] : @"name";
- NSMutableDictionary *localizedKeysByKey = [NSMutableDictionary dictionaryWithObject:localizedKey forKey:@"name"];
- for (NSString *languageCode in [MGLVectorSource mapboxStreetsLanguages]) {
- NSString *key = [NSString stringWithFormat:@"name_%@", languageCode];
- localizedKeysByKey[key] = localizedKey;
- }
- return localizedKeysByKey;
-}
-
-@end
diff --git a/platform/darwin/src/MGLVectorSource.mm b/platform/darwin/src/MGLVectorSource.mm
index b1bda56f2d..90ffe6f98b 100644
--- a/platform/darwin/src/MGLVectorSource.mm
+++ b/platform/darwin/src/MGLVectorSource.mm
@@ -5,6 +5,7 @@
#import "MGLTileSource_Private.h"
#import "MGLStyle_Private.h"
#import "MGLMapView_Private.h"
+
#import "NSPredicate+MGLAdditions.h"
#import "NSURL+MGLAdditions.h"
@@ -71,3 +72,55 @@
}
@end
+
+@implementation MGLVectorSource (Private)
+
++ (NS_SET_OF(NSString *) *)mapboxStreetsLanguages {
+ // https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview
+ static dispatch_once_t onceToken;
+ static NS_SET_OF(NSString *) *mapboxStreetsLanguages;
+ dispatch_once(&onceToken, ^{
+ // https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview
+ mapboxStreetsLanguages = [NSSet setWithObjects:@"ar", @"de", @"en", @"es", @"fr", @"pt", @"ru", @"zh", @"zh-Hans", nil];
+ });
+ return mapboxStreetsLanguages;
+}
+
++ (NSString *)preferredMapboxStreetsLanguage {
+ NSArray<NSString *> *supportedLanguages = [MGLVectorSource mapboxStreetsLanguages].allObjects;
+ NSArray<NSString *> *preferredLanguages = [NSBundle preferredLocalizationsFromArray:supportedLanguages
+ forPreferences:[NSLocale preferredLanguages]];
+ NSString *mostSpecificLanguage;
+ for (NSString *language in preferredLanguages) {
+ if (language.length > mostSpecificLanguage.length) {
+ mostSpecificLanguage = language;
+ }
+ }
+ return mostSpecificLanguage ?: @"en";
+}
+
+- (BOOL)isMapboxStreets {
+ NSURL *url = self.configurationURL;
+ if (![url.scheme isEqualToString:@"mapbox"]) {
+ return NO;
+ }
+ NSArray *identifiers = [url.host componentsSeparatedByString:@","];
+ return [identifiers containsObject:@"mapbox.mapbox-streets-v7"] || [identifiers containsObject:@"mapbox.mapbox-streets-v6"];
+}
+
+- (NS_DICTIONARY_OF(NSString *, NSString *) *)localizedKeysByKeyForPreferredLanguage:(nullable NSString *)preferredLanguage {
+ if (!self.mapboxStreets) {
+ return @{};
+ }
+
+ // Replace {name} and {name_*} with the matching localized name tag.
+ NSString *localizedKey = preferredLanguage ? [NSString stringWithFormat:@"name_%@", preferredLanguage] : @"name";
+ NSMutableDictionary *localizedKeysByKey = [NSMutableDictionary dictionaryWithObject:localizedKey forKey:@"name"];
+ for (NSString *languageCode in [MGLVectorSource mapboxStreetsLanguages]) {
+ NSString *key = [NSString stringWithFormat:@"name_%@", languageCode];
+ localizedKeysByKey[key] = localizedKey;
+ }
+ return localizedKeysByKey;
+}
+
+@end
diff --git a/platform/darwin/src/MGLVectorSource_Private.h b/platform/darwin/src/MGLVectorSource_Private.h
index 335743173e..7d19e03a99 100644
--- a/platform/darwin/src/MGLVectorSource_Private.h
+++ b/platform/darwin/src/MGLVectorSource_Private.h
@@ -3,6 +3,15 @@
NS_ASSUME_NONNULL_BEGIN
@interface MGLVectorSource (Private)
+
+@property (nonatomic, readonly, getter=isMapboxStreets) BOOL mapboxStreets;
+
++ (NS_SET_OF(NSString *) *)mapboxStreetsLanguages;
+
++ (NSString *)preferredMapboxStreetsLanguage;
+
+- (NS_DICTIONARY_OF(NSString *, NSString *) *)localizedKeysByKeyForPreferredLanguage:(nullable NSString *)preferredLanguage;
+
@end
NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLVectorStyleLayer.h b/platform/darwin/src/MGLVectorStyleLayer.h
index 6603570e25..177b1b70f0 100644
--- a/platform/darwin/src/MGLVectorStyleLayer.h
+++ b/platform/darwin/src/MGLVectorStyleLayer.h
@@ -10,10 +10,10 @@ NS_ASSUME_NONNULL_BEGIN
is defined by an `MGLShapeSource` or `MGLVectorSource` object.
Create instances of `MGLCircleStyleLayer`, `MGLFillStyleLayer`,
- `MGLFillExtrusionStyleLayer`, `MGLLineStyleLayer`, and `MGLSymbolStyleLayer` in
- order to use `MGLVectorStyleLayer`'s properties and methods. Do not create
- instances of `MGLVectorStyleLayer` directly, and do not create your own
- subclasses of this class.
+ `MGLFillExtrusionStyleLayer`, `MGLHeatmapStyleLayer`, `MGLLineStyleLayer`, and
+ `MGLSymbolStyleLayer` in order to use `MGLVectorStyleLayer`'s properties and
+ methods. Do not create instances of `MGLVectorStyleLayer` directly, and do not
+ create your own subclasses of this class.
*/
MGL_EXPORT
@interface MGLVectorStyleLayer : MGLForegroundStyleLayer
@@ -34,108 +34,9 @@ MGL_EXPORT
comes from the style, its predicate corresponds to the
<a href="https://www.mapbox.com/mapbox-gl-style-spec/#layer-filter">`filter`</a>
property in the style JSON.
-
- The following comparison operators are supported.
-
- <ul>
- <li><code>NSEqualToPredicateOperatorType</code> (<code>=</code>, <code>==</code>)</li>
- <li><code>NSGreaterThanOrEqualToPredicateOperatorType</code> (<code>>=</code>, <code>=></code>)</li>
- <li><code>NSLessThanOrEqualToPredicateOperatorType</code> (<code><=</code>, <code>=<</code>)</li>
- <li><code>NSGreaterThanPredicateOperatorType</code> (<code>></code>)</li>
- <li><code>NSLessThanPredicateOperatorType</code> (<code><</code>)</li>
- <li><code>NSNotEqualToPredicateOperatorType</code> (<code>!=</code>, <code><></code>)</li>
- <li><code>NSBetweenPredicateOperatorType</code> (<code>BETWEEN</code>)</li>
- </ul>
-
- The following compound operators are supported:
-
- <ul>
- <li><code>NSAndPredicateType</code> (<code>AND</code>, <code>&&</code>)</li>
- <li><code>NSOrPredicateType</code> (<code>OR</code>, <code>||</code>)</li>
- <li><code>NSNotPredicateType</code> (<code>NOT</code>, <code>!</code>)</li>
- </ul>
-
- The following aggregate operators are supported:
-
- <ul>
- <li><code>NSInPredicateOperatorType</code> (<code>IN</code>)</li>
- <li><code>NSContainsPredicateOperatorType</code> (<code>CONTAINS</code>)</li>
- </ul>
-
- To test whether a feature has or lacks a specific attribute, compare the
- attribute to `NULL` or `NIL`. Predicates created using the
- `+[NSPredicate predicateWithValue:]` method are also supported. String
- operators and custom operators are not supported.
-
- For details about the predicate format string syntax, consult the “Predicate
- Format String Syntax” chapter of the
- “<a href="https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Predicates/">Predicate Programming Guide</a>”
- in Apple developer documentation.
-
- The predicate's left-hand expression must be a string that identifies a feature
- attribute or, alternatively, one of the following special attributes:
-
- <table>
- <thead>
- <tr><th>Attribute</th><th>Meaning</th></tr>
- </thead>
- <tbody>
- <tr>
- <td><code>$id</code></td>
- <td>
- A value that uniquely identifies the feature in the containing source.
- For details on the types of values that may be associated with this key,
- consult the documentation for the <code>MGLFeature</code> protocol’s
- <code>identifier</code> property.
- </td>
- </tr>
- <tr>
- <td><code>$type</code></td>
- <td>
- The type of geometry represented by the feature. A feature’s type is
- guaranteed to be one of the following strings:
- <ul>
- <li>
- <code>Point</code> for point features, corresponding to the
- <code>MGLPointAnnotation</code> class
- </li>
- <li>
- <code>LineString</code> for polyline features, corresponding to
- the <code>MGLPolyline</code> class
- </li>
- <li>
- <code>Polygon</code> for polygon features, corresponding to the
- <code>MGLPolygon</code> class
- </li>
- </ul>
- </td>
- </tr>
- <tr>
- <td><code>point_count</code></td>
- <td>The number of point features in a given cluster.</td>
- </tr>
- </tbody>
- </table>
-
- The predicate’s right-hand expression must be an `NSString` (to match strings)
- or `NSNumber` (to match numbers, including Boolean values) or an array of
- `NSString`s or `NSNumber`s, depending on the operator and the type of values
- expected for the attribute being tested. For floating-point values, use
- `-[NSNumber numberWithDouble:]` instead of `-[NSNumber numberWithFloat:]`
- to avoid precision issues.
-
- Automatic type casting is not performed. Therefore, a feature only matches this
- predicate if its value for the attribute in question is of the same type as the
- value specified in the predicate. Also, operator modifiers such as `c` (for
- case insensitivity), `d` (for diacritic insensitivity), and `l` (for locale
- sensitivity) are unsupported for comparison and aggregate operators that are
- used in the predicate.
-
- It is possible to create expressions that contain special characters in the
- predicate format syntax. This includes the `$` in the `$id` and `$type` special
- style attributes and also `hyphen-minus` and `tag:subtag`. However, you must use
- `%K` in the format string to represent these variables:
- `@"%K == 'LineString'", @"$type"`.
+
+ See the “<a href="../predicates-and-expressions.html">Predicates and Expressions</a>”
+ guide for details about the predicate syntax supported by this class.
### Example
diff --git a/platform/darwin/src/NSArray+MGLAdditions.mm b/platform/darwin/src/NSArray+MGLAdditions.mm
index 3cab7ff427..195e96b09d 100644
--- a/platform/darwin/src/NSArray+MGLAdditions.mm
+++ b/platform/darwin/src/NSArray+MGLAdditions.mm
@@ -1,7 +1,7 @@
#import "NSArray+MGLAdditions.h"
#import "NSDictionary+MGLAdditions.h"
-#import "NSExpression+MGLAdditions.mm"
+#import "NSExpression+MGLPrivateAdditions.h"
@implementation NSArray (MGLAdditions)
diff --git a/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm b/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm
index ac2d598d05..5b54d66aeb 100644
--- a/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm
+++ b/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm
@@ -1,7 +1,7 @@
#import "NSComparisonPredicate+MGLAdditions.h"
#import "NSPredicate+MGLAdditions.h"
-#import "NSExpression+MGLAdditions.h"
+#import "NSExpression+MGLPrivateAdditions.h"
@implementation NSComparisonPredicate (MGLAdditions)
@@ -294,3 +294,45 @@
}
@end
+
+@implementation NSComparisonPredicate (MGLExpressionAdditions)
+
+- (id)mgl_jsonExpressionObject {
+ NSString *op;
+ switch (self.predicateOperatorType) {
+ case NSLessThanPredicateOperatorType:
+ op = @"<";
+ break;
+ case NSLessThanOrEqualToPredicateOperatorType:
+ op = @"<=";
+ break;
+ case NSGreaterThanPredicateOperatorType:
+ op = @">";
+ break;
+ case NSGreaterThanOrEqualToPredicateOperatorType:
+ op = @">=";
+ break;
+ case NSEqualToPredicateOperatorType:
+ op = @"==";
+ break;
+ case NSNotEqualToPredicateOperatorType:
+ op = @"!=";
+ break;
+ case NSMatchesPredicateOperatorType:
+ case NSLikePredicateOperatorType:
+ case NSBeginsWithPredicateOperatorType:
+ case NSEndsWithPredicateOperatorType:
+ case NSInPredicateOperatorType:
+ case NSCustomSelectorPredicateOperatorType:
+ case NSContainsPredicateOperatorType:
+ case NSBetweenPredicateOperatorType:
+ [NSException raise:NSInvalidArgumentException
+ format:@"NSPredicateOperatorType:%lu is not supported.", (unsigned long)self.predicateOperatorType];
+ }
+ if (op) {
+ return @[op, self.leftExpression.mgl_jsonExpressionObject, self.rightExpression.mgl_jsonExpressionObject];
+ }
+ return nil;
+}
+
+@end
diff --git a/platform/darwin/src/NSCompoundPredicate+MGLAdditions.mm b/platform/darwin/src/NSCompoundPredicate+MGLAdditions.mm
index 0039b5af82..19568b8033 100644
--- a/platform/darwin/src/NSCompoundPredicate+MGLAdditions.mm
+++ b/platform/darwin/src/NSCompoundPredicate+MGLAdditions.mm
@@ -1,7 +1,7 @@
#import "NSCompoundPredicate+MGLAdditions.h"
#import "NSPredicate+MGLAdditions.h"
-#import "NSExpression+MGLAdditions.h"
+#import "NSExpression+MGLPrivateAdditions.h"
@implementation NSCompoundPredicate (MGLAdditions)
@@ -71,3 +71,31 @@
}
@end
+
+@implementation NSCompoundPredicate (MGLExpressionAdditions)
+
+- (id)mgl_jsonExpressionObject {
+ switch (self.compoundPredicateType) {
+ case NSNotPredicateType: {
+ NSAssert(self.subpredicates.count <= 1, @"NOT predicate cannot have multiple subpredicates.");
+ NSPredicate *subpredicate = self.subpredicates.firstObject;
+ return @[@"!", subpredicate.mgl_jsonExpressionObject];
+ }
+
+ case NSAndPredicateType: {
+ NSArray *subarrays = [self.subpredicates valueForKeyPath:@"mgl_jsonExpressionObject"];
+ return [@[@"all"] arrayByAddingObjectsFromArray:subarrays];
+ }
+
+ case NSOrPredicateType: {
+ NSArray *subarrays = [self.subpredicates valueForKeyPath:@"mgl_jsonExpressionObject"];
+ return [@[@"any"] arrayByAddingObjectsFromArray:subarrays];
+ }
+ }
+
+ [NSException raise:@"Compound predicate type not handled"
+ format:@""];
+ return nil;
+}
+
+@end
diff --git a/platform/darwin/src/NSDictionary+MGLAdditions.mm b/platform/darwin/src/NSDictionary+MGLAdditions.mm
index aad7fd8810..4bc7ddb3cf 100644
--- a/platform/darwin/src/NSDictionary+MGLAdditions.mm
+++ b/platform/darwin/src/NSDictionary+MGLAdditions.mm
@@ -1,6 +1,6 @@
#import "NSDictionary+MGLAdditions.h"
-#import "NSExpression+MGLAdditions.mm"
+#import "NSExpression+MGLPrivateAdditions.h"
#import "NSArray+MGLAdditions.h"
@implementation NSDictionary (MGLAdditions)
diff --git a/platform/darwin/src/NSExpression+MGLAdditions.h b/platform/darwin/src/NSExpression+MGLAdditions.h
index 491ed5a536..b4d36bc1b1 100644
--- a/platform/darwin/src/NSExpression+MGLAdditions.h
+++ b/platform/darwin/src/NSExpression+MGLAdditions.h
@@ -1,17 +1,46 @@
#import <Foundation/Foundation.h>
+#if TARGET_OS_IPHONE
+ #import <UIKit/UIKit.h>
+#else
+ #import <Cocoa/Cocoa.h>
+#endif
-#include <mbgl/style/filter.hpp>
+#import "MGLTypes.h"
NS_ASSUME_NONNULL_BEGIN
@interface NSExpression (MGLAdditions)
-@property (nonatomic, readonly) mbgl::Value mgl_constantMBGLValue;
-@property (nonatomic, readonly) std::vector<mbgl::Value> mgl_aggregateMBGLValue;
-@property (nonatomic, readonly) mbgl::FeatureType mgl_featureType;
-@property (nonatomic, readonly) std::vector<mbgl::FeatureType> mgl_aggregateFeatureType;
-@property (nonatomic, readonly) mbgl::FeatureIdentifier mgl_featureIdentifier;
-@property (nonatomic, readonly) std::vector<mbgl::FeatureIdentifier> mgl_aggregateFeatureIdentifier;
+/**
+ Returns an expression equivalent to the given Foundation object deserialized
+ from JSON data.
+
+ The Foundation object is interpreted according to the
+ [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions).
+ See the “[Predicates and Expressions](../predicates-and-expressions.html)”
+ guide for a correspondence of operators and types between the style
+ specification and the `NSExpression` representation used by this SDK.
+
+ @param object A Foundation object deserialized from JSON data, for example
+ using `NSJSONSerialization`.
+ @return An initialized expression equivalent to `object`, suitable for use as
+ the value of a style layer attribute.
+ */
++ (instancetype)mgl_expressionWithJSONObject:(id)object;
+
+/**
+ An equivalent Foundation object that can be serialized as JSON.
+
+ The Foundation object conforms to the
+ [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions).
+ See the “[Predicates and Expressions](../predicates-and-expressions.html)”
+ guide for a correspondence of operators and types between the style
+ specification and the `NSExpression` representation used by this SDK.
+
+ You can use `NSJSONSerialization` to serialize the Foundation object as data to
+ write to a file.
+ */
+@property (nonatomic, readonly) id mgl_jsonExpressionObject;
@end
diff --git a/platform/darwin/src/NSExpression+MGLAdditions.mm b/platform/darwin/src/NSExpression+MGLAdditions.mm
index a7759cda9d..5ad565c398 100644
--- a/platform/darwin/src/NSExpression+MGLAdditions.mm
+++ b/platform/darwin/src/NSExpression+MGLAdditions.mm
@@ -1,13 +1,19 @@
-#import "NSExpression+MGLAdditions.h"
+#import "NSExpression+MGLPrivateAdditions.h"
#import "MGLTypes.h"
#if TARGET_OS_IPHONE
#import "UIColor+MGLAdditions.h"
+ #define MGLEdgeInsets UIEdgeInsets
#else
#import "NSColor+MGLAdditions.h"
+ #define MGLEdgeInsets NSEdgeInsets
#endif
+#import "NSPredicate+MGLAdditions.h"
+#import "NSValue+MGLStyleAttributeAdditions.h"
-@implementation NSExpression (MGLAdditions)
+#import <mbgl/style/expression/expression.hpp>
+
+@implementation NSExpression (MGLPrivateAdditions)
- (std::vector<mbgl::Value>)mgl_aggregateMBGLValue {
if ([self.constantValue isKindOfClass:[NSArray class]] || [self.constantValue isKindOfClass:[NSSet class]]) {
@@ -154,3 +160,578 @@
}
@end
+
+@implementation NSObject (MGLExpressionAdditions)
+
+- (NSNumber *)mgl_number {
+ return nil;
+}
+
+- (NSNumber *)mgl_numberWithFallbackValues:(id)fallbackValue, ... {
+ if (self.mgl_number) {
+ return self.mgl_number;
+ }
+
+ va_list fallbackValues;
+ va_start(fallbackValues, fallbackValue);
+ for (id value = fallbackValue; value; value = va_arg(fallbackValues, id)) {
+ if ([value mgl_number]) {
+ return [value mgl_number];
+ }
+ }
+
+ return nil;
+}
+
+@end
+
+@implementation NSNull (MGLExpressionAdditions)
+
+- (id)mgl_jsonExpressionObject {
+ return self;
+}
+
+@end
+
+@implementation NSString (MGLExpressionAdditions)
+
+- (id)mgl_jsonExpressionObject {
+ return self;
+}
+
+- (NSNumber *)mgl_number {
+ if (self.doubleValue || ![[NSDecimalNumber decimalNumberWithString:self] isEqual:[NSDecimalNumber notANumber]]) {
+ return @(self.doubleValue);
+ }
+
+ return nil;
+}
+
+@end
+
+@implementation NSNumber (MGLExpressionAdditions)
+
+- (id)mgl_interpolateWithCurveType:(NSString *)curveType
+ parameters:(NSArray *)parameters
+ stops:(NSDictionary<NSNumber *, id> *)stops {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Interpolation expressions lack underlying Objective-C implementations."];
+ return nil;
+}
+
+- (id)mgl_stepWithMinimum:(id)minimum stops:(NSDictionary<NSNumber *, id> *)stops {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Interpolation expressions lack underlying Objective-C implementations."];
+ return nil;
+}
+
+- (NSNumber *)mgl_number {
+ return self;
+}
+
+- (id)mgl_jsonExpressionObject {
+ if ([self isEqualToNumber:@(M_E)]) {
+ return @[@"e"];
+ } else if ([self isEqualToNumber:@(M_PI)]) {
+ return @[@"pi"];
+ }
+ return self;
+}
+
+@end
+
+@implementation MGLColor (MGLExpressionAdditions)
+
+- (id)mgl_jsonExpressionObject {
+ auto color = [self mgl_color];
+ if (color.a == 1) {
+ return @[@"rgb", @(color.r * 255), @(color.g * 255), @(color.b * 255)];
+ }
+ return @[@"rgba", @(color.r * 255), @(color.g * 255), @(color.b * 255), @(color.a)];
+}
+
+@end
+
+@implementation NSArray (MGLExpressionAdditions)
+
+- (id)mgl_jsonExpressionObject {
+ return [self valueForKeyPath:@"mgl_jsonExpressionObject"];
+}
+
+@end
+
+@implementation NSDictionary (MGLExpressionAdditions)
+
+- (id)mgl_jsonExpressionObject {
+ NSMutableDictionary *expressionObject = [NSMutableDictionary dictionaryWithCapacity:self.count];
+ [self enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
+ expressionObject[[key mgl_jsonExpressionObject]] = [obj mgl_jsonExpressionObject];
+ }];
+
+ return expressionObject;
+}
+
+@end
+
+@implementation NSExpression (MGLExpressionAdditions)
+
+- (NSExpression *)mgl_expressionWithContext:(NSDictionary<NSString *, NSExpression *> *)context {
+ [NSException raise:NSInternalInconsistencyException
+ format:@"Assignment expressions lack underlying Objective-C implementations."];
+ return self;
+}
+
+@end
+
+@implementation NSExpression (MGLAdditions)
+
+static NSDictionary<NSString *, NSString *> *MGLFunctionNamesByExpressionOperator;
+static NSDictionary<NSString *, NSString *> *MGLExpressionOperatorsByFunctionNames;
+
+NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) {
+ NSMutableArray *subexpressions = [NSMutableArray arrayWithCapacity:objects.count];
+ for (id object in objects) {
+ NSExpression *expression = [NSExpression mgl_expressionWithJSONObject:object];
+ [subexpressions addObject:expression];
+ }
+ return subexpressions;
+}
+
++ (instancetype)mgl_expressionWithJSONObject:(id)object {
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ MGLFunctionNamesByExpressionOperator = @{
+ @"+": @"add:to:",
+ @"-": @"from:subtract:",
+ @"*": @"multiply:by:",
+ @"/": @"divide:by:",
+ @"%": @"modulus:by:",
+ @"sqrt": @"sqrt:",
+ @"log10": @"log:",
+ @"ln": @"ln:",
+ @"^": @"raise:toPower:",
+ @"upcase": @"uppercase:",
+ @"downcase": @"lowercase:",
+ };
+ });
+
+ if (!object || object == [NSNull null]) {
+ return [NSExpression expressionForConstantValue:nil];
+ }
+
+ if ([object isKindOfClass:[NSString class]] ||
+ [object isKindOfClass:[NSNumber class]] ||
+ [object isKindOfClass:[NSValue class]] ||
+ [object isKindOfClass:[MGLColor class]]) {
+ return [NSExpression expressionForConstantValue:object];
+ }
+
+ if ([object isKindOfClass:[NSDictionary class]]) {
+ NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:[object count]];
+ [object enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
+ dictionary[key] = [NSExpression mgl_expressionWithJSONObject:obj];
+ }];
+ return [NSExpression expressionForConstantValue:dictionary];
+ }
+
+ if ([object isKindOfClass:[NSArray class]]) {
+ NSArray *array = (NSArray *)object;
+ NSString *op = array.firstObject;
+
+ NSArray *argumentObjects = [array subarrayWithRange:NSMakeRange(1, array.count - 1)];
+
+ NSString *functionName = MGLFunctionNamesByExpressionOperator[op];
+ if (functionName) {
+ NSArray *subexpressions = MGLSubexpressionsWithJSONObjects(argumentObjects);
+ if ([op isEqualToString:@"+"] && argumentObjects.count > 2) {
+ NSExpression *subexpression = [NSExpression expressionForAggregate:subexpressions];
+ return [NSExpression expressionForFunction:@"sum:"
+ arguments:@[subexpression]];
+ } else if ([op isEqualToString:@"^"] && [argumentObjects.firstObject isEqual:@[@"e"]]) {
+ functionName = @"exp:";
+ subexpressions = [subexpressions subarrayWithRange:NSMakeRange(1, subexpressions.count - 1)];
+ }
+
+ return [NSExpression expressionForFunction:functionName
+ arguments:subexpressions];
+ } else if ([op isEqualToString:@"literal"]) {
+ if ([argumentObjects.firstObject isKindOfClass:[NSArray class]]) {
+ return [NSExpression expressionForAggregate:MGLSubexpressionsWithJSONObjects(argumentObjects.firstObject)];
+ }
+ return [NSExpression mgl_expressionWithJSONObject:argumentObjects.firstObject];
+ } else if ([op isEqualToString:@"to-boolean"]) {
+ NSExpression *operand = [NSExpression mgl_expressionWithJSONObject:argumentObjects.firstObject];
+ return [NSExpression expressionForFunction:operand selectorName:@"boolValue" arguments:@[]];
+ } else if ([op isEqualToString:@"to-number"]) {
+ NSExpression *operand = [NSExpression mgl_expressionWithJSONObject:argumentObjects.firstObject];
+ argumentObjects = [argumentObjects subarrayWithRange:NSMakeRange(1, argumentObjects.count - 1)];
+ NSArray *subexpressions = MGLSubexpressionsWithJSONObjects(argumentObjects);
+ return [NSExpression expressionForFunction:operand selectorName:@"mgl_numberWithFallbackValues:" arguments:subexpressions];
+ } else if ([op isEqualToString:@"to-string"]) {
+ NSExpression *operand = [NSExpression mgl_expressionWithJSONObject:argumentObjects.firstObject];
+ return [NSExpression expressionForFunction:operand selectorName:@"stringValue" arguments:@[]];
+ } else if ([op isEqualToString:@"get"]) {
+ if (argumentObjects.count == 2) {
+ NSExpression *operand = [NSExpression mgl_expressionWithJSONObject:argumentObjects.lastObject];
+ if ([argumentObjects.firstObject isKindOfClass:[NSString class]]) {
+ return [NSExpression expressionWithFormat:@"%@.%K", operand, argumentObjects.firstObject];
+ }
+ NSExpression *key = [NSExpression mgl_expressionWithJSONObject:argumentObjects.firstObject];
+ return [NSExpression expressionWithFormat:@"%@.%@", operand, key];
+ }
+ return [NSExpression expressionForKeyPath:argumentObjects.firstObject];
+ } else if ([op isEqualToString:@"length"]) {
+ NSArray *subexpressions = MGLSubexpressionsWithJSONObjects(argumentObjects);
+ return [NSExpression expressionForFunction:@"count:" arguments:@[subexpressions.firstObject]];
+ } else if ([op isEqualToString:@"min"]) {
+ NSArray *subexpressions = MGLSubexpressionsWithJSONObjects(argumentObjects);
+ NSExpression *subexpression = [NSExpression expressionForAggregate:subexpressions];
+ return [NSExpression expressionForFunction:@"min:" arguments:@[subexpression]];
+ } else if ([op isEqualToString:@"max"]) {
+ NSArray *subexpressions = MGLSubexpressionsWithJSONObjects(argumentObjects);
+ NSExpression *subexpression = [NSExpression expressionForAggregate:subexpressions];
+ return [NSExpression expressionForFunction:@"max:" arguments:@[subexpression]];
+ } else if ([op isEqualToString:@"e"]) {
+ return [NSExpression expressionForConstantValue:@(M_E)];
+ } else if ([op isEqualToString:@"pi"]) {
+ return [NSExpression expressionForConstantValue:@(M_PI)];
+ } else if ([op isEqualToString:@"concat"]) {
+ NSArray *subexpressions = MGLSubexpressionsWithJSONObjects(argumentObjects);
+ NSExpression *operand = subexpressions.firstObject;
+ subexpressions = [subexpressions subarrayWithRange:NSMakeRange(1, subexpressions.count - 1)];
+ return [NSExpression expressionForFunction:operand selectorName:@"stringByAppendingString:" arguments:subexpressions];
+ } else if ([op isEqualToString:@"interpolate"]) {
+ NSArray *interpolationOptions = argumentObjects.firstObject;
+ NSString *curveType = interpolationOptions.firstObject;
+ NSExpression *curveTypeExpression = [NSExpression mgl_expressionWithJSONObject:curveType];
+ id curveParameters;
+ if ([curveType isEqual:@"exponential"]) {
+ curveParameters = interpolationOptions[1];
+ } else if ([curveType isEqualToString:@"cubic-bezier"]) {
+ curveParameters = @[@"literal", [interpolationOptions subarrayWithRange:NSMakeRange(1, 4)]];
+ }
+ NSExpression *curveParameterExpression = [NSExpression mgl_expressionWithJSONObject:curveParameters];
+ argumentObjects = [argumentObjects subarrayWithRange:NSMakeRange(1, argumentObjects.count - 1)];
+ NSExpression *operand = [NSExpression mgl_expressionWithJSONObject:argumentObjects.firstObject];
+ NSArray *stopExpressions = [argumentObjects subarrayWithRange:NSMakeRange(1, argumentObjects.count - 1)];
+ NSMutableDictionary *stops = [NSMutableDictionary dictionaryWithCapacity:stopExpressions.count / 2];
+ NSEnumerator *stopEnumerator = stopExpressions.objectEnumerator;
+ while (NSNumber *key = stopEnumerator.nextObject) {
+ NSExpression *valueExpression = stopEnumerator.nextObject;
+ stops[key] = [NSExpression mgl_expressionWithJSONObject:valueExpression];
+ }
+ NSExpression *stopExpression = [NSExpression expressionForConstantValue:stops];
+ return [NSExpression expressionForFunction:operand
+ selectorName:@"mgl_interpolateWithCurveType:parameters:stops:"
+ arguments:@[curveTypeExpression, curveParameterExpression, stopExpression]];
+ } else if ([op isEqualToString:@"step"]) {
+ NSExpression *operand = [NSExpression mgl_expressionWithJSONObject:argumentObjects[0]];
+ NSArray *stopExpressions = [argumentObjects subarrayWithRange:NSMakeRange(1, argumentObjects.count - 1)];
+ NSExpression *minimum;
+ if (stopExpressions.count % 2) {
+ minimum = [NSExpression mgl_expressionWithJSONObject:stopExpressions.firstObject];
+ stopExpressions = [stopExpressions subarrayWithRange:NSMakeRange(1, stopExpressions.count - 1)];
+ }
+ NSMutableDictionary *stops = [NSMutableDictionary dictionaryWithCapacity:stopExpressions.count / 2];
+ NSEnumerator *stopEnumerator = stopExpressions.objectEnumerator;
+ while (NSNumber *key = stopEnumerator.nextObject) {
+ NSExpression *valueExpression = stopEnumerator.nextObject;
+ if (minimum) {
+ stops[key] = [NSExpression mgl_expressionWithJSONObject:valueExpression];
+ } else {
+ minimum = [NSExpression mgl_expressionWithJSONObject:valueExpression];
+ }
+ }
+ NSExpression *stopExpression = [NSExpression expressionForConstantValue:stops];
+ return [NSExpression expressionForFunction:operand
+ selectorName:@"mgl_stepWithMinimum:stops:"
+ arguments:@[minimum, stopExpression]];
+ } else if ([op isEqualToString:@"zoom"]) {
+ return [NSExpression expressionForVariable:@"zoomLevel"];
+ } else if ([op isEqualToString:@"heatmap-density"]) {
+ return [NSExpression expressionForVariable:@"heatmapDensity"];
+ } else if ([op isEqualToString:@"let"]) {
+ NSExpression *operand = [NSExpression mgl_expressionWithJSONObject:argumentObjects.lastObject];
+ NSArray *bindingObjects = [argumentObjects subarrayWithRange:NSMakeRange(0, argumentObjects.count - 1)];
+ NSMutableDictionary *context = [NSMutableDictionary dictionaryWithCapacity:bindingObjects.count / 2];
+ NSEnumerator *bindingEnumerator = bindingObjects.objectEnumerator;
+ while (NSString *key = bindingEnumerator.nextObject) {
+ context[key] = [NSExpression mgl_expressionWithJSONObject:bindingEnumerator.nextObject];
+ }
+ return [NSExpression expressionForFunction:operand
+ selectorName:@"mgl_expressionWithContext:"
+ arguments:@[[NSExpression expressionForConstantValue:context]]];
+ } 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]];
+ }
+ return [NSExpression expressionForConditional:conditional trueExpression:trueExpression falseExpression:falseExpression];
+ } else {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Expression operator %@ not yet implemented.", op];
+ }
+ }
+
+ [NSException raise:NSInvalidArgumentException
+ format:@"Unable to convert JSON object %@ to an NSExpression.", object];
+
+ return nil;
+}
+
+- (id)mgl_jsonExpressionObject {
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ MGLExpressionOperatorsByFunctionNames = @{
+ @"add:to:": @"+",
+ @"from:subtract:": @"-",
+ @"multiply:by:": @"*",
+ @"divide:by:": @"/",
+ @"modulus:by:": @"%",
+ @"sqrt:": @"sqrt",
+ @"log:": @"log10",
+ @"ln:": @"ln",
+ @"raise:toPower:": @"^",
+ @"uppercase:": @"upcase",
+ @"lowercase:": @"downcase",
+ };
+ });
+
+ switch (self.expressionType) {
+ case NSVariableExpressionType: {
+ if ([self.variable isEqualToString:@"heatmapDensity"]) {
+ return @[@"heatmap-density"];
+ }
+ if ([self.variable isEqualToString:@"zoomLevel"]) {
+ return @[@"zoom"];
+ }
+ return @[@"var", self.variable];
+ }
+
+ case NSConstantValueExpressionType: {
+ id constantValue = self.constantValue;
+ if (!constantValue || constantValue == [NSNull null]) {
+ return [NSNull null];
+ }
+ if ([constantValue isEqual:@(M_E)]) {
+ return @[@"e"];
+ }
+ if ([constantValue isEqual:@(M_PI)]) {
+ return @[@"pi"];
+ }
+ if ([constantValue isKindOfClass:[NSArray class]] ||
+ [constantValue isKindOfClass:[NSDictionary class]]) {
+ NSArray *collection = [constantValue mgl_jsonExpressionObject];
+ return @[@"literal", collection];
+ }
+ if ([constantValue isKindOfClass:[MGLColor class]]) {
+ auto color = [constantValue mgl_color];
+ if (color.a == 1) {
+ return @[@"rgb", @(color.r * 255), @(color.g * 255), @(color.b * 255)];
+ }
+ return @[@"rgba", @(color.r * 255), @(color.g * 255), @(color.b * 255), @(color.a)];
+ }
+ if ([constantValue isKindOfClass:[NSValue class]]) {
+ const auto boxedValue = (NSValue *)constantValue;
+ if (strcmp([boxedValue objCType], @encode(CGVector)) == 0) {
+ // offset [x, y]
+ std::array<float, 2> mglValue = boxedValue.mgl_offsetArrayValue;
+ return @[@"literal", @[@(mglValue[0]), @(mglValue[1])]];
+ }
+ if (strcmp([boxedValue objCType], @encode(MGLEdgeInsets)) == 0) {
+ // padding [x, y]
+ std::array<float, 4> mglValue = boxedValue.mgl_paddingArrayValue;
+ return @[@"literal", @[@(mglValue[0]), @(mglValue[1]), @(mglValue[2]), @(mglValue[3])]];
+ }
+ }
+ return self.constantValue;
+ }
+
+ case NSKeyPathExpressionType: {
+ NSArray *expressionObject;
+ for (NSString *pathComponent in self.keyPath.pathComponents.reverseObjectEnumerator) {
+ if (expressionObject) {
+ expressionObject = @[@"get", pathComponent, expressionObject];
+ } else {
+ expressionObject = @[@"get", pathComponent];
+ }
+ }
+ return expressionObject;
+ }
+
+ case NSFunctionExpressionType: {
+ NSString *function = self.function;
+ NSString *op = MGLExpressionOperatorsByFunctionNames[function];
+ if (op) {
+ NSArray *arguments = self.arguments.mgl_jsonExpressionObject;
+ return [@[op] arrayByAddingObjectsFromArray:arguments];
+ } else if ([function isEqualToString:@"valueForKeyPath:"]) {
+ return @[@"get", self.arguments.firstObject.mgl_jsonExpressionObject, self.operand.mgl_jsonExpressionObject];
+ } else if ([function isEqualToString:@"average:"]) {
+ NSExpression *sum = [NSExpression expressionForFunction:@"sum:" arguments:self.arguments];
+ NSExpression *count = [NSExpression expressionForFunction:@"count:" arguments:self.arguments];
+ return [NSExpression expressionForFunction:@"divide:by:" arguments:@[sum, count]].mgl_jsonExpressionObject;
+ } else if ([function isEqualToString:@"sum:"]) {
+ NSArray *arguments = [self.arguments.firstObject.collection valueForKeyPath:@"mgl_jsonExpressionObject"];
+ return [@[@"+"] arrayByAddingObjectsFromArray:arguments];
+ } else if ([function isEqualToString:@"count:"]) {
+ NSArray *arguments = self.arguments.firstObject.mgl_jsonExpressionObject;
+ return @[@"length", arguments];
+ } else if ([function isEqualToString:@"min:"]) {
+ NSArray *arguments = [self.arguments.firstObject.collection valueForKeyPath:@"mgl_jsonExpressionObject"];
+ return [@[@"min"] arrayByAddingObjectsFromArray:arguments];
+ } else if ([function isEqualToString:@"max:"]) {
+ NSArray *arguments = [self.arguments.firstObject.collection valueForKeyPath:@"mgl_jsonExpressionObject"];
+ return [@[@"max"] arrayByAddingObjectsFromArray:arguments];
+ } else if ([function isEqualToString:@"exp:"]) {
+ return [NSExpression expressionForFunction:@"raise:toPower:" arguments:@[@(M_E), self.arguments.firstObject]].mgl_jsonExpressionObject;
+ } else if ([function isEqualToString:@"ceiling:"]) {
+ return [NSExpression expressionWithFormat:@"trunc:(%@) + TERNARY(modulus:by:(%@, 1) > 0, 1, 0)",
+ self.arguments.firstObject, self.arguments.firstObject].mgl_jsonExpressionObject;
+ } else if ([function isEqualToString:@"trunc:"]) {
+ return [NSExpression expressionWithFormat:@"%@ - modulus:by:(%@, 1)",
+ self.arguments.firstObject, self.arguments.firstObject].mgl_jsonExpressionObject;
+ } else if ([function isEqualToString:@"abs:"]) {
+ return [NSExpression expressionWithFormat:@"%@ * TERNARY(%@ > 0, 1, -1)",
+ self.arguments.firstObject, self.arguments.firstObject].mgl_jsonExpressionObject;
+ } else if ([function isEqualToString:@"floor:"]) {
+ return [NSExpression expressionWithFormat:@"trunc:(%@) - TERNARY(modulus:by:(%@, 1) < 0, 1, 0)",
+ self.arguments.firstObject, self.arguments.firstObject].mgl_jsonExpressionObject;
+ } else if ([function isEqualToString:@"stringByAppendingString:"]) {
+ NSArray *arguments = self.arguments.mgl_jsonExpressionObject;
+ return [@[@"concat", self.operand.mgl_jsonExpressionObject] arrayByAddingObjectsFromArray:arguments];
+ } else if ([function isEqualToString:@"boolValue"]) {
+ return @[@"to-boolean", self.operand.mgl_jsonExpressionObject];
+ } else if ([function isEqualToString:@"mgl_numberWithFallbackValues:"] ||
+ [function isEqualToString:@"decimalValue"] ||
+ [function isEqualToString:@"floatValue"] ||
+ [function isEqualToString:@"doubleValue"]) {
+ NSArray *arguments = self.arguments.mgl_jsonExpressionObject;
+ return [@[@"to-number", self.operand.mgl_jsonExpressionObject] arrayByAddingObjectsFromArray:arguments];
+ } else if ([function isEqualToString:@"stringValue"]) {
+ return @[@"to-string", self.operand.mgl_jsonExpressionObject];
+ } else if ([function isEqualToString:@"noindex:"]) {
+ return self.arguments.firstObject.mgl_jsonExpressionObject;
+ } else if ([function isEqualToString:@"mgl_interpolateWithCurveType:parameters:stops:"]) {
+ if (self.arguments.count < 3) {
+ [NSException raise:NSInvalidArgumentException format:
+ @"Too few arguments to ‘mgl_interpolateWithCurveType:parameters:stops:’ function; expected 3 arguments."];
+ } else if (self.arguments.count > 3) {
+ [NSException raise:NSInvalidArgumentException format:
+ @"%lu unexpected arguments to ‘mgl_interpolateWithCurveType:parameters:stops:’ function; expected 3 arguments.",
+ self.arguments.count - 3];
+ }
+ NSString *curveType = self.arguments.firstObject.constantValue;
+ NSMutableArray *interpolationArray = [NSMutableArray arrayWithObject:curveType];
+ if ([curveType isEqualToString:@"exponential"]) {
+ id base = [self.arguments[1] mgl_jsonExpressionObject];
+ [interpolationArray addObject:base];
+ } else if ([curveType isEqualToString:@"cubic-bezier"]) {
+ NSArray *controlPoints = [self.arguments[1].collection mgl_jsonExpressionObject];
+ [interpolationArray addObjectsFromArray:controlPoints];
+ }
+ NSMutableArray *expressionObject = [NSMutableArray arrayWithObjects:@"interpolate", interpolationArray, self.operand.mgl_jsonExpressionObject, nil];
+ NSDictionary<NSNumber *, NSExpression *> *stops = self.arguments[2].constantValue;
+ for (NSNumber *key in [stops.allKeys sortedArrayUsingSelector:@selector(compare:)]) {
+ [expressionObject addObject:key];
+ [expressionObject addObject:[stops[key] mgl_jsonExpressionObject]];
+ }
+ return expressionObject;
+ } else if ([function isEqualToString:@"mgl_stepWithMinimum:stops:"]) {
+ id minimum = self.arguments.firstObject.mgl_jsonExpressionObject;
+ NSMutableArray *expressionObject = [NSMutableArray arrayWithObjects:@"step", self.operand.mgl_jsonExpressionObject, minimum, nil];
+ NSDictionary<NSNumber *, NSExpression *> *stops = self.arguments[1].constantValue;
+ for (NSNumber *key in [stops.allKeys sortedArrayUsingSelector:@selector(compare:)]) {
+ [expressionObject addObject:key];
+ [expressionObject addObject:[stops[key] mgl_jsonExpressionObject]];
+ }
+ return expressionObject;
+ } else if ([function isEqualToString:@"mgl_expressionWithContext:"]) {
+ id context = self.arguments.firstObject;
+ if ([context isKindOfClass:[NSExpression class]]) {
+ context = [context constantValue];
+ }
+ NSMutableArray *expressionObject = [NSMutableArray arrayWithObjects:@"let", nil];
+ [context enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, NSExpression * _Nonnull obj, BOOL * _Nonnull stop) {
+ [expressionObject addObject:key];
+ [expressionObject addObject:obj.mgl_jsonExpressionObject];
+ }];
+ [expressionObject addObject:self.operand.mgl_jsonExpressionObject];
+ return expressionObject;
+ } else if ([function isEqualToString:@"median:"] ||
+ [function isEqualToString:@"mode:"] ||
+ [function isEqualToString:@"stddev:"] ||
+ [function isEqualToString:@"random"] ||
+ [function isEqualToString:@"randomn:"] ||
+ [function isEqualToString:@"now"] ||
+ [function isEqualToString:@"bitwiseAnd:with:"] ||
+ [function isEqualToString:@"bitwiseOr:with:"] ||
+ [function isEqualToString:@"bitwiseXor:with:"] ||
+ [function isEqualToString:@"leftshift:by:"] ||
+ [function isEqualToString:@"rightshift:by:"] ||
+ [function isEqualToString:@"onesComplement:"] ||
+ [function isEqualToString:@"distanceToLocation:fromLocation:"]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Expression function %@ not yet implemented.", function];
+ return nil;
+ } else {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Unrecognized expression function %@.", function];
+ return nil;
+ }
+ }
+
+ case NSConditionalExpressionType: {
+ NSMutableArray *arguments = [NSMutableArray arrayWithObjects:self.predicate.mgl_jsonExpressionObject, self.trueExpression.mgl_jsonExpressionObject, nil];
+ if (self.falseExpression.expressionType == NSConditionalExpressionType) {
+ // Fold nested conditionals into a single case expression.
+ NSArray *falseArguments = self.falseExpression.mgl_jsonExpressionObject;
+ falseArguments = [falseArguments subarrayWithRange:NSMakeRange(1, falseArguments.count - 1)];
+ [arguments addObjectsFromArray:falseArguments];
+ } else {
+ [arguments addObject:self.falseExpression.mgl_jsonExpressionObject];
+ }
+
+ [arguments insertObject:@"case" atIndex:0];
+ return arguments;
+ }
+
+ case NSAggregateExpressionType: {
+ NSArray *collection = [self.collection valueForKeyPath:@"mgl_jsonExpressionObject"];
+ return @[@"literal", collection];
+ }
+
+ case NSEvaluatedObjectExpressionType:
+ case NSUnionSetExpressionType:
+ case NSIntersectSetExpressionType:
+ case NSMinusSetExpressionType:
+ case NSSubqueryExpressionType:
+ case NSAnyKeyExpressionType:
+ case NSBlockExpressionType:
+ [NSException raise:NSInvalidArgumentException
+ format:@"Expression type %lu not yet implemented.", self.expressionType];
+ }
+
+ // NSKeyPathSpecifierExpression
+ if (self.expressionType == 10) {
+ return self.description;
+ }
+ // An assignment expression type is present in the BNF grammar, but the
+ // corresponding NSExpressionType value and property getters are missing.
+ if (self.expressionType == 12) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Assignment expressions not yet implemented."];
+ }
+
+ return nil;
+}
+
+@end
diff --git a/platform/darwin/src/NSExpression+MGLPrivateAdditions.h b/platform/darwin/src/NSExpression+MGLPrivateAdditions.h
new file mode 100644
index 0000000000..8d1b4d6af5
--- /dev/null
+++ b/platform/darwin/src/NSExpression+MGLPrivateAdditions.h
@@ -0,0 +1,79 @@
+#import <Foundation/Foundation.h>
+#if TARGET_OS_IPHONE
+ #import <UIKit/UIKit.h>
+#else
+ #import <Cocoa/Cocoa.h>
+#endif
+
+#import "NSExpression+MGLAdditions.h"
+
+#include <mbgl/style/filter.hpp>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface NSObject (MGLExpressionAdditions)
+
+- (NSNumber *)mgl_number;
+- (NSNumber *)mgl_numberWithFallbackValues:(id)fallbackValue, ... NS_REQUIRES_NIL_TERMINATION;
+
+@end
+
+@interface NSExpression (MGLPrivateAdditions)
+
+@property (nonatomic, readonly) mbgl::Value mgl_constantMBGLValue;
+@property (nonatomic, readonly) std::vector<mbgl::Value> mgl_aggregateMBGLValue;
+@property (nonatomic, readonly) mbgl::FeatureType mgl_featureType;
+@property (nonatomic, readonly) std::vector<mbgl::FeatureType> mgl_aggregateFeatureType;
+@property (nonatomic, readonly) mbgl::FeatureIdentifier mgl_featureIdentifier;
+@property (nonatomic, readonly) std::vector<mbgl::FeatureIdentifier> mgl_aggregateFeatureIdentifier;
+
+@end
+
+@interface NSNull (MGLExpressionAdditions)
+
+@property (nonatomic, readonly) id mgl_jsonExpressionObject;
+
+@end
+
+@interface NSString (MGLExpressionAdditions)
+
+@property (nonatomic, readonly) id mgl_jsonExpressionObject;
+
+@end
+
+@interface NSNumber (MGLExpressionAdditions)
+
+- (id)mgl_interpolateWithCurveType:(NSString *)curveType parameters:(NSArray *)parameters stops:(NSDictionary<NSNumber *, id> *)stops;
+- (id)mgl_stepWithMinimum:(id)minimum stops:(NSDictionary<NSNumber *, id> *)stops;
+
+@property (nonatomic, readonly) id mgl_jsonExpressionObject;
+
+@end
+
+@interface NSArray (MGLExpressionAdditions)
+
+@property (nonatomic, readonly) id mgl_jsonExpressionObject;
+
+@end
+
+@interface NSDictionary (MGLExpressionAdditions)
+
+@property (nonatomic, readonly) id mgl_jsonExpressionObject;
+
+@end
+
+@interface MGLColor (MGLExpressionAdditions)
+
+@property (nonatomic, readonly) id mgl_jsonExpressionObject;
+
+@end
+
+@interface NSExpression (MGLExpressionAdditions)
+
+- (NSExpression *)mgl_expressionWithContext:(NSDictionary<NSString *, NSExpression *> *)context;
+
+@end
+
+extern NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects);
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/NSPredicate+MGLAdditions.h b/platform/darwin/src/NSPredicate+MGLAdditions.h
index fd774dd58b..89e9e65c64 100644
--- a/platform/darwin/src/NSPredicate+MGLAdditions.h
+++ b/platform/darwin/src/NSPredicate+MGLAdditions.h
@@ -1,7 +1,6 @@
#import <Foundation/Foundation.h>
-#import "NSExpression+MGLAdditions.h"
-#include <mbgl/style/filter.hpp>
+#import "NSExpression+MGLPrivateAdditions.h"
@interface NSPredicate (MGLAdditions)
@@ -11,3 +10,10 @@
@end
+@interface NSPredicate (MGLExpressionAdditions)
+
++ (instancetype)mgl_predicateWithJSONObject:(id)object;
+
+@property (nonatomic, readonly) id mgl_jsonExpressionObject;
+
+@end
diff --git a/platform/darwin/src/NSPredicate+MGLAdditions.mm b/platform/darwin/src/NSPredicate+MGLAdditions.mm
index e0511d8740..3ffe200d01 100644
--- a/platform/darwin/src/NSPredicate+MGLAdditions.mm
+++ b/platform/darwin/src/NSPredicate+MGLAdditions.mm
@@ -228,3 +228,94 @@ public:
}
@end
+
+@implementation NSPredicate (MGLExpressionAdditions)
+
+NSArray *MGLSubpredicatesWithJSONObjects(NSArray *objects) {
+ NSMutableArray *subpredicates = [NSMutableArray arrayWithCapacity:objects.count];
+ for (id object in objects) {
+ NSPredicate *predicate = [NSPredicate mgl_predicateWithJSONObject:object];
+ [subpredicates addObject:predicate];
+ }
+ return subpredicates;
+}
+
++ (instancetype)mgl_predicateWithJSONObject:(id)object {
+ if ([object isEqual:@YES]) {
+ return [NSPredicate predicateWithValue:YES];
+ }
+ if ([object isEqual:@NO]) {
+ return [NSPredicate predicateWithValue:NO];
+ }
+
+ NSAssert([object isKindOfClass:[NSArray class]], @"Condition for case expression should be an expression.");
+ NSArray *objects = (NSArray *)object;
+ NSString *op = objects.firstObject;
+
+ if ([op isEqualToString:@"=="]) {
+ NSArray *subexpressions = MGLSubexpressionsWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]);
+ return [NSPredicate predicateWithFormat:@"%@ == %@" argumentArray:subexpressions];
+ }
+ if ([op isEqualToString:@"!="]) {
+ NSArray *subexpressions = MGLSubexpressionsWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]);
+ return [NSPredicate predicateWithFormat:@"%@ != %@" argumentArray:subexpressions];
+ }
+ if ([op isEqualToString:@"<"]) {
+ NSArray *subexpressions = MGLSubexpressionsWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]);
+ return [NSPredicate predicateWithFormat:@"%@ < %@" argumentArray:subexpressions];
+ }
+ if ([op isEqualToString:@"<="]) {
+ NSArray *subexpressions = MGLSubexpressionsWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]);
+ return [NSPredicate predicateWithFormat:@"%@ <= %@" argumentArray:subexpressions];
+ }
+ if ([op isEqualToString:@">"]) {
+ NSArray *subexpressions = MGLSubexpressionsWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]);
+ return [NSPredicate predicateWithFormat:@"%@ > %@" argumentArray:subexpressions];
+ }
+ if ([op isEqualToString:@">="]) {
+ NSArray *subexpressions = MGLSubexpressionsWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]);
+ return [NSPredicate predicateWithFormat:@"%K >= %@" argumentArray:subexpressions];
+ }
+ if ([op isEqualToString:@"!"]) {
+ NSArray *subpredicates = MGLSubpredicatesWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]);
+ if (subpredicates.count > 1) {
+ NSCompoundPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:subpredicates];
+ return [NSCompoundPredicate notPredicateWithSubpredicate:predicate];
+ }
+ if (subpredicates.count) {
+ return [NSCompoundPredicate notPredicateWithSubpredicate:subpredicates.firstObject];
+ }
+ return [NSPredicate predicateWithValue:YES];
+ }
+ if ([op isEqualToString:@"all"]) {
+ NSArray *subpredicates = MGLSubpredicatesWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]);
+ return [NSCompoundPredicate andPredicateWithSubpredicates:subpredicates];
+ }
+ if ([op isEqualToString:@"any"]) {
+ NSArray *subpredicates = MGLSubpredicatesWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]);
+ return [NSCompoundPredicate orPredicateWithSubpredicates:subpredicates];
+ }
+
+ NSAssert(NO, @"Unrecognized expression conditional operator %@.", op);
+ return nil;
+}
+
+- (id)mgl_jsonExpressionObject {
+ if ([self isEqual:[NSPredicate predicateWithValue:YES]]) {
+ return @YES;
+ }
+ if ([self isEqual:[NSPredicate predicateWithValue:NO]]) {
+ return @NO;
+ }
+
+ if ([self.predicateFormat hasPrefix:@"BLOCKPREDICATE("]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Block-based predicates are not supported."];
+ }
+
+ [NSException raise:NSInvalidArgumentException
+ format:@"Unrecognized predicate type."];
+ return nil;
+}
+
+@end
diff --git a/platform/darwin/src/NSString+MGLAdditions.h b/platform/darwin/src/NSString+MGLAdditions.h
index d82ecaa671..75c593c10b 100644
--- a/platform/darwin/src/NSString+MGLAdditions.h
+++ b/platform/darwin/src/NSString+MGLAdditions.h
@@ -20,6 +20,20 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (NSString *)mgl_titleCasedStringWithLocale:(NSLocale *)locale;
+/**
+ Returns a transliterated representation of the receiver using the specified
+ script. If transliteration fails, the receiver will be returned.
+
+ Only supports scripts for languages used by Mapbox Streets.
+
+ On iOS 8 or older, this will method will always return the untransliterated
+ receiver.
+
+ @param script The four-letter code representing the name of the script, as
+ specified by ISO 15924.
+ */
+- (NSString *)mgl_stringByTransliteratingIntoScript:(NSString *)script;
+
@end
@interface NSAttributedString (MGLAdditions)
diff --git a/platform/darwin/src/NSString+MGLAdditions.m b/platform/darwin/src/NSString+MGLAdditions.m
index cde4bddcc3..8c9bbe3e21 100644
--- a/platform/darwin/src/NSString+MGLAdditions.m
+++ b/platform/darwin/src/NSString+MGLAdditions.m
@@ -41,6 +41,25 @@
return string;
}
+- (NSString *)mgl_stringByTransliteratingIntoScript:(NSString *)script {
+ if (@available(iOS 9.0, *)) {
+ NSMutableString *string = self.mutableCopy;
+ NSStringTransform transform;
+ if ([script isEqualToString:@"Latn"]) {
+ transform = NSStringTransformToLatin;
+ } else if ([script isEqualToString:@"Hans"]) {
+ // No transform available.
+ } else if ([script isEqualToString:@"Cyrl"]) {
+ transform = @"Any-Latin; Latin-Cyrillic";
+ } else if ([script isEqualToString:@"Arab"]) {
+ transform = @"Any-Latin; Latin-Arabic";
+ }
+ return transform ? [string stringByApplyingTransform:transform reverse:NO] : string;
+ } else {
+ return self;
+ }
+}
+
@end
@implementation NSAttributedString (MGLAdditions)
diff --git a/platform/darwin/src/headless_backend_cgl.cpp b/platform/darwin/src/headless_backend_cgl.cpp
index 3b0c3aaf35..46a5beb870 100644
--- a/platform/darwin/src/headless_backend_cgl.cpp
+++ b/platform/darwin/src/headless_backend_cgl.cpp
@@ -1,5 +1,5 @@
#include <mbgl/gl/headless_backend.hpp>
-#include <mbgl/gl/headless_display.hpp>
+#include <mbgl/util/logging.hpp>
#include <OpenGL/OpenGL.h>
#include <CoreFoundation/CoreFoundation.h>
@@ -9,14 +9,96 @@
namespace mbgl {
-struct CGLImpl : public HeadlessBackend::Impl {
- CGLImpl(CGLContextObj glContext_) : glContext(glContext_) {
+// This class provides a singleton that contains information about the pixel format used for
+// instantiating new headless rendering contexts.
+class CGLDisplayConfig {
+private:
+ // Key for singleton construction.
+ struct Key { explicit Key() = default; };
+
+public:
+ CGLDisplayConfig(Key) {
+ // TODO: test if OpenGL 4.1 with GL_ARB_ES2_compatibility is supported
+ // If it is, use kCGLOGLPVersion_3_2_Core and enable that extension.
+ CGLPixelFormatAttribute attributes[] = {
+ kCGLPFAOpenGLProfile, static_cast<CGLPixelFormatAttribute>(kCGLOGLPVersion_Legacy),
+ // Not adding kCGLPFAAccelerated, as this will make headless rendering impossible when running in VMs.
+ kCGLPFAClosestPolicy,
+ kCGLPFAAccumSize, static_cast<CGLPixelFormatAttribute>(32),
+ kCGLPFAColorSize, static_cast<CGLPixelFormatAttribute>(24),
+ kCGLPFAAlphaSize, static_cast<CGLPixelFormatAttribute>(8),
+ kCGLPFADepthSize, static_cast<CGLPixelFormatAttribute>(16),
+ kCGLPFAStencilSize, static_cast<CGLPixelFormatAttribute>(8),
+ kCGLPFASupportsAutomaticGraphicsSwitching,
+ kCGLPFAAllowOfflineRenderers, // Allows using the integrated GPU
+ static_cast<CGLPixelFormatAttribute>(0)
+ };
+
+ GLint num;
+ // TODO: obtain all configurations and choose the best one.
+ const CGLError error = CGLChoosePixelFormat(attributes, &pixelFormat, &num);
+ if (error != kCGLNoError) {
+ throw std::runtime_error(std::string("Error choosing pixel format:") + CGLErrorString(error) + "\n");
+ }
+ if (num <= 0) {
+ throw std::runtime_error("No pixel formats found.");
+ }
+ }
+
+ ~CGLDisplayConfig() {
+ const CGLError error = CGLDestroyPixelFormat(pixelFormat);
+ if (error != kCGLNoError) {
+ Log::Error(Event::OpenGL, std::string("Error destroying pixel format:") + CGLErrorString(error));
+ }
+ }
+
+ static std::shared_ptr<const CGLDisplayConfig> create() {
+ static std::weak_ptr<const CGLDisplayConfig> instance;
+ auto shared = instance.lock();
+ if (!shared) {
+ instance = shared = std::make_shared<CGLDisplayConfig>(Key{});
+ }
+ return shared;
+ }
+
+public:
+ CGLPixelFormatObj pixelFormat = nullptr;
+};
+
+class CGLBackendImpl : public HeadlessBackend::Impl {
+public:
+ CGLBackendImpl() {
+ CGLError error = CGLCreateContext(cglDisplay->pixelFormat, nullptr, &glContext);
+ if (error != kCGLNoError) {
+ throw std::runtime_error(std::string("Error creating GL context object:") +
+ CGLErrorString(error) + "\n");
+ }
+
+ error = CGLEnable(glContext, kCGLCEMPEngine);
+ if (error != kCGLNoError) {
+ throw std::runtime_error(std::string("Error enabling OpenGL multithreading:") +
+ CGLErrorString(error) + "\n");
+ }
}
- ~CGLImpl() {
+ ~CGLBackendImpl() final {
CGLDestroyContext(glContext);
}
+ gl::ProcAddress getExtensionFunctionPointer(const char* name) final {
+ static CFBundleRef framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl"));
+ if (!framework) {
+ throw std::runtime_error("Failed to load OpenGL framework.");
+ }
+
+ CFStringRef str =
+ CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingASCII);
+ void* symbol = CFBundleGetFunctionPointerForName(framework, str);
+ CFRelease(str);
+
+ return reinterpret_cast<gl::ProcAddress>(symbol);
+ }
+
void activateContext() final {
CGLError error = CGLSetCurrentContext(glContext);
if (error != kCGLNoError) {
@@ -33,46 +115,14 @@ struct CGLImpl : public HeadlessBackend::Impl {
}
}
+private:
+ const std::shared_ptr<const CGLDisplayConfig> cglDisplay = CGLDisplayConfig::create();
CGLContextObj glContext = nullptr;
};
-gl::ProcAddress HeadlessBackend::initializeExtension(const char* name) {
- static CFBundleRef framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl"));
- if (!framework) {
- throw std::runtime_error("Failed to load OpenGL framework.");
- }
-
- CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingASCII);
- void* symbol = CFBundleGetFunctionPointerForName(framework, str);
- CFRelease(str);
-
- return reinterpret_cast<gl::ProcAddress>(symbol);
-}
-
-bool HeadlessBackend::hasDisplay() {
- if (!display) {
- display = HeadlessDisplay::create();
- }
- return bool(display);
-}
-
-void HeadlessBackend::createContext() {
- assert(!hasContext());
-
- CGLContextObj glContext = nullptr;
- CGLError error = CGLCreateContext(display->attribute<CGLPixelFormatObj>(), nullptr, &glContext);
- if (error != kCGLNoError) {
- throw std::runtime_error(std::string("Error creating GL context object:") +
- CGLErrorString(error) + "\n");
- }
-
- error = CGLEnable(glContext, kCGLCEMPEngine);
- if (error != kCGLNoError) {
- throw std::runtime_error(std::string("Error enabling OpenGL multithreading:") +
- CGLErrorString(error) + "\n");
- }
-
- impl.reset(new CGLImpl(glContext));
+void HeadlessBackend::createImpl() {
+ assert(!impl);
+ impl = std::make_unique<CGLBackendImpl>();
}
} // namespace mbgl
diff --git a/platform/darwin/src/headless_backend_eagl.mm b/platform/darwin/src/headless_backend_eagl.mm
index 1daaeaf54c..050fa62c78 100644
--- a/platform/darwin/src/headless_backend_eagl.mm
+++ b/platform/darwin/src/headless_backend_eagl.mm
@@ -6,51 +6,48 @@
namespace mbgl {
-struct EAGLImpl : public HeadlessBackend::Impl {
- EAGLImpl(EAGLContext* glContext_) : glContext(glContext_) {
- [reinterpret_cast<EAGLContext*>(glContext) retain];
- reinterpret_cast<EAGLContext*>(glContext).multiThreaded = YES;
+class EAGLBackendImpl : public HeadlessBackend::Impl {
+public:
+ EAGLBackendImpl() {
+ glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
+ if (glContext == nil) {
+ throw std::runtime_error("Error creating GL context object");
+ }
+ glContext.multiThreaded = YES;
}
- ~EAGLImpl() {
- [glContext release];
+ // Required for ARC to deallocate correctly.
+ ~EAGLBackendImpl() final = default;
+
+ gl::ProcAddress getExtensionFunctionPointer(const char* name) final {
+ static CFBundleRef framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengles"));
+ if (!framework) {
+ throw std::runtime_error("Failed to load OpenGL framework.");
+ }
+
+ CFStringRef str =
+ CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingASCII);
+ void* symbol = CFBundleGetFunctionPointerForName(framework, str);
+ CFRelease(str);
+
+ return reinterpret_cast<gl::ProcAddress>(symbol);
}
- void activateContext() {
+ void activateContext() final {
[EAGLContext setCurrentContext:glContext];
}
- void deactivateContext() {
+ void deactivateContext() final {
[EAGLContext setCurrentContext:nil];
}
+private:
EAGLContext* glContext = nullptr;
};
-gl::ProcAddress HeadlessBackend::initializeExtension(const char* name) {
- static CFBundleRef framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengles"));
- if (!framework) {
- throw std::runtime_error("Failed to load OpenGL framework.");
- }
-
- CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingASCII);
- void* symbol = CFBundleGetFunctionPointerForName(framework, str);
- CFRelease(str);
-
- return reinterpret_cast<gl::ProcAddress>(symbol);
-}
-
-bool HeadlessBackend::hasDisplay() {
- return true;
-}
-
-void HeadlessBackend::createContext() {
- EAGLContext* glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
- if (glContext == nil) {
- throw std::runtime_error("Error creating GL context object");
- }
-
- impl.reset(new EAGLImpl(glContext));
+void HeadlessBackend::createImpl() {
+ assert(!impl);
+ impl = std::make_unique<EAGLBackendImpl>();
}
} // namespace mbgl
diff --git a/platform/darwin/src/headless_display_cgl.cpp b/platform/darwin/src/headless_display_cgl.cpp
deleted file mode 100644
index 5b7a1f2bac..0000000000
--- a/platform/darwin/src/headless_display_cgl.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-#include <mbgl/gl/headless_display.hpp>
-
-#include <OpenGL/OpenGL.h>
-
-#include <stdexcept>
-#include <string>
-
-namespace mbgl {
-
-class HeadlessDisplay::Impl {
-public:
- Impl();
- ~Impl();
- CGLPixelFormatObj pixelFormat = nullptr;
-};
-
-HeadlessDisplay::Impl::Impl() {
- // TODO: test if OpenGL 4.1 with GL_ARB_ES2_compatibility is supported
- // If it is, use kCGLOGLPVersion_3_2_Core and enable that extension.
- CGLPixelFormatAttribute attributes[] = {
- kCGLPFAOpenGLProfile, static_cast<CGLPixelFormatAttribute>(kCGLOGLPVersion_Legacy),
- // Not adding kCGLPFAAccelerated, as this will make headless rendering impossible when running in VMs.
- kCGLPFAClosestPolicy,
- kCGLPFAAccumSize, static_cast<CGLPixelFormatAttribute>(32),
- kCGLPFAColorSize, static_cast<CGLPixelFormatAttribute>(24),
- kCGLPFAAlphaSize, static_cast<CGLPixelFormatAttribute>(8),
- kCGLPFADepthSize, static_cast<CGLPixelFormatAttribute>(16),
- kCGLPFAStencilSize, static_cast<CGLPixelFormatAttribute>(8),
- kCGLPFASupportsAutomaticGraphicsSwitching,
- kCGLPFAAllowOfflineRenderers, // Allows using the integrated GPU
- static_cast<CGLPixelFormatAttribute>(0)
- };
-
- GLint num;
- CGLError error = CGLChoosePixelFormat(attributes, &pixelFormat, &num);
- if (error != kCGLNoError) {
- throw std::runtime_error(std::string("Error choosing pixel format:") + CGLErrorString(error) + "\n");
- }
- if (num <= 0) {
- throw std::runtime_error("No pixel formats found.");
- }
-}
-
-HeadlessDisplay::Impl::~Impl() {
- CGLDestroyPixelFormat(pixelFormat);
-}
-
-template <>
-CGLPixelFormatObj HeadlessDisplay::attribute() const {
- return impl->pixelFormat;
-}
-
-HeadlessDisplay::HeadlessDisplay()
- : impl(std::make_unique<Impl>()) {
-}
-
-HeadlessDisplay::~HeadlessDisplay() {
-}
-
-} // namespace mbgl
diff --git a/platform/darwin/src/image.mm b/platform/darwin/src/image.mm
index 8c0d5fa484..3a5adcca0a 100644
--- a/platform/darwin/src/image.mm
+++ b/platform/darwin/src/image.mm
@@ -11,7 +11,7 @@ using CGDataProviderHandle = CFHandle<CGDataProviderRef, CGDataProviderRef, CGDa
using CGColorSpaceHandle = CFHandle<CGColorSpaceRef, CGColorSpaceRef, CGColorSpaceRelease>;
using CGContextHandle = CFHandle<CGContextRef, CGContextRef, CGContextRelease>;
-CGImageRef CGImageFromMGLPremultipliedImage(mbgl::PremultipliedImage&& src) {
+CGImageRef CGImageCreateWithMGLPremultipliedImage(mbgl::PremultipliedImage&& src) {
// We're converting the PremultipliedImage's backing store to a CGDataProvider, and are taking
// over ownership of the memory.
CGDataProviderHandle provider(CGDataProviderCreateWithData(
diff --git a/platform/darwin/src/run_loop.cpp b/platform/darwin/src/run_loop.cpp
index d60a88cf52..0778b004e5 100644
--- a/platform/darwin/src/run_loop.cpp
+++ b/platform/darwin/src/run_loop.cpp
@@ -29,11 +29,8 @@ RunLoop::~RunLoop() {
Scheduler::SetCurrent(nullptr);
}
-void RunLoop::push(std::shared_ptr<WorkTask> task) {
- withMutex([&] {
- queue.push(std::move(task));
- impl->async->send();
- });
+void RunLoop::wake() {
+ impl->async->send();
}
void RunLoop::run() {
diff --git a/platform/darwin/test/MGLAttributionInfoTests.m b/platform/darwin/test/MGLAttributionInfoTests.m
index ed4927d44b..eccc6ceece 100644
--- a/platform/darwin/test/MGLAttributionInfoTests.m
+++ b/platform/darwin/test/MGLAttributionInfoTests.m
@@ -51,12 +51,12 @@
XCTAssertEqualObjects([infos[3] feedbackURLAtCenterCoordinate:mapbox zoomLevel:14],
[NSURL URLWithString:@"https://www.mapbox.com/feedback/?referrer=com.mapbox.sdk.ios#/77.63680/12.98108/14.00/0.0/0"]);
XCTAssertEqualObjects([infos[3] feedbackURLForStyleURL:styleURL atCenterCoordinate:mapbox zoomLevel:3.14159 direction:90.9 pitch:12.5],
- [NSURL URLWithString:@"https://www.mapbox.com/feedback/?referrer=com.mapbox.sdk.ios&owner=mapbox&id=satellite-streets-v99&access_token#/77.63680/12.98108/3.14/90.9/13"]);
+ [NSURL URLWithString:@"https://www.mapbox.com/feedback/?referrer=com.mapbox.sdk.ios&owner=mapbox&id=satellite-streets-v99&access_token&map_sdk_version=1.0.0#/77.63680/12.98108/3.14/90.9/13"]);
#else
XCTAssertEqualObjects([infos[3] feedbackURLAtCenterCoordinate:mapbox zoomLevel:14],
[NSURL URLWithString:@"https://www.mapbox.com/feedback/?referrer=com.mapbox.MapboxGL#/77.63680/12.98108/14.00/0.0/0"]);
XCTAssertEqualObjects([infos[3] feedbackURLForStyleURL:styleURL atCenterCoordinate:mapbox zoomLevel:3.14159 direction:90.9 pitch:12.5],
- [NSURL URLWithString:@"https://www.mapbox.com/feedback/?referrer=com.mapbox.MapboxGL&owner=mapbox&id=satellite-streets-v99&access_token#/77.63680/12.98108/3.14/90.9/13"]);
+ [NSURL URLWithString:@"https://www.mapbox.com/feedback/?referrer=com.mapbox.MapboxGL&owner=mapbox&id=satellite-streets-v99&access_token&map_sdk_version=1.0.0#/77.63680/12.98108/3.14/90.9/13"]);
#endif
}
diff --git a/platform/darwin/test/MGLBackgroundStyleLayerTests.mm b/platform/darwin/test/MGLBackgroundStyleLayerTests.mm
index c96a4fe7fa..e7c2982413 100644
--- a/platform/darwin/test/MGLBackgroundStyleLayerTests.mm
+++ b/platform/darwin/test/MGLBackgroundStyleLayerTests.mm
@@ -31,39 +31,44 @@
{
XCTAssertTrue(rawLayer->getBackgroundColor().isUndefined(),
@"background-color should be unset initially.");
- MGLStyleValue<MGLColor *> *defaultStyleValue = layer.backgroundColor;
+ NSExpression *defaultExpression = layer.backgroundColor;
- MGLStyleValue<MGLColor *> *constantStyleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]];
- layer.backgroundColor = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
+ layer.backgroundColor = constantExpression;
mbgl::style::PropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } };
XCTAssertEqual(rawLayer->getBackgroundColor(), propertyValue,
- @"Setting backgroundColor to a constant value should update background-color.");
- XCTAssertEqualObjects(layer.backgroundColor, constantStyleValue,
- @"backgroundColor should round-trip constant values.");
-
- MGLStyleValue<MGLColor *> * functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.backgroundColor = functionStyleValue;
-
- mbgl::style::IntervalStops<mbgl::Color> intervalStops = { {{18, { 1, 0, 0, 1 }}} };
+ @"Setting backgroundColor to a constant value expression should update background-color.");
+ XCTAssertEqualObjects(layer.backgroundColor, constantExpression,
+ @"backgroundColor should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.backgroundColor = functionExpression;
+
+ mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{
+ { -INFINITY, { 1, 0, 0, 1 } },
+ { 18, { 1, 0, 0, 1 } },
+ }};
propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops };
XCTAssertEqual(rawLayer->getBackgroundColor(), propertyValue,
- @"Setting backgroundColor to a camera function should update background-color.");
- XCTAssertEqualObjects(layer.backgroundColor, functionStyleValue,
- @"backgroundColor should round-trip camera functions.");
+ @"Setting backgroundColor to a camera expression should update background-color.");
+ XCTAssertEqualObjects(layer.backgroundColor, functionExpression,
+ @"backgroundColor should round-trip camera expressions.");
layer.backgroundColor = nil;
XCTAssertTrue(rawLayer->getBackgroundColor().isUndefined(),
@"Unsetting backgroundColor should return background-color to the default value.");
- XCTAssertEqualObjects(layer.backgroundColor, defaultStyleValue,
+ XCTAssertEqualObjects(layer.backgroundColor, defaultExpression,
@"backgroundColor should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.backgroundColor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.backgroundColor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.backgroundColor = functionExpression, NSException, NSInvalidArgumentException, @"MGLBackgroundLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.backgroundColor = functionExpression, NSException, NSInvalidArgumentException, @"MGLBackgroundLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
// Transition property test
layer.backgroundColorTransition = transitionTest;
auto toptions = rawLayer->getBackgroundColorTransition();
@@ -79,39 +84,44 @@
{
XCTAssertTrue(rawLayer->getBackgroundOpacity().isUndefined(),
@"background-opacity should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.backgroundOpacity;
+ NSExpression *defaultExpression = layer.backgroundOpacity;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.backgroundOpacity = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.backgroundOpacity = constantExpression;
mbgl::style::PropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getBackgroundOpacity(), propertyValue,
- @"Setting backgroundOpacity to a constant value should update background-opacity.");
- XCTAssertEqualObjects(layer.backgroundOpacity, constantStyleValue,
- @"backgroundOpacity should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.backgroundOpacity = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting backgroundOpacity to a constant value expression should update background-opacity.");
+ XCTAssertEqualObjects(layer.backgroundOpacity, constantExpression,
+ @"backgroundOpacity should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.backgroundOpacity = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getBackgroundOpacity(), propertyValue,
- @"Setting backgroundOpacity to a camera function should update background-opacity.");
- XCTAssertEqualObjects(layer.backgroundOpacity, functionStyleValue,
- @"backgroundOpacity should round-trip camera functions.");
+ @"Setting backgroundOpacity to a camera expression should update background-opacity.");
+ XCTAssertEqualObjects(layer.backgroundOpacity, functionExpression,
+ @"backgroundOpacity should round-trip camera expressions.");
layer.backgroundOpacity = nil;
XCTAssertTrue(rawLayer->getBackgroundOpacity().isUndefined(),
@"Unsetting backgroundOpacity should return background-opacity to the default value.");
- XCTAssertEqualObjects(layer.backgroundOpacity, defaultStyleValue,
+ XCTAssertEqualObjects(layer.backgroundOpacity, defaultExpression,
@"backgroundOpacity should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.backgroundOpacity = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.backgroundOpacity = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.backgroundOpacity = functionExpression, NSException, NSInvalidArgumentException, @"MGLBackgroundLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.backgroundOpacity = functionExpression, NSException, NSInvalidArgumentException, @"MGLBackgroundLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
// Transition property test
layer.backgroundOpacityTransition = transitionTest;
auto toptions = rawLayer->getBackgroundOpacityTransition();
@@ -127,39 +137,44 @@
{
XCTAssertTrue(rawLayer->getBackgroundPattern().isUndefined(),
@"background-pattern should be unset initially.");
- MGLStyleValue<NSString *> *defaultStyleValue = layer.backgroundPattern;
+ NSExpression *defaultExpression = layer.backgroundPattern;
- MGLStyleValue<NSString *> *constantStyleValue = [MGLStyleValue<NSString *> valueWithRawValue:@"Background Pattern"];
- layer.backgroundPattern = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'Background Pattern'"];
+ layer.backgroundPattern = constantExpression;
mbgl::style::PropertyValue<std::string> propertyValue = { "Background Pattern" };
XCTAssertEqual(rawLayer->getBackgroundPattern(), propertyValue,
- @"Setting backgroundPattern to a constant value should update background-pattern.");
- XCTAssertEqualObjects(layer.backgroundPattern, constantStyleValue,
- @"backgroundPattern should round-trip constant values.");
-
- MGLStyleValue<NSString *> * functionStyleValue = [MGLStyleValue<NSString *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.backgroundPattern = functionStyleValue;
-
- mbgl::style::IntervalStops<std::string> intervalStops = { {{18, "Background Pattern"}} };
+ @"Setting backgroundPattern to a constant value expression should update background-pattern.");
+ XCTAssertEqualObjects(layer.backgroundPattern, constantExpression,
+ @"backgroundPattern should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"'Background Pattern'"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.backgroundPattern = functionExpression;
+
+ mbgl::style::IntervalStops<std::string> intervalStops = {{
+ { -INFINITY, "Background Pattern" },
+ { 18, "Background Pattern" },
+ }};
propertyValue = mbgl::style::CameraFunction<std::string> { intervalStops };
XCTAssertEqual(rawLayer->getBackgroundPattern(), propertyValue,
- @"Setting backgroundPattern to a camera function should update background-pattern.");
- XCTAssertEqualObjects(layer.backgroundPattern, functionStyleValue,
- @"backgroundPattern should round-trip camera functions.");
+ @"Setting backgroundPattern to a camera expression should update background-pattern.");
+ XCTAssertEqualObjects(layer.backgroundPattern, functionExpression,
+ @"backgroundPattern should round-trip camera expressions.");
layer.backgroundPattern = nil;
XCTAssertTrue(rawLayer->getBackgroundPattern().isUndefined(),
@"Unsetting backgroundPattern should return background-pattern to the default value.");
- XCTAssertEqualObjects(layer.backgroundPattern, defaultStyleValue,
+ XCTAssertEqualObjects(layer.backgroundPattern, defaultExpression,
@"backgroundPattern should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSString *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.backgroundPattern = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSString *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.backgroundPattern = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.backgroundPattern = functionExpression, NSException, NSInvalidArgumentException, @"MGLBackgroundLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.backgroundPattern = functionExpression, NSException, NSInvalidArgumentException, @"MGLBackgroundLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
// Transition property test
layer.backgroundPatternTransition = transitionTest;
auto toptions = rawLayer->getBackgroundPatternTransition();
diff --git a/platform/darwin/test/MGLCircleStyleLayerTests.mm b/platform/darwin/test/MGLCircleStyleLayerTests.mm
index c0c503153a..7677344580 100644
--- a/platform/darwin/test/MGLCircleStyleLayerTests.mm
+++ b/platform/darwin/test/MGLCircleStyleLayerTests.mm
@@ -52,40 +52,44 @@
{
XCTAssertTrue(rawLayer->getCircleBlur().isUndefined(),
@"circle-blur should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.circleBlur;
+ NSExpression *defaultExpression = layer.circleBlur;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.circleBlur = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.circleBlur = constantExpression;
mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getCircleBlur(), propertyValue,
- @"Setting circleBlur to a constant value should update circle-blur.");
- XCTAssertEqualObjects(layer.circleBlur, constantStyleValue,
- @"circleBlur should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.circleBlur = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting circleBlur to a constant value expression should update circle-blur.");
+ XCTAssertEqualObjects(layer.circleBlur, constantExpression,
+ @"circleBlur should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.circleBlur = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getCircleBlur(), propertyValue,
- @"Setting circleBlur to a camera function should update circle-blur.");
- XCTAssertEqualObjects(layer.circleBlur, functionStyleValue,
- @"circleBlur should round-trip camera functions.");
+ @"Setting circleBlur to a camera expression should update circle-blur.");
+ XCTAssertEqualObjects(layer.circleBlur, functionExpression,
+ @"circleBlur should round-trip camera expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
- layer.circleBlur = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.circleBlur = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getCircleBlur(), propertyValue,
- @"Setting circleBlur to a source function should update circle-blur.");
- XCTAssertEqualObjects(layer.circleBlur, functionStyleValue,
- @"circleBlur should round-trip source functions.");
+ @"Setting circleBlur to a data expression should update circle-blur.");
+ XCTAssertEqualObjects(layer.circleBlur, functionExpression,
+ @"circleBlur should round-trip data expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
- layer.circleBlur = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.circleBlur = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 };
@@ -93,15 +97,15 @@
propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getCircleBlur(), propertyValue,
- @"Setting circleBlur to a composite function should update circle-blur.");
- XCTAssertEqualObjects(layer.circleBlur, functionStyleValue,
- @"circleBlur should round-trip composite functions.");
+ @"Setting circleBlur to a camera-data expression should update circle-blur.");
+ XCTAssertEqualObjects(layer.circleBlur, functionExpression,
+ @"circleBlur should round-trip camera-data expressions.");
layer.circleBlur = nil;
XCTAssertTrue(rawLayer->getCircleBlur().isUndefined(),
@"Unsetting circleBlur should return circle-blur to the default value.");
- XCTAssertEqualObjects(layer.circleBlur, defaultStyleValue,
+ XCTAssertEqualObjects(layer.circleBlur, defaultExpression,
@"circleBlur should return the default value after being unset.");
// Transition property test
layer.circleBlurTransition = transitionTest;
@@ -118,40 +122,44 @@
{
XCTAssertTrue(rawLayer->getCircleColor().isUndefined(),
@"circle-color should be unset initially.");
- MGLStyleValue<MGLColor *> *defaultStyleValue = layer.circleColor;
+ NSExpression *defaultExpression = layer.circleColor;
- MGLStyleValue<MGLColor *> *constantStyleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]];
- layer.circleColor = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
+ layer.circleColor = constantExpression;
mbgl::style::DataDrivenPropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } };
XCTAssertEqual(rawLayer->getCircleColor(), propertyValue,
- @"Setting circleColor to a constant value should update circle-color.");
- XCTAssertEqualObjects(layer.circleColor, constantStyleValue,
- @"circleColor should round-trip constant values.");
-
- MGLStyleValue<MGLColor *> * functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.circleColor = functionStyleValue;
-
- mbgl::style::IntervalStops<mbgl::Color> intervalStops = { {{18, { 1, 0, 0, 1 }}} };
+ @"Setting circleColor to a constant value expression should update circle-color.");
+ XCTAssertEqualObjects(layer.circleColor, constantExpression,
+ @"circleColor should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.circleColor = functionExpression;
+
+ mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{
+ { -INFINITY, { 1, 0, 0, 1 } },
+ { 18, { 1, 0, 0, 1 } },
+ }};
propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops };
XCTAssertEqual(rawLayer->getCircleColor(), propertyValue,
- @"Setting circleColor to a camera function should update circle-color.");
- XCTAssertEqualObjects(layer.circleColor, functionStyleValue,
- @"circleColor should round-trip camera functions.");
+ @"Setting circleColor to a camera expression should update circle-color.");
+ XCTAssertEqualObjects(layer.circleColor, functionExpression,
+ @"circleColor should round-trip camera expressions.");
- functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
- layer.circleColor = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.circleColor = functionExpression;
mbgl::style::ExponentialStops<mbgl::Color> exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<mbgl::Color> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getCircleColor(), propertyValue,
- @"Setting circleColor to a source function should update circle-color.");
- XCTAssertEqualObjects(layer.circleColor, functionStyleValue,
- @"circleColor should round-trip source functions.");
+ @"Setting circleColor to a data expression should update circle-color.");
+ XCTAssertEqualObjects(layer.circleColor, functionExpression,
+ @"circleColor should round-trip data expressions.");
- functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
- layer.circleColor = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.circleColor = functionExpression;
std::map<float, mbgl::Color> innerStops { {18, { 1, 0, 0, 1 }} };
mbgl::style::CompositeExponentialStops<mbgl::Color> compositeStops { { {10.0, innerStops} }, 1.0 };
@@ -159,15 +167,15 @@
propertyValue = mbgl::style::CompositeFunction<mbgl::Color> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getCircleColor(), propertyValue,
- @"Setting circleColor to a composite function should update circle-color.");
- XCTAssertEqualObjects(layer.circleColor, functionStyleValue,
- @"circleColor should round-trip composite functions.");
+ @"Setting circleColor to a camera-data expression should update circle-color.");
+ XCTAssertEqualObjects(layer.circleColor, functionExpression,
+ @"circleColor should round-trip camera-data expressions.");
layer.circleColor = nil;
XCTAssertTrue(rawLayer->getCircleColor().isUndefined(),
@"Unsetting circleColor should return circle-color to the default value.");
- XCTAssertEqualObjects(layer.circleColor, defaultStyleValue,
+ XCTAssertEqualObjects(layer.circleColor, defaultExpression,
@"circleColor should return the default value after being unset.");
// Transition property test
layer.circleColorTransition = transitionTest;
@@ -184,40 +192,44 @@
{
XCTAssertTrue(rawLayer->getCircleOpacity().isUndefined(),
@"circle-opacity should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.circleOpacity;
+ NSExpression *defaultExpression = layer.circleOpacity;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.circleOpacity = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.circleOpacity = constantExpression;
mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getCircleOpacity(), propertyValue,
- @"Setting circleOpacity to a constant value should update circle-opacity.");
- XCTAssertEqualObjects(layer.circleOpacity, constantStyleValue,
- @"circleOpacity should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.circleOpacity = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting circleOpacity to a constant value expression should update circle-opacity.");
+ XCTAssertEqualObjects(layer.circleOpacity, constantExpression,
+ @"circleOpacity should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.circleOpacity = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getCircleOpacity(), propertyValue,
- @"Setting circleOpacity to a camera function should update circle-opacity.");
- XCTAssertEqualObjects(layer.circleOpacity, functionStyleValue,
- @"circleOpacity should round-trip camera functions.");
+ @"Setting circleOpacity to a camera expression should update circle-opacity.");
+ XCTAssertEqualObjects(layer.circleOpacity, functionExpression,
+ @"circleOpacity should round-trip camera expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
- layer.circleOpacity = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.circleOpacity = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getCircleOpacity(), propertyValue,
- @"Setting circleOpacity to a source function should update circle-opacity.");
- XCTAssertEqualObjects(layer.circleOpacity, functionStyleValue,
- @"circleOpacity should round-trip source functions.");
+ @"Setting circleOpacity to a data expression should update circle-opacity.");
+ XCTAssertEqualObjects(layer.circleOpacity, functionExpression,
+ @"circleOpacity should round-trip data expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
- layer.circleOpacity = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.circleOpacity = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 };
@@ -225,15 +237,15 @@
propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getCircleOpacity(), propertyValue,
- @"Setting circleOpacity to a composite function should update circle-opacity.");
- XCTAssertEqualObjects(layer.circleOpacity, functionStyleValue,
- @"circleOpacity should round-trip composite functions.");
+ @"Setting circleOpacity to a camera-data expression should update circle-opacity.");
+ XCTAssertEqualObjects(layer.circleOpacity, functionExpression,
+ @"circleOpacity should round-trip camera-data expressions.");
layer.circleOpacity = nil;
XCTAssertTrue(rawLayer->getCircleOpacity().isUndefined(),
@"Unsetting circleOpacity should return circle-opacity to the default value.");
- XCTAssertEqualObjects(layer.circleOpacity, defaultStyleValue,
+ XCTAssertEqualObjects(layer.circleOpacity, defaultExpression,
@"circleOpacity should return the default value after being unset.");
// Transition property test
layer.circleOpacityTransition = transitionTest;
@@ -250,79 +262,88 @@
{
XCTAssertTrue(rawLayer->getCirclePitchAlignment().isUndefined(),
@"circle-pitch-alignment should be unset initially.");
- MGLStyleValue<NSValue *> *defaultStyleValue = layer.circlePitchAlignment;
+ NSExpression *defaultExpression = layer.circlePitchAlignment;
- MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLCirclePitchAlignment:MGLCirclePitchAlignmentViewport]];
- layer.circlePitchAlignment = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'viewport'"];
+ layer.circlePitchAlignment = constantExpression;
mbgl::style::PropertyValue<mbgl::style::AlignmentType> propertyValue = { mbgl::style::AlignmentType::Viewport };
XCTAssertEqual(rawLayer->getCirclePitchAlignment(), propertyValue,
- @"Setting circlePitchAlignment to a constant value should update circle-pitch-alignment.");
- XCTAssertEqualObjects(layer.circlePitchAlignment, constantStyleValue,
- @"circlePitchAlignment should round-trip constant values.");
-
- MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.circlePitchAlignment = functionStyleValue;
-
- mbgl::style::IntervalStops<mbgl::style::AlignmentType> intervalStops = { {{18, mbgl::style::AlignmentType::Viewport}} };
+ @"Setting circlePitchAlignment to a constant value expression should update circle-pitch-alignment.");
+ XCTAssertEqualObjects(layer.circlePitchAlignment, constantExpression,
+ @"circlePitchAlignment should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"'viewport'"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.circlePitchAlignment = functionExpression;
+
+ mbgl::style::IntervalStops<mbgl::style::AlignmentType> intervalStops = {{
+ { -INFINITY, mbgl::style::AlignmentType::Viewport },
+ { 18, mbgl::style::AlignmentType::Viewport },
+ }};
propertyValue = mbgl::style::CameraFunction<mbgl::style::AlignmentType> { intervalStops };
XCTAssertEqual(rawLayer->getCirclePitchAlignment(), propertyValue,
- @"Setting circlePitchAlignment to a camera function should update circle-pitch-alignment.");
- XCTAssertEqualObjects(layer.circlePitchAlignment, functionStyleValue,
- @"circlePitchAlignment should round-trip camera functions.");
+ @"Setting circlePitchAlignment to a camera expression should update circle-pitch-alignment.");
+ XCTAssertEqualObjects(layer.circlePitchAlignment, functionExpression,
+ @"circlePitchAlignment should round-trip camera expressions.");
layer.circlePitchAlignment = nil;
XCTAssertTrue(rawLayer->getCirclePitchAlignment().isUndefined(),
@"Unsetting circlePitchAlignment should return circle-pitch-alignment to the default value.");
- XCTAssertEqualObjects(layer.circlePitchAlignment, defaultStyleValue,
+ XCTAssertEqualObjects(layer.circlePitchAlignment, defaultExpression,
@"circlePitchAlignment should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.circlePitchAlignment = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.circlePitchAlignment = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.circlePitchAlignment = functionExpression, NSException, NSInvalidArgumentException, @"MGLCircleLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.circlePitchAlignment = functionExpression, NSException, NSInvalidArgumentException, @"MGLCircleLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// circle-radius
{
XCTAssertTrue(rawLayer->getCircleRadius().isUndefined(),
@"circle-radius should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.circleRadius;
+ NSExpression *defaultExpression = layer.circleRadius;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.circleRadius = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.circleRadius = constantExpression;
mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getCircleRadius(), propertyValue,
- @"Setting circleRadius to a constant value should update circle-radius.");
- XCTAssertEqualObjects(layer.circleRadius, constantStyleValue,
- @"circleRadius should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.circleRadius = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting circleRadius to a constant value expression should update circle-radius.");
+ XCTAssertEqualObjects(layer.circleRadius, constantExpression,
+ @"circleRadius should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.circleRadius = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getCircleRadius(), propertyValue,
- @"Setting circleRadius to a camera function should update circle-radius.");
- XCTAssertEqualObjects(layer.circleRadius, functionStyleValue,
- @"circleRadius should round-trip camera functions.");
+ @"Setting circleRadius to a camera expression should update circle-radius.");
+ XCTAssertEqualObjects(layer.circleRadius, functionExpression,
+ @"circleRadius should round-trip camera expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
- layer.circleRadius = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.circleRadius = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getCircleRadius(), propertyValue,
- @"Setting circleRadius to a source function should update circle-radius.");
- XCTAssertEqualObjects(layer.circleRadius, functionStyleValue,
- @"circleRadius should round-trip source functions.");
+ @"Setting circleRadius to a data expression should update circle-radius.");
+ XCTAssertEqualObjects(layer.circleRadius, functionExpression,
+ @"circleRadius should round-trip data expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
- layer.circleRadius = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.circleRadius = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 };
@@ -330,15 +351,15 @@
propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getCircleRadius(), propertyValue,
- @"Setting circleRadius to a composite function should update circle-radius.");
- XCTAssertEqualObjects(layer.circleRadius, functionStyleValue,
- @"circleRadius should round-trip composite functions.");
+ @"Setting circleRadius to a camera-data expression should update circle-radius.");
+ XCTAssertEqualObjects(layer.circleRadius, functionExpression,
+ @"circleRadius should round-trip camera-data expressions.");
layer.circleRadius = nil;
XCTAssertTrue(rawLayer->getCircleRadius().isUndefined(),
@"Unsetting circleRadius should return circle-radius to the default value.");
- XCTAssertEqualObjects(layer.circleRadius, defaultStyleValue,
+ XCTAssertEqualObjects(layer.circleRadius, defaultExpression,
@"circleRadius should return the default value after being unset.");
// Transition property test
layer.circleRadiusTransition = transitionTest;
@@ -355,79 +376,88 @@
{
XCTAssertTrue(rawLayer->getCirclePitchScale().isUndefined(),
@"circle-pitch-scale should be unset initially.");
- MGLStyleValue<NSValue *> *defaultStyleValue = layer.circleScaleAlignment;
+ NSExpression *defaultExpression = layer.circleScaleAlignment;
- MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLCircleScaleAlignment:MGLCircleScaleAlignmentViewport]];
- layer.circleScaleAlignment = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'viewport'"];
+ layer.circleScaleAlignment = constantExpression;
mbgl::style::PropertyValue<mbgl::style::CirclePitchScaleType> propertyValue = { mbgl::style::CirclePitchScaleType::Viewport };
XCTAssertEqual(rawLayer->getCirclePitchScale(), propertyValue,
- @"Setting circleScaleAlignment to a constant value should update circle-pitch-scale.");
- XCTAssertEqualObjects(layer.circleScaleAlignment, constantStyleValue,
- @"circleScaleAlignment should round-trip constant values.");
-
- MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.circleScaleAlignment = functionStyleValue;
-
- mbgl::style::IntervalStops<mbgl::style::CirclePitchScaleType> intervalStops = { {{18, mbgl::style::CirclePitchScaleType::Viewport}} };
+ @"Setting circleScaleAlignment to a constant value expression should update circle-pitch-scale.");
+ XCTAssertEqualObjects(layer.circleScaleAlignment, constantExpression,
+ @"circleScaleAlignment should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"'viewport'"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.circleScaleAlignment = functionExpression;
+
+ mbgl::style::IntervalStops<mbgl::style::CirclePitchScaleType> intervalStops = {{
+ { -INFINITY, mbgl::style::CirclePitchScaleType::Viewport },
+ { 18, mbgl::style::CirclePitchScaleType::Viewport },
+ }};
propertyValue = mbgl::style::CameraFunction<mbgl::style::CirclePitchScaleType> { intervalStops };
XCTAssertEqual(rawLayer->getCirclePitchScale(), propertyValue,
- @"Setting circleScaleAlignment to a camera function should update circle-pitch-scale.");
- XCTAssertEqualObjects(layer.circleScaleAlignment, functionStyleValue,
- @"circleScaleAlignment should round-trip camera functions.");
+ @"Setting circleScaleAlignment to a camera expression should update circle-pitch-scale.");
+ XCTAssertEqualObjects(layer.circleScaleAlignment, functionExpression,
+ @"circleScaleAlignment should round-trip camera expressions.");
layer.circleScaleAlignment = nil;
XCTAssertTrue(rawLayer->getCirclePitchScale().isUndefined(),
@"Unsetting circleScaleAlignment should return circle-pitch-scale to the default value.");
- XCTAssertEqualObjects(layer.circleScaleAlignment, defaultStyleValue,
+ XCTAssertEqualObjects(layer.circleScaleAlignment, defaultExpression,
@"circleScaleAlignment should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.circleScaleAlignment = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.circleScaleAlignment = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.circleScaleAlignment = functionExpression, NSException, NSInvalidArgumentException, @"MGLCircleLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.circleScaleAlignment = functionExpression, NSException, NSInvalidArgumentException, @"MGLCircleLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// circle-stroke-color
{
XCTAssertTrue(rawLayer->getCircleStrokeColor().isUndefined(),
@"circle-stroke-color should be unset initially.");
- MGLStyleValue<MGLColor *> *defaultStyleValue = layer.circleStrokeColor;
+ NSExpression *defaultExpression = layer.circleStrokeColor;
- MGLStyleValue<MGLColor *> *constantStyleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]];
- layer.circleStrokeColor = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
+ layer.circleStrokeColor = constantExpression;
mbgl::style::DataDrivenPropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } };
XCTAssertEqual(rawLayer->getCircleStrokeColor(), propertyValue,
- @"Setting circleStrokeColor to a constant value should update circle-stroke-color.");
- XCTAssertEqualObjects(layer.circleStrokeColor, constantStyleValue,
- @"circleStrokeColor should round-trip constant values.");
-
- MGLStyleValue<MGLColor *> * functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.circleStrokeColor = functionStyleValue;
-
- mbgl::style::IntervalStops<mbgl::Color> intervalStops = { {{18, { 1, 0, 0, 1 }}} };
+ @"Setting circleStrokeColor to a constant value expression should update circle-stroke-color.");
+ XCTAssertEqualObjects(layer.circleStrokeColor, constantExpression,
+ @"circleStrokeColor should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.circleStrokeColor = functionExpression;
+
+ mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{
+ { -INFINITY, { 1, 0, 0, 1 } },
+ { 18, { 1, 0, 0, 1 } },
+ }};
propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops };
XCTAssertEqual(rawLayer->getCircleStrokeColor(), propertyValue,
- @"Setting circleStrokeColor to a camera function should update circle-stroke-color.");
- XCTAssertEqualObjects(layer.circleStrokeColor, functionStyleValue,
- @"circleStrokeColor should round-trip camera functions.");
+ @"Setting circleStrokeColor to a camera expression should update circle-stroke-color.");
+ XCTAssertEqualObjects(layer.circleStrokeColor, functionExpression,
+ @"circleStrokeColor should round-trip camera expressions.");
- functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
- layer.circleStrokeColor = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.circleStrokeColor = functionExpression;
mbgl::style::ExponentialStops<mbgl::Color> exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<mbgl::Color> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getCircleStrokeColor(), propertyValue,
- @"Setting circleStrokeColor to a source function should update circle-stroke-color.");
- XCTAssertEqualObjects(layer.circleStrokeColor, functionStyleValue,
- @"circleStrokeColor should round-trip source functions.");
+ @"Setting circleStrokeColor to a data expression should update circle-stroke-color.");
+ XCTAssertEqualObjects(layer.circleStrokeColor, functionExpression,
+ @"circleStrokeColor should round-trip data expressions.");
- functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
- layer.circleStrokeColor = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.circleStrokeColor = functionExpression;
std::map<float, mbgl::Color> innerStops { {18, { 1, 0, 0, 1 }} };
mbgl::style::CompositeExponentialStops<mbgl::Color> compositeStops { { {10.0, innerStops} }, 1.0 };
@@ -435,15 +465,15 @@
propertyValue = mbgl::style::CompositeFunction<mbgl::Color> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getCircleStrokeColor(), propertyValue,
- @"Setting circleStrokeColor to a composite function should update circle-stroke-color.");
- XCTAssertEqualObjects(layer.circleStrokeColor, functionStyleValue,
- @"circleStrokeColor should round-trip composite functions.");
+ @"Setting circleStrokeColor to a camera-data expression should update circle-stroke-color.");
+ XCTAssertEqualObjects(layer.circleStrokeColor, functionExpression,
+ @"circleStrokeColor should round-trip camera-data expressions.");
layer.circleStrokeColor = nil;
XCTAssertTrue(rawLayer->getCircleStrokeColor().isUndefined(),
@"Unsetting circleStrokeColor should return circle-stroke-color to the default value.");
- XCTAssertEqualObjects(layer.circleStrokeColor, defaultStyleValue,
+ XCTAssertEqualObjects(layer.circleStrokeColor, defaultExpression,
@"circleStrokeColor should return the default value after being unset.");
// Transition property test
layer.circleStrokeColorTransition = transitionTest;
@@ -460,40 +490,44 @@
{
XCTAssertTrue(rawLayer->getCircleStrokeOpacity().isUndefined(),
@"circle-stroke-opacity should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.circleStrokeOpacity;
+ NSExpression *defaultExpression = layer.circleStrokeOpacity;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.circleStrokeOpacity = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.circleStrokeOpacity = constantExpression;
mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getCircleStrokeOpacity(), propertyValue,
- @"Setting circleStrokeOpacity to a constant value should update circle-stroke-opacity.");
- XCTAssertEqualObjects(layer.circleStrokeOpacity, constantStyleValue,
- @"circleStrokeOpacity should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.circleStrokeOpacity = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting circleStrokeOpacity to a constant value expression should update circle-stroke-opacity.");
+ XCTAssertEqualObjects(layer.circleStrokeOpacity, constantExpression,
+ @"circleStrokeOpacity should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.circleStrokeOpacity = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getCircleStrokeOpacity(), propertyValue,
- @"Setting circleStrokeOpacity to a camera function should update circle-stroke-opacity.");
- XCTAssertEqualObjects(layer.circleStrokeOpacity, functionStyleValue,
- @"circleStrokeOpacity should round-trip camera functions.");
+ @"Setting circleStrokeOpacity to a camera expression should update circle-stroke-opacity.");
+ XCTAssertEqualObjects(layer.circleStrokeOpacity, functionExpression,
+ @"circleStrokeOpacity should round-trip camera expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
- layer.circleStrokeOpacity = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.circleStrokeOpacity = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getCircleStrokeOpacity(), propertyValue,
- @"Setting circleStrokeOpacity to a source function should update circle-stroke-opacity.");
- XCTAssertEqualObjects(layer.circleStrokeOpacity, functionStyleValue,
- @"circleStrokeOpacity should round-trip source functions.");
+ @"Setting circleStrokeOpacity to a data expression should update circle-stroke-opacity.");
+ XCTAssertEqualObjects(layer.circleStrokeOpacity, functionExpression,
+ @"circleStrokeOpacity should round-trip data expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
- layer.circleStrokeOpacity = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.circleStrokeOpacity = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 };
@@ -501,15 +535,15 @@
propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getCircleStrokeOpacity(), propertyValue,
- @"Setting circleStrokeOpacity to a composite function should update circle-stroke-opacity.");
- XCTAssertEqualObjects(layer.circleStrokeOpacity, functionStyleValue,
- @"circleStrokeOpacity should round-trip composite functions.");
+ @"Setting circleStrokeOpacity to a camera-data expression should update circle-stroke-opacity.");
+ XCTAssertEqualObjects(layer.circleStrokeOpacity, functionExpression,
+ @"circleStrokeOpacity should round-trip camera-data expressions.");
layer.circleStrokeOpacity = nil;
XCTAssertTrue(rawLayer->getCircleStrokeOpacity().isUndefined(),
@"Unsetting circleStrokeOpacity should return circle-stroke-opacity to the default value.");
- XCTAssertEqualObjects(layer.circleStrokeOpacity, defaultStyleValue,
+ XCTAssertEqualObjects(layer.circleStrokeOpacity, defaultExpression,
@"circleStrokeOpacity should return the default value after being unset.");
// Transition property test
layer.circleStrokeOpacityTransition = transitionTest;
@@ -526,40 +560,44 @@
{
XCTAssertTrue(rawLayer->getCircleStrokeWidth().isUndefined(),
@"circle-stroke-width should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.circleStrokeWidth;
+ NSExpression *defaultExpression = layer.circleStrokeWidth;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.circleStrokeWidth = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.circleStrokeWidth = constantExpression;
mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getCircleStrokeWidth(), propertyValue,
- @"Setting circleStrokeWidth to a constant value should update circle-stroke-width.");
- XCTAssertEqualObjects(layer.circleStrokeWidth, constantStyleValue,
- @"circleStrokeWidth should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.circleStrokeWidth = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting circleStrokeWidth to a constant value expression should update circle-stroke-width.");
+ XCTAssertEqualObjects(layer.circleStrokeWidth, constantExpression,
+ @"circleStrokeWidth should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.circleStrokeWidth = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getCircleStrokeWidth(), propertyValue,
- @"Setting circleStrokeWidth to a camera function should update circle-stroke-width.");
- XCTAssertEqualObjects(layer.circleStrokeWidth, functionStyleValue,
- @"circleStrokeWidth should round-trip camera functions.");
+ @"Setting circleStrokeWidth to a camera expression should update circle-stroke-width.");
+ XCTAssertEqualObjects(layer.circleStrokeWidth, functionExpression,
+ @"circleStrokeWidth should round-trip camera expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
- layer.circleStrokeWidth = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.circleStrokeWidth = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getCircleStrokeWidth(), propertyValue,
- @"Setting circleStrokeWidth to a source function should update circle-stroke-width.");
- XCTAssertEqualObjects(layer.circleStrokeWidth, functionStyleValue,
- @"circleStrokeWidth should round-trip source functions.");
+ @"Setting circleStrokeWidth to a data expression should update circle-stroke-width.");
+ XCTAssertEqualObjects(layer.circleStrokeWidth, functionExpression,
+ @"circleStrokeWidth should round-trip data expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
- layer.circleStrokeWidth = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.circleStrokeWidth = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 };
@@ -567,15 +605,15 @@
propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getCircleStrokeWidth(), propertyValue,
- @"Setting circleStrokeWidth to a composite function should update circle-stroke-width.");
- XCTAssertEqualObjects(layer.circleStrokeWidth, functionStyleValue,
- @"circleStrokeWidth should round-trip composite functions.");
+ @"Setting circleStrokeWidth to a camera-data expression should update circle-stroke-width.");
+ XCTAssertEqualObjects(layer.circleStrokeWidth, functionExpression,
+ @"circleStrokeWidth should round-trip camera-data expressions.");
layer.circleStrokeWidth = nil;
XCTAssertTrue(rawLayer->getCircleStrokeWidth().isUndefined(),
@"Unsetting circleStrokeWidth should return circle-stroke-width to the default value.");
- XCTAssertEqualObjects(layer.circleStrokeWidth, defaultStyleValue,
+ XCTAssertEqualObjects(layer.circleStrokeWidth, defaultExpression,
@"circleStrokeWidth should return the default value after being unset.");
// Transition property test
layer.circleStrokeWidthTransition = transitionTest;
@@ -592,84 +630,94 @@
{
XCTAssertTrue(rawLayer->getCircleTranslate().isUndefined(),
@"circle-translate should be unset initially.");
- MGLStyleValue<NSValue *> *defaultStyleValue = layer.circleTranslation;
+ NSExpression *defaultExpression = layer.circleTranslation;
- MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@",
#if TARGET_OS_IPHONE
[NSValue valueWithCGVector:CGVectorMake(1, 1)]
#else
[NSValue valueWithMGLVector:CGVectorMake(1, -1)]
#endif
];
- layer.circleTranslation = constantStyleValue;
+ layer.circleTranslation = constantExpression;
mbgl::style::PropertyValue<std::array<float, 2>> propertyValue = { { 1, 1 } };
XCTAssertEqual(rawLayer->getCircleTranslate(), propertyValue,
- @"Setting circleTranslation to a constant value should update circle-translate.");
- XCTAssertEqualObjects(layer.circleTranslation, constantStyleValue,
- @"circleTranslation should round-trip constant values.");
-
- MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.circleTranslation = functionStyleValue;
-
- mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = { {{18, { 1, 1 }}} };
+ @"Setting circleTranslation to a constant value expression should update circle-translate.");
+ XCTAssertEqualObjects(layer.circleTranslation, constantExpression,
+ @"circleTranslation should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"{1, 1}"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.circleTranslation = functionExpression;
+
+ mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = {{
+ { -INFINITY, { 1, 1 } },
+ { 18, { 1, 1 } },
+ }};
propertyValue = mbgl::style::CameraFunction<std::array<float, 2>> { intervalStops };
XCTAssertEqual(rawLayer->getCircleTranslate(), propertyValue,
- @"Setting circleTranslation to a camera function should update circle-translate.");
- XCTAssertEqualObjects(layer.circleTranslation, functionStyleValue,
- @"circleTranslation should round-trip camera functions.");
+ @"Setting circleTranslation to a camera expression should update circle-translate.");
+ XCTAssertEqualObjects(layer.circleTranslation, functionExpression,
+ @"circleTranslation should round-trip camera expressions.");
layer.circleTranslation = nil;
XCTAssertTrue(rawLayer->getCircleTranslate().isUndefined(),
@"Unsetting circleTranslation should return circle-translate to the default value.");
- XCTAssertEqualObjects(layer.circleTranslation, defaultStyleValue,
+ XCTAssertEqualObjects(layer.circleTranslation, defaultExpression,
@"circleTranslation should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.circleTranslation = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.circleTranslation = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.circleTranslation = functionExpression, NSException, NSInvalidArgumentException, @"MGLCircleLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.circleTranslation = functionExpression, NSException, NSInvalidArgumentException, @"MGLCircleLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// circle-translate-anchor
{
XCTAssertTrue(rawLayer->getCircleTranslateAnchor().isUndefined(),
@"circle-translate-anchor should be unset initially.");
- MGLStyleValue<NSValue *> *defaultStyleValue = layer.circleTranslationAnchor;
+ NSExpression *defaultExpression = layer.circleTranslationAnchor;
- MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLCircleTranslationAnchor:MGLCircleTranslationAnchorViewport]];
- layer.circleTranslationAnchor = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'viewport'"];
+ layer.circleTranslationAnchor = constantExpression;
mbgl::style::PropertyValue<mbgl::style::TranslateAnchorType> propertyValue = { mbgl::style::TranslateAnchorType::Viewport };
XCTAssertEqual(rawLayer->getCircleTranslateAnchor(), propertyValue,
- @"Setting circleTranslationAnchor to a constant value should update circle-translate-anchor.");
- XCTAssertEqualObjects(layer.circleTranslationAnchor, constantStyleValue,
- @"circleTranslationAnchor should round-trip constant values.");
-
- MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.circleTranslationAnchor = functionStyleValue;
-
- mbgl::style::IntervalStops<mbgl::style::TranslateAnchorType> intervalStops = { {{18, mbgl::style::TranslateAnchorType::Viewport}} };
+ @"Setting circleTranslationAnchor to a constant value expression should update circle-translate-anchor.");
+ XCTAssertEqualObjects(layer.circleTranslationAnchor, constantExpression,
+ @"circleTranslationAnchor should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"'viewport'"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.circleTranslationAnchor = functionExpression;
+
+ mbgl::style::IntervalStops<mbgl::style::TranslateAnchorType> intervalStops = {{
+ { -INFINITY, mbgl::style::TranslateAnchorType::Viewport },
+ { 18, mbgl::style::TranslateAnchorType::Viewport },
+ }};
propertyValue = mbgl::style::CameraFunction<mbgl::style::TranslateAnchorType> { intervalStops };
XCTAssertEqual(rawLayer->getCircleTranslateAnchor(), propertyValue,
- @"Setting circleTranslationAnchor to a camera function should update circle-translate-anchor.");
- XCTAssertEqualObjects(layer.circleTranslationAnchor, functionStyleValue,
- @"circleTranslationAnchor should round-trip camera functions.");
+ @"Setting circleTranslationAnchor to a camera expression should update circle-translate-anchor.");
+ XCTAssertEqualObjects(layer.circleTranslationAnchor, functionExpression,
+ @"circleTranslationAnchor should round-trip camera expressions.");
layer.circleTranslationAnchor = nil;
XCTAssertTrue(rawLayer->getCircleTranslateAnchor().isUndefined(),
@"Unsetting circleTranslationAnchor should return circle-translate-anchor to the default value.");
- XCTAssertEqualObjects(layer.circleTranslationAnchor, defaultStyleValue,
+ XCTAssertEqualObjects(layer.circleTranslationAnchor, defaultExpression,
@"circleTranslationAnchor should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.circleTranslationAnchor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.circleTranslationAnchor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.circleTranslationAnchor = functionExpression, NSException, NSInvalidArgumentException, @"MGLCircleLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.circleTranslationAnchor = functionExpression, NSException, NSInvalidArgumentException, @"MGLCircleLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
}
diff --git a/platform/darwin/test/MGLComputedShapeSourceTests.m b/platform/darwin/test/MGLComputedShapeSourceTests.m
new file mode 100644
index 0000000000..6eb45913d6
--- /dev/null
+++ b/platform/darwin/test/MGLComputedShapeSourceTests.m
@@ -0,0 +1,24 @@
+#import <XCTest/XCTest.h>
+
+#import <Mapbox/Mapbox.h>
+
+
+@interface MGLComputedShapeSourceTests : XCTestCase
+@end
+
+@implementation MGLComputedShapeSourceTests
+
+- (void)testInitializer {
+ MGLComputedShapeSource *source = [[MGLComputedShapeSource alloc] initWithIdentifier:@"id" options:@{}];
+ XCTAssertNotNil(source);
+ XCTAssertNotNil(source.requestQueue);
+ XCTAssertNil(source.dataSource);
+}
+
+- (void)testNilOptions {
+ MGLComputedShapeSource *source = [[MGLComputedShapeSource alloc] initWithIdentifier:@"id" options:nil];
+ XCTAssertNotNil(source);
+}
+
+
+@end
diff --git a/platform/darwin/test/MGLCoordinateFormatterTests.m b/platform/darwin/test/MGLCoordinateFormatterTests.m
index ac083fa103..d693f739ec 100644
--- a/platform/darwin/test/MGLCoordinateFormatterTests.m
+++ b/platform/darwin/test/MGLCoordinateFormatterTests.m
@@ -24,7 +24,12 @@
coordinate = CLLocationCoordinate2DMake(38.9131982, -77.0325453144239);
XCTAssertEqualObjects([shortFormatter stringFromCoordinate:coordinate], @"38°54′48″N, 77°1′57″W");
XCTAssertEqualObjects([mediumFormatter stringFromCoordinate:coordinate], @"38°54′48″ north, 77°1′57″ west");
- XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"38 degrees, 54 minutes, and 48 seconds north by 77 degrees, 1 minute, and 57 seconds west");
+ if (@available(iOS 9.0, *)) {
+ XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"38 degrees, 54 minutes, and 48 seconds north by 77 degrees, 1 minute, and 57 seconds west");
+ } else {
+ // Foundation in iOS 8 does not know how to pluralize coordinates.
+ XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"38 degree(s), 54 minute(s), and 48 second(s) north by 77 degree(s), 1 minute(s), and 57 second(s) west");
+ }
shortFormatter.allowsSeconds = NO;
mediumFormatter.allowsSeconds = NO;
@@ -33,7 +38,12 @@
coordinate = CLLocationCoordinate2DMake(38.9131982, -77.0325453144239);
XCTAssertEqualObjects([shortFormatter stringFromCoordinate:coordinate], @"38°55′N, 77°2′W");
XCTAssertEqualObjects([mediumFormatter stringFromCoordinate:coordinate], @"38°55′ north, 77°2′ west");
- XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"38 degrees and 55 minutes north by 77 degrees and 2 minutes west");
+ if (@available(iOS 9.0, *)) {
+ XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"38 degrees and 55 minutes north by 77 degrees and 2 minutes west");
+ } else {
+ // Foundation in iOS 8 does not know how to pluralize coordinates.
+ XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"38 degree(s) and 55 minute(s) north by 77 degree(s) and 2 minute(s) west");
+ }
shortFormatter.allowsMinutes = NO;
mediumFormatter.allowsMinutes = NO;
@@ -42,7 +52,12 @@
coordinate = CLLocationCoordinate2DMake(38.9131982, -77.0325453144239);
XCTAssertEqualObjects([shortFormatter stringFromCoordinate:coordinate], @"39°N, 77°W");
XCTAssertEqualObjects([mediumFormatter stringFromCoordinate:coordinate], @"39° north, 77° west");
- XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"39 degrees north by 77 degrees west");
+ if (@available(iOS 9.0, *)) {
+ XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"39 degrees north by 77 degrees west");
+ } else {
+ // Foundation in iOS 8 does not know how to pluralize coordinates.
+ XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"39 degree(s) north by 77 degree(s) west");
+ }
}
@end
diff --git a/platform/darwin/test/MGLDocumentationExampleTests.swift b/platform/darwin/test/MGLDocumentationExampleTests.swift
index 668e5f57f8..a216d9ad1c 100644
--- a/platform/darwin/test/MGLDocumentationExampleTests.swift
+++ b/platform/darwin/test/MGLDocumentationExampleTests.swift
@@ -88,6 +88,26 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
XCTAssertNotNil(mapView.style?.source(withIdentifier: "clouds"))
}
+
+ func testMGLRasterDEMSource() {
+ // We want to use mapbox.terrain-rgb in the example, but using a mapbox:
+ // URL requires setting an access token. So this identically named
+ // subclass of MGLRasterDEMSource swaps in a nonexistent URL.
+ class MGLRasterDEMSource: Mapbox.MGLRasterDEMSource {
+ override init(identifier: String, configurationURL: URL, tileSize: CGFloat = 256) {
+ let bogusURL = URL(string: "https://example.com/raster-rgb.json")!
+ super.init(identifier: identifier, configurationURL: bogusURL, tileSize: tileSize)
+ }
+ }
+
+ //#-example-code
+ let terrainRGBURL = URL(string: "mapbox://mapbox.terrain-rgb")!
+ let source = MGLRasterDEMSource(identifier: "hills", configurationURL: terrainRGBURL)
+ mapView.style?.addSource(source)
+ //#-end-example-code
+
+ XCTAssertNotNil(mapView.style?.source(withIdentifier: "hills"))
+ }
func testMGLVectorSource() {
//#-example-code
@@ -137,12 +157,15 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
//#-example-code
let layer = MGLCircleStyleLayer(identifier: "circles", source: population)
layer.sourceLayerIdentifier = "population"
- layer.circleColor = MGLStyleValue(rawValue: .green)
- layer.circleRadius = MGLStyleValue(interpolationMode: .exponential,
- cameraStops: [12: MGLStyleValue(rawValue: 2),
- 22: MGLStyleValue(rawValue: 180)],
- options: [.interpolationBase: 1.75])
- layer.circleOpacity = MGLStyleValue(rawValue: 0.7)
+ #if os(macOS)
+ layer.circleColor = NSExpression(forConstantValue: NSColor.green)
+ #else
+ layer.circleColor = NSExpression(forConstantValue: UIColor.green)
+ #endif
+ layer.circleRadius = NSExpression(format: "FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'exponential', 1.75, %@)",
+ [12: 2,
+ 22: 180])
+ layer.circleOpacity = NSExpression(forConstantValue: 0.7)
layer.predicate = NSPredicate(format: "%K == %@", "marital-status", "married")
mapView.style?.addLayer(layer)
//#-end-example-code
@@ -157,12 +180,15 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
//#-example-code
let layer = MGLLineStyleLayer(identifier: "trails-path", source: trails)
layer.sourceLayerIdentifier = "trails"
- layer.lineWidth = MGLStyleValue(interpolationMode: .exponential,
- cameraStops: [14: MGLStyleValue(rawValue: 2),
- 18: MGLStyleValue(rawValue: 20)],
- options: [.interpolationBase: 1.5])
- layer.lineColor = MGLStyleValue(rawValue: .brown)
- layer.lineCap = MGLStyleValue(rawValue: NSValue(mglLineCap: .round))
+ layer.lineWidth = NSExpression(format: "FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'exponential', 1.5, %@)",
+ [14: 2,
+ 18: 20])
+ #if os(macOS)
+ layer.lineColor = NSExpression(forConstantValue: NSColor.brown)
+ #else
+ layer.lineColor = NSExpression(forConstantValue: UIColor.brown)
+ #endif
+ layer.lineCap = NSExpression(forConstantValue: "round")
layer.predicate = NSPredicate(format: "%K == %@", "trail-type", "mountain-biking")
mapView.style?.addLayer(layer)
//#-end-example-code
@@ -177,7 +203,11 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
//#-example-code
let layer = MGLFillStyleLayer(identifier: "parks", source: parks)
layer.sourceLayerIdentifier = "parks"
- layer.fillColor = MGLStyleValue(rawValue: .green)
+ #if os(macOS)
+ layer.fillColor = NSExpression(forConstantValue: NSColor.green)
+ #else
+ layer.fillColor = NSExpression(forConstantValue: UIColor.green)
+ #endif
layer.predicate = NSPredicate(format: "type == %@", "national-park")
mapView.style?.addLayer(layer)
//#-end-example-code
@@ -192,14 +222,32 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
//#-example-code
let layer = MGLFillExtrusionStyleLayer(identifier: "buildings", source: buildings)
layer.sourceLayerIdentifier = "building"
- layer.fillExtrusionHeight = MGLStyleValue(interpolationMode: .identity, sourceStops: nil, attributeName: "height", options: nil)
- layer.fillExtrusionBase = MGLStyleValue(interpolationMode: .identity, sourceStops: nil, attributeName: "min_height", options: nil)
+ layer.fillExtrusionHeight = NSExpression(forKeyPath: "height")
+ layer.fillExtrusionBase = NSExpression(forKeyPath: "min_height")
layer.predicate = NSPredicate(format: "extrude == 'true'")
mapView.style?.addLayer(layer)
//#-end-example-code
XCTAssertNotNil(mapView.style?.layer(withIdentifier: "buildings"))
}
+
+ func testMGLHeatmapStyleLayer() {
+ let earthquakes = MGLShapeSource(identifier: "earthquakes", url: URL(string: "https://example.com/earthquakes.json")!, options: [:])
+ mapView.style?.addSource(earthquakes)
+
+ //#-example-code
+ let layer = MGLHeatmapStyleLayer(identifier: "earthquake-heat", source: earthquakes)
+ layer.heatmapWeight = NSExpression(format: "FUNCTION(magnitude, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)",
+ [0: 0,
+ 6: 1])
+ layer.heatmapIntensity = NSExpression(format: "FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)",
+ [0: 1,
+ 9: 3])
+ mapView.style?.addLayer(layer)
+ //#-end-example-code
+
+ XCTAssertNotNil(mapView.style?.layer(withIdentifier: "earthquake-heat"))
+ }
func testMGLSymbolStyleLayer() {
let pois = MGLVectorSource(identifier: "pois", configurationURL: URL(string: "https://example.com/style.json")!)
@@ -208,17 +256,17 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
//#-example-code
let layer = MGLSymbolStyleLayer(identifier: "coffeeshops", source: pois)
layer.sourceLayerIdentifier = "pois"
- layer.iconImageName = MGLStyleValue(rawValue: "coffee")
- layer.iconScale = MGLStyleValue(rawValue: 0.5)
- layer.text = MGLStyleValue(rawValue: "{name}")
+ layer.iconImageName = NSExpression(forConstantValue: "coffee")
+ layer.iconScale = NSExpression(forConstantValue: 0.5)
+ layer.text = NSExpression(forKeyPath: "name")
#if os(macOS)
var vector = CGVector(dx: 10, dy: 0)
- layer.textTranslation = MGLStyleValue(rawValue: NSValue(bytes: &vector, objCType: "{CGVector=dd}"))
+ layer.textTranslation = NSExpression(forConstantValue: NSValue(bytes: &vector, objCType: "{CGVector=dd}"))
#else
- layer.textTranslation = MGLStyleValue(rawValue: NSValue(cgVector: CGVector(dx: 10, dy: 0)))
+ layer.textTranslation = NSExpression(forConstantValue: NSValue(cgVector: CGVector(dx: 10, dy: 0)))
#endif
- layer.textJustification = MGLStyleValue(rawValue: NSValue(mglTextJustification: .left))
- layer.textAnchor = MGLStyleValue(rawValue: NSValue(mglTextAnchor: .left))
+ layer.textJustification = NSExpression(forConstantValue: "left")
+ layer.textAnchor = NSExpression(forConstantValue: "left")
layer.predicate = NSPredicate(format: "%K == %@", "venue-type", "coffee")
mapView.style?.addLayer(layer)
//#-end-example-code
@@ -239,13 +287,40 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
//#-example-code
let layer = MGLRasterStyleLayer(identifier: "clouds", source: source)
- layer.rasterOpacity = MGLStyleValue(rawValue: 0.5)
+ layer.rasterOpacity = NSExpression(forConstantValue: 0.5)
mapView.style?.addLayer(layer)
//#-end-example-code
XCTAssertNotNil(mapView.style?.layer(withIdentifier: "clouds"))
}
+ func testMGLHillshadeStyleLayer() {
+ let source = MGLRasterDEMSource(identifier: "dem", tileURLTemplates: ["https://example.com/raster-rgb/{z}/{x}/{y}.png"], options: [
+ .minimumZoomLevel: 9,
+ .maximumZoomLevel: 16,
+ .tileSize: 256,
+ .attributionInfos: [
+ MGLAttributionInfo(title: NSAttributedString(string: "© Mapbox"), url: URL(string: "http://mapbox.com"))
+ ]
+ ])
+ mapView.style?.addSource(source)
+
+ let canals = MGLVectorSource(identifier: "canals", configurationURL: URL(string: "https://example.com/style.json")!)
+ mapView.style?.addSource(canals)
+ let canalShadowLayer = MGLLineStyleLayer(identifier: "waterway-river-canal-shadow", source: canals)
+ mapView.style?.addLayer(canalShadowLayer)
+
+ //#-example-code
+ let layer = MGLHillshadeStyleLayer(identifier: "hills", source: source)
+ layer.hillshadeExaggeration = NSExpression(forConstantValue: 0.6)
+ if let canalShadowLayer = mapView.style?.layer(withIdentifier: "waterway-river-canal-shadow") {
+ mapView.style?.insertLayer(layer, below: canalShadowLayer)
+ }
+ //#-end-example-code
+
+ XCTAssertNotNil(mapView.style?.layer(withIdentifier: "hills"))
+ }
+
func testMGLVectorStyleLayer$predicate() {
let terrain = MGLVectorSource(identifier: "terrain", configurationURL: URL(string: "https://example.com/style.json")!)
mapView.style?.addSource(terrain)
@@ -316,7 +391,7 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
}
//#-end-example-code
- wait(for: [expectation], timeout: 1)
+ wait(for: [expectation], timeout: 5)
}
// For testMGLMapView().
diff --git a/platform/darwin/test/MGLDocumentationGuideTests.swift b/platform/darwin/test/MGLDocumentationGuideTests.swift
index c71f1b46c7..f939695f32 100644
--- a/platform/darwin/test/MGLDocumentationGuideTests.swift
+++ b/platform/darwin/test/MGLDocumentationGuideTests.swift
@@ -1,3 +1,4 @@
+import XCTest
import Foundation
import Mapbox
#if os(iOS)
@@ -52,25 +53,26 @@ class MGLDocumentationGuideTests: XCTestCase, MGLMapViewDelegate {
func testUsingStyleFunctionsAtRuntime$Stops() {
//#-example-code
#if os(macOS)
- let stops = [
- 0: MGLStyleValue<NSColor>(rawValue: .yellow),
- 2.5: MGLStyleValue(rawValue: .orange),
- 5: MGLStyleValue(rawValue: .red),
- 7.5: MGLStyleValue(rawValue: .blue),
- 10: MGLStyleValue(rawValue: .white),
+ let stops: [Float: NSColor] = [
+ 0: .yellow,
+ 2.5: .orange,
+ 5: .red,
+ 7.5: .blue,
+ 10: .white,
]
#else
- let stops = [
- 0: MGLStyleValue<UIColor>(rawValue: .yellow),
- 2.5: MGLStyleValue(rawValue: .orange),
- 5: MGLStyleValue(rawValue: .red),
- 7.5: MGLStyleValue(rawValue: .blue),
- 10: MGLStyleValue(rawValue: .white),
+ let stops: [Float: UIColor] = [
+ 0: .yellow,
+ 2.5: .orange,
+ 5: .red,
+ 7.5: .blue,
+ 10: .white,
]
#endif
//#-end-example-code
- let _ = MGLStyleValue(interpolationMode: .exponential, cameraStops: stops, options: nil)
+ let _ = NSExpression(format: "FUNCTION(mag, 'mgl_stepWithMinimum:stops:', %@, %@)",
+ stops[0]!, stops)
}
func testUsingStyleFunctionsAtRuntime$Linear() {
@@ -83,36 +85,32 @@ class MGLDocumentationGuideTests: XCTestCase, MGLMapViewDelegate {
mapView.style?.addSource(source)
#if os(macOS)
- let stops = [
- 0: MGLStyleValue<NSColor>(rawValue: .yellow),
- 2.5: MGLStyleValue(rawValue: .orange),
- 5: MGLStyleValue(rawValue: .red),
- 7.5: MGLStyleValue(rawValue: .blue),
- 10: MGLStyleValue(rawValue: .white),
+ let stops: [Float: NSColor] = [
+ 0: .yellow,
+ 2.5: .orange,
+ 5: .red,
+ 7.5: .blue,
+ 10: .white,
]
#else
- let stops = [
- 0: MGLStyleValue<UIColor>(rawValue: .yellow),
- 2.5: MGLStyleValue(rawValue: .orange),
- 5: MGLStyleValue(rawValue: .red),
- 7.5: MGLStyleValue(rawValue: .blue),
- 10: MGLStyleValue(rawValue: .white),
+ let stops: [Float: UIColor] = [
+ 0: .yellow,
+ 2.5: .orange,
+ 5: .red,
+ 7.5: .blue,
+ 10: .white,
]
#endif
let layer = MGLCircleStyleLayer(identifier: "circles", source: source)
#if os(macOS)
- layer.circleColor = MGLStyleValue(interpolationMode: .exponential,
- sourceStops: stops,
- attributeName: "mag",
- options: [.defaultValue: MGLStyleValue<NSColor>(rawValue: .green)])
+ layer.circleColor = NSExpression(format: "FUNCTION(mag, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)",
+ stops)
#else
- layer.circleColor = MGLStyleValue(interpolationMode: .exponential,
- sourceStops: stops,
- attributeName: "mag",
- options: [.defaultValue: MGLStyleValue<UIColor>(rawValue: .green)])
+ layer.circleColor = NSExpression(format: "FUNCTION(mag, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)",
+ stops)
#endif
- layer.circleRadius = MGLStyleValue(rawValue: 10)
+ layer.circleRadius = NSExpression(forConstantValue: 10)
mapView.style?.insertLayer(layer, below: symbolLayer)
//#-end-example-code
}
@@ -123,14 +121,13 @@ class MGLDocumentationGuideTests: XCTestCase, MGLMapViewDelegate {
//#-example-code
let stops = [
- 12: MGLStyleValue<NSNumber>(rawValue: 0.5),
- 14: MGLStyleValue(rawValue: 2),
- 18: MGLStyleValue(rawValue: 18),
+ 12: 0.5,
+ 14: 2,
+ 18: 18,
]
- layer.circleRadius = MGLStyleValue(interpolationMode: .exponential,
- cameraStops: stops,
- options: [.interpolationBase: 1.5])
+ layer.circleRadius = NSExpression(format: "FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'exponential', 1.5, %@)",
+ stops)
//#-end-example-code
}
@@ -140,31 +137,27 @@ class MGLDocumentationGuideTests: XCTestCase, MGLMapViewDelegate {
//#-example-code
#if os(macOS)
- let stops = [
- 0: MGLStyleValue<NSColor>(rawValue: .yellow),
- 2.5: MGLStyleValue(rawValue: .orange),
- 5: MGLStyleValue(rawValue: .red),
- 7.5: MGLStyleValue(rawValue: .blue),
- 10: MGLStyleValue(rawValue: .white),
+ let stops: [Float: NSColor] = [
+ 0: .yellow,
+ 2.5: .orange,
+ 5: .red,
+ 7.5: .blue,
+ 10: .white,
]
- layer.circleColor = MGLStyleValue(interpolationMode: .interval,
- sourceStops: stops,
- attributeName: "mag",
- options: [.defaultValue: MGLStyleValue<NSColor>(rawValue: .green)])
+ layer.circleColor = NSExpression(format: "FUNCTION(mag, 'mgl_stepWithMinimum:stops:', %@, %@)",
+ NSColor.green, stops)
#else
- let stops = [
- 0: MGLStyleValue<UIColor>(rawValue: .yellow),
- 2.5: MGLStyleValue(rawValue: .orange),
- 5: MGLStyleValue(rawValue: .red),
- 7.5: MGLStyleValue(rawValue: .blue),
- 10: MGLStyleValue(rawValue: .white),
+ let stops: [Float: UIColor] = [
+ 0: .yellow,
+ 2.5: .orange,
+ 5: .red,
+ 7.5: .blue,
+ 10: .white,
]
- layer.circleColor = MGLStyleValue(interpolationMode: .interval,
- sourceStops: stops,
- attributeName: "mag",
- options: [.defaultValue: MGLStyleValue<UIColor>(rawValue: .green)])
+ layer.circleColor = NSExpression(format: "FUNCTION(mag, 'mgl_stepWithMinimum:stops:', %@, %@)",
+ UIColor.green, stops)
#endif
//#-end-example-code
}
@@ -175,28 +168,24 @@ class MGLDocumentationGuideTests: XCTestCase, MGLMapViewDelegate {
//#-example-code
#if os(macOS)
- let categoricalStops = [
- "earthquake": MGLStyleValue<NSColor>(rawValue: .orange),
- "explosion": MGLStyleValue(rawValue: .red),
- "quarry blast": MGLStyleValue(rawValue: .yellow),
+ let colors: [String: NSColor] = [
+ "earthquake": .orange,
+ "explosion": .red,
+ "quarry blast": .yellow,
]
-
- layer.circleColor = MGLStyleValue(interpolationMode: .categorical,
- sourceStops: categoricalStops,
- attributeName: "type",
- options: [.defaultValue: MGLStyleValue<NSColor>(rawValue: .blue)])
+ let defaultColor = NSColor.blue
#else
- let categoricalStops = [
- "earthquake": MGLStyleValue<UIColor>(rawValue: .orange),
- "explosion": MGLStyleValue(rawValue: .red),
- "quarry blast": MGLStyleValue(rawValue: .yellow),
+ let colors: [String: UIColor] = [
+ "earthquake": .orange,
+ "explosion": .red,
+ "quarry blast": .yellow,
]
-
- layer.circleColor = MGLStyleValue(interpolationMode: .categorical,
- sourceStops: categoricalStops,
- attributeName: "type",
- options: [.defaultValue: MGLStyleValue<UIColor>(rawValue: .blue)])
+ let defaultColor = UIColor.blue
#endif
+
+ layer.circleColor = NSExpression(
+ format: "TERNARY(FUNCTION(%@, 'valueForKeyPath:', type) != nil, FUNCTION(%@, 'valueForKeyPath:', type), %@)",
+ colors, colors, defaultColor)
//#-end-example-code
}
@@ -205,10 +194,7 @@ class MGLDocumentationGuideTests: XCTestCase, MGLMapViewDelegate {
let layer = MGLCircleStyleLayer(identifier: "circles", source: source)
//#-example-code
- layer.circleRadius = MGLStyleValue(interpolationMode: .identity,
- sourceStops: nil,
- attributeName: "mag",
- options: [.defaultValue: MGLStyleValue<NSNumber>(rawValue: 0)])
+ layer.circleRadius = NSExpression(forKeyPath: "mag")
//#-end-example-code
}
}
diff --git a/platform/darwin/test/MGLExpressionTests.mm b/platform/darwin/test/MGLExpressionTests.mm
index ad0833a068..a5ed2f7bf5 100644
--- a/platform/darwin/test/MGLExpressionTests.mm
+++ b/platform/darwin/test/MGLExpressionTests.mm
@@ -1,9 +1,17 @@
#import <XCTest/XCTest.h>
+#import "MGLStyleLayerTests.h"
+
#import <string>
#import "MGLTypes.h"
-#import "NSExpression+MGLAdditions.h"
+#import "NSExpression+MGLPrivateAdditions.h"
+#import "NSValue+MGLAdditions.h"
+#if TARGET_OS_IPHONE
+#import "UIColor+MGLAdditions.h"
+#else
+#import "NSColor+MGLAdditions.h"
+#endif
#define MGLAssertEqualValues(actual, expected, ...) \
XCTAssertTrue(actual.is<__typeof__(expected)>()); \
@@ -17,11 +25,14 @@
XCTAssertEqualWithAccuracy(actual.get<__typeof__(expected)>(), expected, accuracy, __VA_ARGS__); \
}
+#define MGLConstantExpression(constant) \
+ [NSExpression expressionForConstantValue:constant]
+
#define MGLAssertConstantEqualsValue(constant, value, ...) \
- MGLAssertEqualValues([NSExpression expressionForConstantValue:constant].mgl_constantMBGLValue, value, __VA_ARGS__);
+ MGLAssertEqualValues(MGLConstantExpression(constant).mgl_constantMBGLValue, value, __VA_ARGS__);
#define MGLAssertConstantEqualsValueWithAccuracy(constant, value, accuracy, ...) \
- MGLAssertEqualValuesWithAccuracy([NSExpression expressionForConstantValue:constant].mgl_constantMBGLValue, value, accuracy, __VA_ARGS__);
+ MGLAssertEqualValuesWithAccuracy(MGLConstantExpression(constant).mgl_constantMBGLValue, value, accuracy, __VA_ARGS__);
using namespace std::string_literals;
@@ -140,4 +151,450 @@ using namespace std::string_literals;
XCTAssertEqual([NSExpression expressionForConstantValue:nil].mgl_featureType, mbgl::FeatureType::Unknown);
}
+#pragma mark - JSON expression object tests
+
+- (void)testVariableExpressionObject {
+ {
+ NSExpression *expression = [NSExpression expressionForVariable:@"zoomLevel"];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, @[@"zoom"]);
+ XCTAssertEqualObjects([NSExpression expressionWithFormat:@"$zoomLevel"].mgl_jsonExpressionObject, @[@"zoom"]);
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:@[@"zoom"]], expression);
+ NSMutableDictionary *context = [@{@"zoomLevel": @16} mutableCopy];
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:context], @16);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForVariable:@"heatmapDensity"];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, @[@"heatmap-density"]);
+ XCTAssertEqualObjects([NSExpression expressionWithFormat:@"$heatmapDensity"].mgl_jsonExpressionObject, @[@"heatmap-density"]);
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:@[@"heatmap-density"]], expression);
+ NSMutableDictionary *context = [@{@"heatmapDensity": @1} mutableCopy];
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:context], @1);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForVariable:@"loremIpsum"];
+ NSArray *jsonExpression = @[@"var", @"loremIpsum"];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithFormat:@"$loremIpsum"].mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ NSMutableDictionary *context = [@{@"loremIpsum": @"Lorem ipsum dolor sit amet"} mutableCopy];
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:context], @"Lorem ipsum dolor sit amet");
+ }
+ {
+ NSDictionary *context = @{@"loremIpsum": MGLConstantExpression(@"Lorem ipsum dolor sit amet")};
+ NSExpression *expression = [NSExpression expressionWithFormat:@"FUNCTION(uppercase($loremIpsum), 'mgl_expressionWithContext:', %@)", context];
+ NSArray *jsonExpression = @[@"let", @"loremIpsum", @"Lorem ipsum dolor sit amet", @[@"upcase", @[@"var", @"loremIpsum"]]];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ }
+}
+
+- (void)testConstantValueExpressionObject {
+ {
+ NSExpression *expression = [NSExpression expressionForConstantValue:nil];
+ XCTAssert(expression.mgl_jsonExpressionObject == [NSNull null]);
+ XCTAssert([NSExpression expressionWithFormat:@"nil"].mgl_jsonExpressionObject == [NSNull null]);
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:[NSNull null]], expression);
+ XCTAssertNil([expression expressionValueWithObject:nil context:nil]);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForConstantValue:@1];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, @1);
+ XCTAssertEqualObjects([NSExpression expressionWithFormat:@"1"].mgl_jsonExpressionObject, @1);
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:@1], expression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @1);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForConstantValue:@YES];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, @YES);
+ XCTAssertEqualObjects([NSExpression expressionWithFormat:@"TRUE"].mgl_jsonExpressionObject, @YES);
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:@YES], expression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @YES);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForConstantValue:nil];
+ XCTAssert(expression.mgl_jsonExpressionObject == [NSNull null]);
+ XCTAssert([NSExpression expressionWithFormat:@"nil"].mgl_jsonExpressionObject == [NSNull null]);
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:[NSNull null]], expression);
+ XCTAssertNil([expression expressionValueWithObject:nil context:nil]);
+ }
+ {
+ CGVector vector = CGVectorMake(1, 2);
+ NSExpression *expression = [NSExpression expressionForConstantValue:@(vector)];
+#if !TARGET_OS_IPHONE
+ NSArray *jsonExpression = @[@"literal", @[@1, @-2]];
+#else
+ NSArray *jsonExpression = @[@"literal", @[@1, @2]];
+#endif
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ // No way to distinguish offsets from ordinary arrays in expressions.
+ XCTAssertEqualObjects([[NSExpression mgl_expressionWithJSONObject:jsonExpression].collection valueForKeyPath:@"constantValue"], jsonExpression.lastObject);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @(vector));
+ }
+ {
+#if !TARGET_OS_IPHONE
+ NSEdgeInsets padding = {1, 2, 3, 4};
+ NSValue *value = [NSValue valueWithEdgeInsets:padding];
+#else
+ UIEdgeInsets padding = {1, 2, 3, 4};
+ NSValue *value = [NSValue valueWithUIEdgeInsets:padding];
+#endif
+ NSExpression *expression = [NSExpression expressionForConstantValue:value];
+ NSArray *jsonExpression = @[@"literal", @[@1, @4, @3, @2]];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ // No way to distinguish offsets from ordinary arrays in expressions.
+ XCTAssertEqualObjects([[NSExpression mgl_expressionWithJSONObject:jsonExpression].collection valueForKeyPath:@"constantValue"], jsonExpression.lastObject);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], value);
+ }
+ {
+ MGLColor *color = [MGLColor mgl_colorWithColor:{ 255.0/255, 239.0/255, 213.0/255, 1 }]; // papayawhip
+ NSExpression *expression = [NSExpression expressionForConstantValue:color];
+ NSArray *jsonExpression = @[@"rgb", @255, @239, @213];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], color);
+ }
+ {
+ MGLColor *color = [MGLColor mgl_colorWithColor:{ 255.0/255, 239.0/255, 213.0/255, 0.5 }]; // papayawhip
+ NSExpression *expression = [NSExpression expressionForConstantValue:color];
+ NSArray *jsonExpression = @[@"rgba", @255, @239, @213, @0.5];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], color);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionWithFormat:@"noindex(513)"];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, @513);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @513);
+ }
+}
+
+- (void)testKeyPathExpressionObject {
+ {
+ NSExpression *expression = [NSExpression expressionForKeyPath:@"highway"];
+ NSArray *jsonExpression = @[@"get", @"highway"];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithFormat:@"highway"].mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionWithFormat:@"%@.population", @{@"population": MGLConstantExpression(@12000)}];
+ NSArray *jsonExpression = @[@"get", @"population", @[@"literal", @{@"population": @12000}]];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionWithFormat:@"%@.uppercase('population')", @{@"POPULATION": MGLConstantExpression(@12000)}];
+ NSArray *jsonExpression = @[@"get", @[@"upcase", @"population"], @[@"literal", @{@"POPULATION": @12000}]];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ }
+}
+
+- (void)testStatisticalExpressionObject {
+ {
+ NSExpression *expression = [NSExpression expressionWithFormat:@"average({1, 2, 2, 3, 4, 7, 9})"];
+ NSArray *jsonExpression = @[@"/", @[@"+", @1, @2, @2, @3, @4, @7, @9], @[@"length", @[@"literal", @[@1, @2, @2, @3, @4, @7, @9]]]];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @4);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionWithFormat:@"sum({1, 2, 2, 3, 4, 7, 9})"];
+ NSArray *jsonExpression = @[@"+", @1, @2, @2, @3, @4, @7, @9];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @28);
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionWithFormat:@"count({1, 2, 2, 3, 4, 7, 9})"];
+ NSArray *jsonExpression = @[@"length", @[@"literal", @[@1, @2, @2, @3, @4, @7, @9]]];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @7);
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionWithFormat:@"min({1, 2, 2, 3, 4, 7, 9})"];
+ NSArray *jsonExpression = @[@"min", @1, @2, @2, @3, @4, @7, @9];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @1);
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionWithFormat:@"max({1, 2, 2, 3, 4, 7, 9})"];
+ NSArray *jsonExpression = @[@"max", @1, @2, @2, @3, @4, @7, @9];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @9);
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ }
+}
+
+- (void)testArithmeticExpressionObject {
+ NSArray *arguments = @[MGLConstantExpression(@1), MGLConstantExpression(@1)];
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"add:to:" arguments:arguments];
+ NSArray *jsonExpression = @[@"+", @1, @1];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithFormat:@"1 + 1"].mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"from:subtract:" arguments:arguments];
+ NSArray *jsonExpression = @[@"-", @1, @1];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithFormat:@"1 - 1"].mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"multiply:by:" arguments:arguments];
+ NSArray *jsonExpression = @[@"*", @1, @1];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithFormat:@"1 * 1"].mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"divide:by:" arguments:arguments];
+ NSArray *jsonExpression = @[@"/", @1, @1];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithFormat:@"1 / 1"].mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"modulus:by:" arguments:arguments];
+ NSArray *jsonExpression = @[@"%", @1, @1];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ // NSExpression lacks a shorthand operator for modulus.
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"ceiling:" arguments:@[MGLConstantExpression(@1.5)]];
+ NSArray *jsonTruncation = @[@"-", @1.5, @[@"%", @1.5, @1]];
+ NSArray *jsonExpression = @[@"+", jsonTruncation, @[@"case", @[@">", @[@"%", @1.5, @1], @0], @1, @0]];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @2);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"ceiling:" arguments:@[MGLConstantExpression(@-1.5)]];
+ NSArray *jsonTruncation = @[@"-", @-1.5, @[@"%", @-1.5, @1]];
+ NSArray *jsonExpression = @[@"+", jsonTruncation, @[@"case", @[@">", @[@"%", @-1.5, @1], @0], @1, @0]];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @-1);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"ceiling:" arguments:@[MGLConstantExpression(@2)]];
+ NSArray *jsonTruncation = @[@"-", @2, @[@"%", @2, @1]];
+ NSArray *jsonExpression = @[@"+", jsonTruncation, @[@"case", @[@">", @[@"%", @2, @1], @0], @1, @0]];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @2);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"ceiling:" arguments:@[MGLConstantExpression(@-2)]];
+ NSArray *jsonTruncation = @[@"-", @-2, @[@"%", @-2, @1]];
+ NSArray *jsonExpression = @[@"+", jsonTruncation, @[@"case", @[@">", @[@"%", @-2, @1], @0], @1, @0]];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @-2);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"trunc:" arguments:@[MGLConstantExpression(@1.5)]];
+ NSArray *jsonExpression = @[@"-", @1.5, @[@"%", @1.5, @1]];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @1);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"trunc:" arguments:@[MGLConstantExpression(@-1.5)]];
+ NSArray *jsonExpression = @[@"-", @-1.5, @[@"%", @-1.5, @1]];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @-1);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"abs:" arguments:@[MGLConstantExpression(@2)]];
+ NSArray *jsonExpression = @[@"*", @2, @[@"case", @[@">", @2, @0], @1, @-1]];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @2);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"abs:" arguments:@[MGLConstantExpression(@-2)]];
+ NSArray *jsonExpression = @[@"*", @-2, @[@"case", @[@">", @-2, @0], @1, @-1]];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @2);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"floor:" arguments:@[MGLConstantExpression(@1.5)]];
+ NSArray *jsonTruncation = @[@"-", @1.5, @[@"%", @1.5, @1]];
+ NSArray *jsonExpression = @[@"-", jsonTruncation, @[@"case", @[@"<", @[@"%", @1.5, @1], @0], @1, @0]];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @1);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"floor:" arguments:@[MGLConstantExpression(@-1.5)]];
+ NSArray *jsonTruncation = @[@"-", @-1.5, @[@"%", @-1.5, @1]];
+ NSArray *jsonExpression = @[@"-", jsonTruncation, @[@"case", @[@"<", @[@"%", @-1.5, @1], @0], @1, @0]];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @-2);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"floor:" arguments:@[MGLConstantExpression(@2)]];
+ NSArray *jsonTruncation = @[@"-", @2, @[@"%", @2, @1]];
+ NSArray *jsonExpression = @[@"-", jsonTruncation, @[@"case", @[@"<", @[@"%", @2, @1], @0], @1, @0]];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @2);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"floor:" arguments:@[MGLConstantExpression(@-2)]];
+ NSArray *jsonTruncation = @[@"-", @-2, @[@"%", @-2, @1]];
+ NSArray *jsonExpression = @[@"-", jsonTruncation, @[@"case", @[@"<", @[@"%", @-2, @1], @0], @1, @0]];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @-2);
+ }
+}
+
+- (void)testTrigonometricExpressionObject {
+ NSArray *arguments = @[MGLConstantExpression(@1), MGLConstantExpression(@1)];
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"sqrt:" arguments:arguments];
+ NSArray *jsonExpression = @[@"sqrt", @1, @1];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"ln:" arguments:arguments];
+ NSArray *jsonExpression = @[@"ln", @1, @1];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"raise:toPower:" arguments:arguments];
+ NSArray *jsonExpression = @[@"^", @1, @1];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithFormat:@"1 ** 1"].mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"exp:" arguments:@[MGLConstantExpression(@0)]];
+ NSArray *jsonExpression = @[@"^", @[@"e"], @0];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @1);
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForConstantValue:@(M_E)];
+ NSArray *jsonExpression = @[@"e"];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @(M_E));
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForConstantValue:@(M_PI)];
+ NSArray *jsonExpression = @[@"pi"];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @(M_PI));
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ }
+}
+
+- (void)testStringFormattingExpressionObject {
+ NSArray *arguments = @[MGLConstantExpression(@"MacDonald")];
+ {
+ NSExpression *expression = [NSExpression expressionWithFormat:@"FUNCTION('Old', 'stringByAppendingString:', 'MacDonald')"];
+ NSArray *jsonExpression = @[@"concat", @"Old", @"MacDonald"];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @"OldMacDonald");
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"uppercase:" arguments:arguments];
+ NSArray *jsonExpression = @[@"upcase", @"MacDonald"];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"lowercase:" arguments:arguments];
+ NSArray *jsonExpression = @[@"downcase", @"MacDonald"];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ }
+}
+
+- (void)testTypeConversionExpressionObject {
+ {
+ NSExpression *expression = [NSExpression expressionWithFormat:@"FUNCTION(number, 'boolValue')"];
+ NSArray *jsonExpression = @[@"to-boolean", @[@"get", @"number"]];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ // NSExpression is unable to evaluate -[NSNumber boolValue] by itself
+ // because it returns a primitive instead of an object.
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionWithFormat:@"FUNCTION(postalCode, 'mgl_numberWithFallbackValues:', zipCode)"];
+ NSArray *jsonExpression = @[@"to-number", @[@"get", @"postalCode"], @[@"get", @"zipCode"]];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithFormat:@"FUNCTION(postalCode, 'doubleValue', zipCode)"].mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithFormat:@"FUNCTION(postalCode, 'floatValue', zipCode)"].mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithFormat:@"FUNCTION(postalCode, 'decimalValue', zipCode)"].mgl_jsonExpressionObject, jsonExpression);
+ // NSExpression is unable to evaluate NSNumber’s -floatValue,
+ // -doubleValue, or -decimalValue by themselves because they each return
+ // a primitive instead of an object.
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionWithFormat:@"FUNCTION(number, 'stringValue')"];
+ NSArray *jsonExpression = @[@"to-string", @[@"get", @"number"]];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:@{@"number": @1.5} context:nil], @"1.5");
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ }
+}
+
+- (void)testInterpolationExpressionObject {
+ {
+ NSDictionary *stops = @{@0: MGLConstantExpression(@100), @10: MGLConstantExpression(@200)};
+ NSExpression *expression = [NSExpression expressionWithFormat:@"FUNCTION(x, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", stops];
+ NSArray *jsonExpression = @[@"interpolate", @[@"linear"], @[@"get", @"x"], @0, @100, @10, @200];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ }
+ {
+ NSDictionary *stops = @{@1: MGLConstantExpression(@2), @3: MGLConstantExpression(@6)};
+ NSExpression *expression = [NSExpression expressionWithFormat:@"FUNCTION(x, 'mgl_interpolateWithCurveType:parameters:stops:', 'exponential', 2, %@)", stops];
+ NSArray *jsonExpression = @[@"interpolate", @[@"exponential", @2], @[@"get", @"x"], @1, @2, @3, @6];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ }
+ {
+ NSDictionary *stops = @{@0: MGLConstantExpression(@0), @100: MGLConstantExpression(@100)};
+ NSExpression *expression = [NSExpression expressionWithFormat:@"FUNCTION(x, 'mgl_interpolateWithCurveType:parameters:stops:', 'cubic-bezier', { 0.42, 0, 0.58, 1 }, %@)", stops];
+ NSArray *jsonExpression = @[@"interpolate", @[@"cubic-bezier", @0.42, @0, @0.58, @1], @[@"get", @"x"], @0, @0, @100, @100];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ }
+ {
+ NSDictionary *stops = @{@0: MGLConstantExpression(@111), @1: MGLConstantExpression(@1111)};
+ NSExpression *expression = [NSExpression expressionWithFormat:@"FUNCTION(x, 'mgl_stepWithMinimum:stops:', 11, %@)", stops];
+ NSArray *jsonExpression = @[@"step", @[@"get", @"x"], @11, @0, @111, @1, @1111];
+ 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);
+ }
+ }
+}
+
@end
diff --git a/platform/darwin/test/MGLFeatureTests.mm b/platform/darwin/test/MGLFeatureTests.mm
index 818ad8200e..14b64be854 100644
--- a/platform/darwin/test/MGLFeatureTests.mm
+++ b/platform/darwin/test/MGLFeatureTests.mm
@@ -298,29 +298,36 @@
}
- (void)testShapeCollectionFeatureGeoJSONDictionary {
- MGLPointAnnotation *pointFeature = [[MGLPointAnnotation alloc] init];
+ MGLPointFeature *pointFeature = [[MGLPointFeature alloc] init];
CLLocationCoordinate2D pointCoordinate = { 10, 10 };
pointFeature.coordinate = pointCoordinate;
CLLocationCoordinate2D coord1 = { 0, 0 };
CLLocationCoordinate2D coord2 = { 10, 10 };
CLLocationCoordinate2D coords[] = { coord1, coord2 };
- MGLPolyline *polyline = [MGLPolyline polylineWithCoordinates:coords count:2];
+ MGLPolylineFeature *polylineFeature = [MGLPolylineFeature polylineWithCoordinates:coords count:2];
+
+ MGLShapeCollectionFeature *shapeCollectionFeature = [MGLShapeCollectionFeature shapeCollectionWithShapes:@[pointFeature, polylineFeature]];
- MGLShapeCollectionFeature *shapeCollectionFeature = [MGLShapeCollectionFeature shapeCollectionWithShapes:@[pointFeature,
- polyline]];
// A GeoJSON feature
NSDictionary *geoJSONFeature = [shapeCollectionFeature geoJSONDictionary];
// it has the correct geometry
NSDictionary *expectedGeometry = @{@"type": @"GeometryCollection",
@"geometries": @[
- @{@"type": @"Point",
- @"coordinates": @[@(pointCoordinate.longitude), @(pointCoordinate.latitude)]},
- @{@"type": @"LineString",
- @"coordinates": @[@[@(coord1.longitude), @(coord1.latitude)],
- @[@(coord2.longitude), @(coord2.latitude)]]}
- ]};
+ @{ @"geometry": @{@"type": @"Point",
+ @"coordinates": @[@(pointCoordinate.longitude), @(pointCoordinate.latitude)]},
+ @"properties": [NSNull null],
+ @"type": @"Feature",
+ },
+ @{ @"geometry": @{@"type": @"LineString",
+ @"coordinates": @[@[@(coord1.longitude), @(coord1.latitude)],
+ @[@(coord2.longitude), @(coord2.latitude)]]},
+ @"properties": [NSNull null],
+ @"type": @"Feature",
+ }
+ ]
+ };
XCTAssertEqualObjects(geoJSONFeature[@"geometry"], expectedGeometry);
// When the shape collection is created with an empty array of shapes
diff --git a/platform/darwin/test/MGLFillExtrusionStyleLayerTests.mm b/platform/darwin/test/MGLFillExtrusionStyleLayerTests.mm
index 5d99c815ea..f5e26381d6 100644
--- a/platform/darwin/test/MGLFillExtrusionStyleLayerTests.mm
+++ b/platform/darwin/test/MGLFillExtrusionStyleLayerTests.mm
@@ -52,40 +52,44 @@
{
XCTAssertTrue(rawLayer->getFillExtrusionBase().isUndefined(),
@"fill-extrusion-base should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.fillExtrusionBase;
+ NSExpression *defaultExpression = layer.fillExtrusionBase;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.fillExtrusionBase = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.fillExtrusionBase = constantExpression;
mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getFillExtrusionBase(), propertyValue,
- @"Setting fillExtrusionBase to a constant value should update fill-extrusion-base.");
- XCTAssertEqualObjects(layer.fillExtrusionBase, constantStyleValue,
- @"fillExtrusionBase should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.fillExtrusionBase = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting fillExtrusionBase to a constant value expression should update fill-extrusion-base.");
+ XCTAssertEqualObjects(layer.fillExtrusionBase, constantExpression,
+ @"fillExtrusionBase should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.fillExtrusionBase = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getFillExtrusionBase(), propertyValue,
- @"Setting fillExtrusionBase to a camera function should update fill-extrusion-base.");
- XCTAssertEqualObjects(layer.fillExtrusionBase, functionStyleValue,
- @"fillExtrusionBase should round-trip camera functions.");
+ @"Setting fillExtrusionBase to a camera expression should update fill-extrusion-base.");
+ XCTAssertEqualObjects(layer.fillExtrusionBase, functionExpression,
+ @"fillExtrusionBase should round-trip camera expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
- layer.fillExtrusionBase = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.fillExtrusionBase = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getFillExtrusionBase(), propertyValue,
- @"Setting fillExtrusionBase to a source function should update fill-extrusion-base.");
- XCTAssertEqualObjects(layer.fillExtrusionBase, functionStyleValue,
- @"fillExtrusionBase should round-trip source functions.");
+ @"Setting fillExtrusionBase to a data expression should update fill-extrusion-base.");
+ XCTAssertEqualObjects(layer.fillExtrusionBase, functionExpression,
+ @"fillExtrusionBase should round-trip data expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
- layer.fillExtrusionBase = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.fillExtrusionBase = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 };
@@ -93,15 +97,15 @@
propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getFillExtrusionBase(), propertyValue,
- @"Setting fillExtrusionBase to a composite function should update fill-extrusion-base.");
- XCTAssertEqualObjects(layer.fillExtrusionBase, functionStyleValue,
- @"fillExtrusionBase should round-trip composite functions.");
+ @"Setting fillExtrusionBase to a camera-data expression should update fill-extrusion-base.");
+ XCTAssertEqualObjects(layer.fillExtrusionBase, functionExpression,
+ @"fillExtrusionBase should round-trip camera-data expressions.");
layer.fillExtrusionBase = nil;
XCTAssertTrue(rawLayer->getFillExtrusionBase().isUndefined(),
@"Unsetting fillExtrusionBase should return fill-extrusion-base to the default value.");
- XCTAssertEqualObjects(layer.fillExtrusionBase, defaultStyleValue,
+ XCTAssertEqualObjects(layer.fillExtrusionBase, defaultExpression,
@"fillExtrusionBase should return the default value after being unset.");
// Transition property test
layer.fillExtrusionBaseTransition = transitionTest;
@@ -118,40 +122,44 @@
{
XCTAssertTrue(rawLayer->getFillExtrusionColor().isUndefined(),
@"fill-extrusion-color should be unset initially.");
- MGLStyleValue<MGLColor *> *defaultStyleValue = layer.fillExtrusionColor;
+ NSExpression *defaultExpression = layer.fillExtrusionColor;
- MGLStyleValue<MGLColor *> *constantStyleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]];
- layer.fillExtrusionColor = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
+ layer.fillExtrusionColor = constantExpression;
mbgl::style::DataDrivenPropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } };
XCTAssertEqual(rawLayer->getFillExtrusionColor(), propertyValue,
- @"Setting fillExtrusionColor to a constant value should update fill-extrusion-color.");
- XCTAssertEqualObjects(layer.fillExtrusionColor, constantStyleValue,
- @"fillExtrusionColor should round-trip constant values.");
-
- MGLStyleValue<MGLColor *> * functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.fillExtrusionColor = functionStyleValue;
-
- mbgl::style::IntervalStops<mbgl::Color> intervalStops = { {{18, { 1, 0, 0, 1 }}} };
+ @"Setting fillExtrusionColor to a constant value expression should update fill-extrusion-color.");
+ XCTAssertEqualObjects(layer.fillExtrusionColor, constantExpression,
+ @"fillExtrusionColor should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.fillExtrusionColor = functionExpression;
+
+ mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{
+ { -INFINITY, { 1, 0, 0, 1 } },
+ { 18, { 1, 0, 0, 1 } },
+ }};
propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops };
XCTAssertEqual(rawLayer->getFillExtrusionColor(), propertyValue,
- @"Setting fillExtrusionColor to a camera function should update fill-extrusion-color.");
- XCTAssertEqualObjects(layer.fillExtrusionColor, functionStyleValue,
- @"fillExtrusionColor should round-trip camera functions.");
+ @"Setting fillExtrusionColor to a camera expression should update fill-extrusion-color.");
+ XCTAssertEqualObjects(layer.fillExtrusionColor, functionExpression,
+ @"fillExtrusionColor should round-trip camera expressions.");
- functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
- layer.fillExtrusionColor = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.fillExtrusionColor = functionExpression;
mbgl::style::ExponentialStops<mbgl::Color> exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<mbgl::Color> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getFillExtrusionColor(), propertyValue,
- @"Setting fillExtrusionColor to a source function should update fill-extrusion-color.");
- XCTAssertEqualObjects(layer.fillExtrusionColor, functionStyleValue,
- @"fillExtrusionColor should round-trip source functions.");
+ @"Setting fillExtrusionColor to a data expression should update fill-extrusion-color.");
+ XCTAssertEqualObjects(layer.fillExtrusionColor, functionExpression,
+ @"fillExtrusionColor should round-trip data expressions.");
- functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
- layer.fillExtrusionColor = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.fillExtrusionColor = functionExpression;
std::map<float, mbgl::Color> innerStops { {18, { 1, 0, 0, 1 }} };
mbgl::style::CompositeExponentialStops<mbgl::Color> compositeStops { { {10.0, innerStops} }, 1.0 };
@@ -159,15 +167,15 @@
propertyValue = mbgl::style::CompositeFunction<mbgl::Color> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getFillExtrusionColor(), propertyValue,
- @"Setting fillExtrusionColor to a composite function should update fill-extrusion-color.");
- XCTAssertEqualObjects(layer.fillExtrusionColor, functionStyleValue,
- @"fillExtrusionColor should round-trip composite functions.");
+ @"Setting fillExtrusionColor to a camera-data expression should update fill-extrusion-color.");
+ XCTAssertEqualObjects(layer.fillExtrusionColor, functionExpression,
+ @"fillExtrusionColor should round-trip camera-data expressions.");
layer.fillExtrusionColor = nil;
XCTAssertTrue(rawLayer->getFillExtrusionColor().isUndefined(),
@"Unsetting fillExtrusionColor should return fill-extrusion-color to the default value.");
- XCTAssertEqualObjects(layer.fillExtrusionColor, defaultStyleValue,
+ XCTAssertEqualObjects(layer.fillExtrusionColor, defaultExpression,
@"fillExtrusionColor should return the default value after being unset.");
// Transition property test
layer.fillExtrusionColorTransition = transitionTest;
@@ -184,40 +192,44 @@
{
XCTAssertTrue(rawLayer->getFillExtrusionHeight().isUndefined(),
@"fill-extrusion-height should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.fillExtrusionHeight;
+ NSExpression *defaultExpression = layer.fillExtrusionHeight;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.fillExtrusionHeight = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.fillExtrusionHeight = constantExpression;
mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getFillExtrusionHeight(), propertyValue,
- @"Setting fillExtrusionHeight to a constant value should update fill-extrusion-height.");
- XCTAssertEqualObjects(layer.fillExtrusionHeight, constantStyleValue,
- @"fillExtrusionHeight should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.fillExtrusionHeight = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting fillExtrusionHeight to a constant value expression should update fill-extrusion-height.");
+ XCTAssertEqualObjects(layer.fillExtrusionHeight, constantExpression,
+ @"fillExtrusionHeight should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.fillExtrusionHeight = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getFillExtrusionHeight(), propertyValue,
- @"Setting fillExtrusionHeight to a camera function should update fill-extrusion-height.");
- XCTAssertEqualObjects(layer.fillExtrusionHeight, functionStyleValue,
- @"fillExtrusionHeight should round-trip camera functions.");
+ @"Setting fillExtrusionHeight to a camera expression should update fill-extrusion-height.");
+ XCTAssertEqualObjects(layer.fillExtrusionHeight, functionExpression,
+ @"fillExtrusionHeight should round-trip camera expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
- layer.fillExtrusionHeight = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.fillExtrusionHeight = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getFillExtrusionHeight(), propertyValue,
- @"Setting fillExtrusionHeight to a source function should update fill-extrusion-height.");
- XCTAssertEqualObjects(layer.fillExtrusionHeight, functionStyleValue,
- @"fillExtrusionHeight should round-trip source functions.");
+ @"Setting fillExtrusionHeight to a data expression should update fill-extrusion-height.");
+ XCTAssertEqualObjects(layer.fillExtrusionHeight, functionExpression,
+ @"fillExtrusionHeight should round-trip data expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
- layer.fillExtrusionHeight = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.fillExtrusionHeight = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 };
@@ -225,15 +237,15 @@
propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getFillExtrusionHeight(), propertyValue,
- @"Setting fillExtrusionHeight to a composite function should update fill-extrusion-height.");
- XCTAssertEqualObjects(layer.fillExtrusionHeight, functionStyleValue,
- @"fillExtrusionHeight should round-trip composite functions.");
+ @"Setting fillExtrusionHeight to a camera-data expression should update fill-extrusion-height.");
+ XCTAssertEqualObjects(layer.fillExtrusionHeight, functionExpression,
+ @"fillExtrusionHeight should round-trip camera-data expressions.");
layer.fillExtrusionHeight = nil;
XCTAssertTrue(rawLayer->getFillExtrusionHeight().isUndefined(),
@"Unsetting fillExtrusionHeight should return fill-extrusion-height to the default value.");
- XCTAssertEqualObjects(layer.fillExtrusionHeight, defaultStyleValue,
+ XCTAssertEqualObjects(layer.fillExtrusionHeight, defaultExpression,
@"fillExtrusionHeight should return the default value after being unset.");
// Transition property test
layer.fillExtrusionHeightTransition = transitionTest;
@@ -250,39 +262,44 @@
{
XCTAssertTrue(rawLayer->getFillExtrusionOpacity().isUndefined(),
@"fill-extrusion-opacity should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.fillExtrusionOpacity;
+ NSExpression *defaultExpression = layer.fillExtrusionOpacity;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.fillExtrusionOpacity = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.fillExtrusionOpacity = constantExpression;
mbgl::style::PropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getFillExtrusionOpacity(), propertyValue,
- @"Setting fillExtrusionOpacity to a constant value should update fill-extrusion-opacity.");
- XCTAssertEqualObjects(layer.fillExtrusionOpacity, constantStyleValue,
- @"fillExtrusionOpacity should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.fillExtrusionOpacity = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting fillExtrusionOpacity to a constant value expression should update fill-extrusion-opacity.");
+ XCTAssertEqualObjects(layer.fillExtrusionOpacity, constantExpression,
+ @"fillExtrusionOpacity should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.fillExtrusionOpacity = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getFillExtrusionOpacity(), propertyValue,
- @"Setting fillExtrusionOpacity to a camera function should update fill-extrusion-opacity.");
- XCTAssertEqualObjects(layer.fillExtrusionOpacity, functionStyleValue,
- @"fillExtrusionOpacity should round-trip camera functions.");
+ @"Setting fillExtrusionOpacity to a camera expression should update fill-extrusion-opacity.");
+ XCTAssertEqualObjects(layer.fillExtrusionOpacity, functionExpression,
+ @"fillExtrusionOpacity should round-trip camera expressions.");
layer.fillExtrusionOpacity = nil;
XCTAssertTrue(rawLayer->getFillExtrusionOpacity().isUndefined(),
@"Unsetting fillExtrusionOpacity should return fill-extrusion-opacity to the default value.");
- XCTAssertEqualObjects(layer.fillExtrusionOpacity, defaultStyleValue,
+ XCTAssertEqualObjects(layer.fillExtrusionOpacity, defaultExpression,
@"fillExtrusionOpacity should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.fillExtrusionOpacity = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.fillExtrusionOpacity = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.fillExtrusionOpacity = functionExpression, NSException, NSInvalidArgumentException, @"MGLFillExtrusionLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.fillExtrusionOpacity = functionExpression, NSException, NSInvalidArgumentException, @"MGLFillExtrusionLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
// Transition property test
layer.fillExtrusionOpacityTransition = transitionTest;
auto toptions = rawLayer->getFillExtrusionOpacityTransition();
@@ -298,39 +315,44 @@
{
XCTAssertTrue(rawLayer->getFillExtrusionPattern().isUndefined(),
@"fill-extrusion-pattern should be unset initially.");
- MGLStyleValue<NSString *> *defaultStyleValue = layer.fillExtrusionPattern;
+ NSExpression *defaultExpression = layer.fillExtrusionPattern;
- MGLStyleValue<NSString *> *constantStyleValue = [MGLStyleValue<NSString *> valueWithRawValue:@"Fill Extrusion Pattern"];
- layer.fillExtrusionPattern = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'Fill Extrusion Pattern'"];
+ layer.fillExtrusionPattern = constantExpression;
mbgl::style::PropertyValue<std::string> propertyValue = { "Fill Extrusion Pattern" };
XCTAssertEqual(rawLayer->getFillExtrusionPattern(), propertyValue,
- @"Setting fillExtrusionPattern to a constant value should update fill-extrusion-pattern.");
- XCTAssertEqualObjects(layer.fillExtrusionPattern, constantStyleValue,
- @"fillExtrusionPattern should round-trip constant values.");
-
- MGLStyleValue<NSString *> * functionStyleValue = [MGLStyleValue<NSString *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.fillExtrusionPattern = functionStyleValue;
-
- mbgl::style::IntervalStops<std::string> intervalStops = { {{18, "Fill Extrusion Pattern"}} };
+ @"Setting fillExtrusionPattern to a constant value expression should update fill-extrusion-pattern.");
+ XCTAssertEqualObjects(layer.fillExtrusionPattern, constantExpression,
+ @"fillExtrusionPattern should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"'Fill Extrusion Pattern'"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.fillExtrusionPattern = functionExpression;
+
+ mbgl::style::IntervalStops<std::string> intervalStops = {{
+ { -INFINITY, "Fill Extrusion Pattern" },
+ { 18, "Fill Extrusion Pattern" },
+ }};
propertyValue = mbgl::style::CameraFunction<std::string> { intervalStops };
XCTAssertEqual(rawLayer->getFillExtrusionPattern(), propertyValue,
- @"Setting fillExtrusionPattern to a camera function should update fill-extrusion-pattern.");
- XCTAssertEqualObjects(layer.fillExtrusionPattern, functionStyleValue,
- @"fillExtrusionPattern should round-trip camera functions.");
+ @"Setting fillExtrusionPattern to a camera expression should update fill-extrusion-pattern.");
+ XCTAssertEqualObjects(layer.fillExtrusionPattern, functionExpression,
+ @"fillExtrusionPattern should round-trip camera expressions.");
layer.fillExtrusionPattern = nil;
XCTAssertTrue(rawLayer->getFillExtrusionPattern().isUndefined(),
@"Unsetting fillExtrusionPattern should return fill-extrusion-pattern to the default value.");
- XCTAssertEqualObjects(layer.fillExtrusionPattern, defaultStyleValue,
+ XCTAssertEqualObjects(layer.fillExtrusionPattern, defaultExpression,
@"fillExtrusionPattern should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSString *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.fillExtrusionPattern = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSString *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.fillExtrusionPattern = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.fillExtrusionPattern = functionExpression, NSException, NSInvalidArgumentException, @"MGLFillExtrusionLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.fillExtrusionPattern = functionExpression, NSException, NSInvalidArgumentException, @"MGLFillExtrusionLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
// Transition property test
layer.fillExtrusionPatternTransition = transitionTest;
auto toptions = rawLayer->getFillExtrusionPatternTransition();
@@ -346,84 +368,94 @@
{
XCTAssertTrue(rawLayer->getFillExtrusionTranslate().isUndefined(),
@"fill-extrusion-translate should be unset initially.");
- MGLStyleValue<NSValue *> *defaultStyleValue = layer.fillExtrusionTranslation;
+ NSExpression *defaultExpression = layer.fillExtrusionTranslation;
- MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@",
#if TARGET_OS_IPHONE
[NSValue valueWithCGVector:CGVectorMake(1, 1)]
#else
[NSValue valueWithMGLVector:CGVectorMake(1, -1)]
#endif
];
- layer.fillExtrusionTranslation = constantStyleValue;
+ layer.fillExtrusionTranslation = constantExpression;
mbgl::style::PropertyValue<std::array<float, 2>> propertyValue = { { 1, 1 } };
XCTAssertEqual(rawLayer->getFillExtrusionTranslate(), propertyValue,
- @"Setting fillExtrusionTranslation to a constant value should update fill-extrusion-translate.");
- XCTAssertEqualObjects(layer.fillExtrusionTranslation, constantStyleValue,
- @"fillExtrusionTranslation should round-trip constant values.");
-
- MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.fillExtrusionTranslation = functionStyleValue;
-
- mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = { {{18, { 1, 1 }}} };
+ @"Setting fillExtrusionTranslation to a constant value expression should update fill-extrusion-translate.");
+ XCTAssertEqualObjects(layer.fillExtrusionTranslation, constantExpression,
+ @"fillExtrusionTranslation should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"{1, 1}"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.fillExtrusionTranslation = functionExpression;
+
+ mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = {{
+ { -INFINITY, { 1, 1 } },
+ { 18, { 1, 1 } },
+ }};
propertyValue = mbgl::style::CameraFunction<std::array<float, 2>> { intervalStops };
XCTAssertEqual(rawLayer->getFillExtrusionTranslate(), propertyValue,
- @"Setting fillExtrusionTranslation to a camera function should update fill-extrusion-translate.");
- XCTAssertEqualObjects(layer.fillExtrusionTranslation, functionStyleValue,
- @"fillExtrusionTranslation should round-trip camera functions.");
+ @"Setting fillExtrusionTranslation to a camera expression should update fill-extrusion-translate.");
+ XCTAssertEqualObjects(layer.fillExtrusionTranslation, functionExpression,
+ @"fillExtrusionTranslation should round-trip camera expressions.");
layer.fillExtrusionTranslation = nil;
XCTAssertTrue(rawLayer->getFillExtrusionTranslate().isUndefined(),
@"Unsetting fillExtrusionTranslation should return fill-extrusion-translate to the default value.");
- XCTAssertEqualObjects(layer.fillExtrusionTranslation, defaultStyleValue,
+ XCTAssertEqualObjects(layer.fillExtrusionTranslation, defaultExpression,
@"fillExtrusionTranslation should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.fillExtrusionTranslation = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.fillExtrusionTranslation = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.fillExtrusionTranslation = functionExpression, NSException, NSInvalidArgumentException, @"MGLFillExtrusionLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.fillExtrusionTranslation = functionExpression, NSException, NSInvalidArgumentException, @"MGLFillExtrusionLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// fill-extrusion-translate-anchor
{
XCTAssertTrue(rawLayer->getFillExtrusionTranslateAnchor().isUndefined(),
@"fill-extrusion-translate-anchor should be unset initially.");
- MGLStyleValue<NSValue *> *defaultStyleValue = layer.fillExtrusionTranslationAnchor;
+ NSExpression *defaultExpression = layer.fillExtrusionTranslationAnchor;
- MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLFillExtrusionTranslationAnchor:MGLFillExtrusionTranslationAnchorViewport]];
- layer.fillExtrusionTranslationAnchor = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'viewport'"];
+ layer.fillExtrusionTranslationAnchor = constantExpression;
mbgl::style::PropertyValue<mbgl::style::TranslateAnchorType> propertyValue = { mbgl::style::TranslateAnchorType::Viewport };
XCTAssertEqual(rawLayer->getFillExtrusionTranslateAnchor(), propertyValue,
- @"Setting fillExtrusionTranslationAnchor to a constant value should update fill-extrusion-translate-anchor.");
- XCTAssertEqualObjects(layer.fillExtrusionTranslationAnchor, constantStyleValue,
- @"fillExtrusionTranslationAnchor should round-trip constant values.");
-
- MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.fillExtrusionTranslationAnchor = functionStyleValue;
-
- mbgl::style::IntervalStops<mbgl::style::TranslateAnchorType> intervalStops = { {{18, mbgl::style::TranslateAnchorType::Viewport}} };
+ @"Setting fillExtrusionTranslationAnchor to a constant value expression should update fill-extrusion-translate-anchor.");
+ XCTAssertEqualObjects(layer.fillExtrusionTranslationAnchor, constantExpression,
+ @"fillExtrusionTranslationAnchor should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"'viewport'"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.fillExtrusionTranslationAnchor = functionExpression;
+
+ mbgl::style::IntervalStops<mbgl::style::TranslateAnchorType> intervalStops = {{
+ { -INFINITY, mbgl::style::TranslateAnchorType::Viewport },
+ { 18, mbgl::style::TranslateAnchorType::Viewport },
+ }};
propertyValue = mbgl::style::CameraFunction<mbgl::style::TranslateAnchorType> { intervalStops };
XCTAssertEqual(rawLayer->getFillExtrusionTranslateAnchor(), propertyValue,
- @"Setting fillExtrusionTranslationAnchor to a camera function should update fill-extrusion-translate-anchor.");
- XCTAssertEqualObjects(layer.fillExtrusionTranslationAnchor, functionStyleValue,
- @"fillExtrusionTranslationAnchor should round-trip camera functions.");
+ @"Setting fillExtrusionTranslationAnchor to a camera expression should update fill-extrusion-translate-anchor.");
+ XCTAssertEqualObjects(layer.fillExtrusionTranslationAnchor, functionExpression,
+ @"fillExtrusionTranslationAnchor should round-trip camera expressions.");
layer.fillExtrusionTranslationAnchor = nil;
XCTAssertTrue(rawLayer->getFillExtrusionTranslateAnchor().isUndefined(),
@"Unsetting fillExtrusionTranslationAnchor should return fill-extrusion-translate-anchor to the default value.");
- XCTAssertEqualObjects(layer.fillExtrusionTranslationAnchor, defaultStyleValue,
+ XCTAssertEqualObjects(layer.fillExtrusionTranslationAnchor, defaultExpression,
@"fillExtrusionTranslationAnchor should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.fillExtrusionTranslationAnchor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.fillExtrusionTranslationAnchor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.fillExtrusionTranslationAnchor = functionExpression, NSException, NSInvalidArgumentException, @"MGLFillExtrusionLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.fillExtrusionTranslationAnchor = functionExpression, NSException, NSInvalidArgumentException, @"MGLFillExtrusionLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
}
diff --git a/platform/darwin/test/MGLFillStyleLayerTests.mm b/platform/darwin/test/MGLFillStyleLayerTests.mm
index 85f0b24fa7..25521f3ede 100644
--- a/platform/darwin/test/MGLFillStyleLayerTests.mm
+++ b/platform/darwin/test/MGLFillStyleLayerTests.mm
@@ -52,79 +52,88 @@
{
XCTAssertTrue(rawLayer->getFillAntialias().isUndefined(),
@"fill-antialias should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.fillAntialiased;
+ NSExpression *defaultExpression = layer.fillAntialiased;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@NO];
- layer.fillAntialiased = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"false"];
+ layer.fillAntialiased = constantExpression;
mbgl::style::PropertyValue<bool> propertyValue = { false };
XCTAssertEqual(rawLayer->getFillAntialias(), propertyValue,
- @"Setting fillAntialiased to a constant value should update fill-antialias.");
- XCTAssertEqualObjects(layer.fillAntialiased, constantStyleValue,
- @"fillAntialiased should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.fillAntialiased = functionStyleValue;
-
- mbgl::style::IntervalStops<bool> intervalStops = { {{18, false}} };
+ @"Setting fillAntialiased to a constant value expression should update fill-antialias.");
+ XCTAssertEqualObjects(layer.fillAntialiased, constantExpression,
+ @"fillAntialiased should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"false"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.fillAntialiased = functionExpression;
+
+ mbgl::style::IntervalStops<bool> intervalStops = {{
+ { -INFINITY, false },
+ { 18, false },
+ }};
propertyValue = mbgl::style::CameraFunction<bool> { intervalStops };
XCTAssertEqual(rawLayer->getFillAntialias(), propertyValue,
- @"Setting fillAntialiased to a camera function should update fill-antialias.");
- XCTAssertEqualObjects(layer.fillAntialiased, functionStyleValue,
- @"fillAntialiased should round-trip camera functions.");
+ @"Setting fillAntialiased to a camera expression should update fill-antialias.");
+ XCTAssertEqualObjects(layer.fillAntialiased, functionExpression,
+ @"fillAntialiased should round-trip camera expressions.");
layer.fillAntialiased = nil;
XCTAssertTrue(rawLayer->getFillAntialias().isUndefined(),
@"Unsetting fillAntialiased should return fill-antialias to the default value.");
- XCTAssertEqualObjects(layer.fillAntialiased, defaultStyleValue,
+ XCTAssertEqualObjects(layer.fillAntialiased, defaultExpression,
@"fillAntialiased should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.fillAntialiased = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.fillAntialiased = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.fillAntialiased = functionExpression, NSException, NSInvalidArgumentException, @"MGLFillLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.fillAntialiased = functionExpression, NSException, NSInvalidArgumentException, @"MGLFillLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// fill-color
{
XCTAssertTrue(rawLayer->getFillColor().isUndefined(),
@"fill-color should be unset initially.");
- MGLStyleValue<MGLColor *> *defaultStyleValue = layer.fillColor;
+ NSExpression *defaultExpression = layer.fillColor;
- MGLStyleValue<MGLColor *> *constantStyleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]];
- layer.fillColor = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
+ layer.fillColor = constantExpression;
mbgl::style::DataDrivenPropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } };
XCTAssertEqual(rawLayer->getFillColor(), propertyValue,
- @"Setting fillColor to a constant value should update fill-color.");
- XCTAssertEqualObjects(layer.fillColor, constantStyleValue,
- @"fillColor should round-trip constant values.");
-
- MGLStyleValue<MGLColor *> * functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.fillColor = functionStyleValue;
-
- mbgl::style::IntervalStops<mbgl::Color> intervalStops = { {{18, { 1, 0, 0, 1 }}} };
+ @"Setting fillColor to a constant value expression should update fill-color.");
+ XCTAssertEqualObjects(layer.fillColor, constantExpression,
+ @"fillColor should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.fillColor = functionExpression;
+
+ mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{
+ { -INFINITY, { 1, 0, 0, 1 } },
+ { 18, { 1, 0, 0, 1 } },
+ }};
propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops };
XCTAssertEqual(rawLayer->getFillColor(), propertyValue,
- @"Setting fillColor to a camera function should update fill-color.");
- XCTAssertEqualObjects(layer.fillColor, functionStyleValue,
- @"fillColor should round-trip camera functions.");
+ @"Setting fillColor to a camera expression should update fill-color.");
+ XCTAssertEqualObjects(layer.fillColor, functionExpression,
+ @"fillColor should round-trip camera expressions.");
- functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
- layer.fillColor = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.fillColor = functionExpression;
mbgl::style::ExponentialStops<mbgl::Color> exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<mbgl::Color> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getFillColor(), propertyValue,
- @"Setting fillColor to a source function should update fill-color.");
- XCTAssertEqualObjects(layer.fillColor, functionStyleValue,
- @"fillColor should round-trip source functions.");
+ @"Setting fillColor to a data expression should update fill-color.");
+ XCTAssertEqualObjects(layer.fillColor, functionExpression,
+ @"fillColor should round-trip data expressions.");
- functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
- layer.fillColor = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.fillColor = functionExpression;
std::map<float, mbgl::Color> innerStops { {18, { 1, 0, 0, 1 }} };
mbgl::style::CompositeExponentialStops<mbgl::Color> compositeStops { { {10.0, innerStops} }, 1.0 };
@@ -132,15 +141,15 @@
propertyValue = mbgl::style::CompositeFunction<mbgl::Color> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getFillColor(), propertyValue,
- @"Setting fillColor to a composite function should update fill-color.");
- XCTAssertEqualObjects(layer.fillColor, functionStyleValue,
- @"fillColor should round-trip composite functions.");
+ @"Setting fillColor to a camera-data expression should update fill-color.");
+ XCTAssertEqualObjects(layer.fillColor, functionExpression,
+ @"fillColor should round-trip camera-data expressions.");
layer.fillColor = nil;
XCTAssertTrue(rawLayer->getFillColor().isUndefined(),
@"Unsetting fillColor should return fill-color to the default value.");
- XCTAssertEqualObjects(layer.fillColor, defaultStyleValue,
+ XCTAssertEqualObjects(layer.fillColor, defaultExpression,
@"fillColor should return the default value after being unset.");
// Transition property test
layer.fillColorTransition = transitionTest;
@@ -157,40 +166,44 @@
{
XCTAssertTrue(rawLayer->getFillOpacity().isUndefined(),
@"fill-opacity should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.fillOpacity;
+ NSExpression *defaultExpression = layer.fillOpacity;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.fillOpacity = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.fillOpacity = constantExpression;
mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getFillOpacity(), propertyValue,
- @"Setting fillOpacity to a constant value should update fill-opacity.");
- XCTAssertEqualObjects(layer.fillOpacity, constantStyleValue,
- @"fillOpacity should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.fillOpacity = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting fillOpacity to a constant value expression should update fill-opacity.");
+ XCTAssertEqualObjects(layer.fillOpacity, constantExpression,
+ @"fillOpacity should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.fillOpacity = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getFillOpacity(), propertyValue,
- @"Setting fillOpacity to a camera function should update fill-opacity.");
- XCTAssertEqualObjects(layer.fillOpacity, functionStyleValue,
- @"fillOpacity should round-trip camera functions.");
+ @"Setting fillOpacity to a camera expression should update fill-opacity.");
+ XCTAssertEqualObjects(layer.fillOpacity, functionExpression,
+ @"fillOpacity should round-trip camera expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
- layer.fillOpacity = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.fillOpacity = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getFillOpacity(), propertyValue,
- @"Setting fillOpacity to a source function should update fill-opacity.");
- XCTAssertEqualObjects(layer.fillOpacity, functionStyleValue,
- @"fillOpacity should round-trip source functions.");
+ @"Setting fillOpacity to a data expression should update fill-opacity.");
+ XCTAssertEqualObjects(layer.fillOpacity, functionExpression,
+ @"fillOpacity should round-trip data expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
- layer.fillOpacity = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.fillOpacity = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 };
@@ -198,15 +211,15 @@
propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getFillOpacity(), propertyValue,
- @"Setting fillOpacity to a composite function should update fill-opacity.");
- XCTAssertEqualObjects(layer.fillOpacity, functionStyleValue,
- @"fillOpacity should round-trip composite functions.");
+ @"Setting fillOpacity to a camera-data expression should update fill-opacity.");
+ XCTAssertEqualObjects(layer.fillOpacity, functionExpression,
+ @"fillOpacity should round-trip camera-data expressions.");
layer.fillOpacity = nil;
XCTAssertTrue(rawLayer->getFillOpacity().isUndefined(),
@"Unsetting fillOpacity should return fill-opacity to the default value.");
- XCTAssertEqualObjects(layer.fillOpacity, defaultStyleValue,
+ XCTAssertEqualObjects(layer.fillOpacity, defaultExpression,
@"fillOpacity should return the default value after being unset.");
// Transition property test
layer.fillOpacityTransition = transitionTest;
@@ -223,40 +236,44 @@
{
XCTAssertTrue(rawLayer->getFillOutlineColor().isUndefined(),
@"fill-outline-color should be unset initially.");
- MGLStyleValue<MGLColor *> *defaultStyleValue = layer.fillOutlineColor;
+ NSExpression *defaultExpression = layer.fillOutlineColor;
- MGLStyleValue<MGLColor *> *constantStyleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]];
- layer.fillOutlineColor = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
+ layer.fillOutlineColor = constantExpression;
mbgl::style::DataDrivenPropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } };
XCTAssertEqual(rawLayer->getFillOutlineColor(), propertyValue,
- @"Setting fillOutlineColor to a constant value should update fill-outline-color.");
- XCTAssertEqualObjects(layer.fillOutlineColor, constantStyleValue,
- @"fillOutlineColor should round-trip constant values.");
-
- MGLStyleValue<MGLColor *> * functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.fillOutlineColor = functionStyleValue;
-
- mbgl::style::IntervalStops<mbgl::Color> intervalStops = { {{18, { 1, 0, 0, 1 }}} };
+ @"Setting fillOutlineColor to a constant value expression should update fill-outline-color.");
+ XCTAssertEqualObjects(layer.fillOutlineColor, constantExpression,
+ @"fillOutlineColor should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.fillOutlineColor = functionExpression;
+
+ mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{
+ { -INFINITY, { 1, 0, 0, 1 } },
+ { 18, { 1, 0, 0, 1 } },
+ }};
propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops };
XCTAssertEqual(rawLayer->getFillOutlineColor(), propertyValue,
- @"Setting fillOutlineColor to a camera function should update fill-outline-color.");
- XCTAssertEqualObjects(layer.fillOutlineColor, functionStyleValue,
- @"fillOutlineColor should round-trip camera functions.");
+ @"Setting fillOutlineColor to a camera expression should update fill-outline-color.");
+ XCTAssertEqualObjects(layer.fillOutlineColor, functionExpression,
+ @"fillOutlineColor should round-trip camera expressions.");
- functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
- layer.fillOutlineColor = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.fillOutlineColor = functionExpression;
mbgl::style::ExponentialStops<mbgl::Color> exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<mbgl::Color> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getFillOutlineColor(), propertyValue,
- @"Setting fillOutlineColor to a source function should update fill-outline-color.");
- XCTAssertEqualObjects(layer.fillOutlineColor, functionStyleValue,
- @"fillOutlineColor should round-trip source functions.");
+ @"Setting fillOutlineColor to a data expression should update fill-outline-color.");
+ XCTAssertEqualObjects(layer.fillOutlineColor, functionExpression,
+ @"fillOutlineColor should round-trip data expressions.");
- functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
- layer.fillOutlineColor = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.fillOutlineColor = functionExpression;
std::map<float, mbgl::Color> innerStops { {18, { 1, 0, 0, 1 }} };
mbgl::style::CompositeExponentialStops<mbgl::Color> compositeStops { { {10.0, innerStops} }, 1.0 };
@@ -264,15 +281,15 @@
propertyValue = mbgl::style::CompositeFunction<mbgl::Color> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getFillOutlineColor(), propertyValue,
- @"Setting fillOutlineColor to a composite function should update fill-outline-color.");
- XCTAssertEqualObjects(layer.fillOutlineColor, functionStyleValue,
- @"fillOutlineColor should round-trip composite functions.");
+ @"Setting fillOutlineColor to a camera-data expression should update fill-outline-color.");
+ XCTAssertEqualObjects(layer.fillOutlineColor, functionExpression,
+ @"fillOutlineColor should round-trip camera-data expressions.");
layer.fillOutlineColor = nil;
XCTAssertTrue(rawLayer->getFillOutlineColor().isUndefined(),
@"Unsetting fillOutlineColor should return fill-outline-color to the default value.");
- XCTAssertEqualObjects(layer.fillOutlineColor, defaultStyleValue,
+ XCTAssertEqualObjects(layer.fillOutlineColor, defaultExpression,
@"fillOutlineColor should return the default value after being unset.");
// Transition property test
layer.fillOutlineColorTransition = transitionTest;
@@ -289,39 +306,44 @@
{
XCTAssertTrue(rawLayer->getFillPattern().isUndefined(),
@"fill-pattern should be unset initially.");
- MGLStyleValue<NSString *> *defaultStyleValue = layer.fillPattern;
+ NSExpression *defaultExpression = layer.fillPattern;
- MGLStyleValue<NSString *> *constantStyleValue = [MGLStyleValue<NSString *> valueWithRawValue:@"Fill Pattern"];
- layer.fillPattern = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'Fill Pattern'"];
+ layer.fillPattern = constantExpression;
mbgl::style::PropertyValue<std::string> propertyValue = { "Fill Pattern" };
XCTAssertEqual(rawLayer->getFillPattern(), propertyValue,
- @"Setting fillPattern to a constant value should update fill-pattern.");
- XCTAssertEqualObjects(layer.fillPattern, constantStyleValue,
- @"fillPattern should round-trip constant values.");
-
- MGLStyleValue<NSString *> * functionStyleValue = [MGLStyleValue<NSString *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.fillPattern = functionStyleValue;
-
- mbgl::style::IntervalStops<std::string> intervalStops = { {{18, "Fill Pattern"}} };
+ @"Setting fillPattern to a constant value expression should update fill-pattern.");
+ XCTAssertEqualObjects(layer.fillPattern, constantExpression,
+ @"fillPattern should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"'Fill Pattern'"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.fillPattern = functionExpression;
+
+ mbgl::style::IntervalStops<std::string> intervalStops = {{
+ { -INFINITY, "Fill Pattern" },
+ { 18, "Fill Pattern" },
+ }};
propertyValue = mbgl::style::CameraFunction<std::string> { intervalStops };
XCTAssertEqual(rawLayer->getFillPattern(), propertyValue,
- @"Setting fillPattern to a camera function should update fill-pattern.");
- XCTAssertEqualObjects(layer.fillPattern, functionStyleValue,
- @"fillPattern should round-trip camera functions.");
+ @"Setting fillPattern to a camera expression should update fill-pattern.");
+ XCTAssertEqualObjects(layer.fillPattern, functionExpression,
+ @"fillPattern should round-trip camera expressions.");
layer.fillPattern = nil;
XCTAssertTrue(rawLayer->getFillPattern().isUndefined(),
@"Unsetting fillPattern should return fill-pattern to the default value.");
- XCTAssertEqualObjects(layer.fillPattern, defaultStyleValue,
+ XCTAssertEqualObjects(layer.fillPattern, defaultExpression,
@"fillPattern should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSString *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.fillPattern = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSString *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.fillPattern = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.fillPattern = functionExpression, NSException, NSInvalidArgumentException, @"MGLFillLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.fillPattern = functionExpression, NSException, NSInvalidArgumentException, @"MGLFillLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
// Transition property test
layer.fillPatternTransition = transitionTest;
auto toptions = rawLayer->getFillPatternTransition();
@@ -337,84 +359,94 @@
{
XCTAssertTrue(rawLayer->getFillTranslate().isUndefined(),
@"fill-translate should be unset initially.");
- MGLStyleValue<NSValue *> *defaultStyleValue = layer.fillTranslation;
+ NSExpression *defaultExpression = layer.fillTranslation;
- MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@",
#if TARGET_OS_IPHONE
[NSValue valueWithCGVector:CGVectorMake(1, 1)]
#else
[NSValue valueWithMGLVector:CGVectorMake(1, -1)]
#endif
];
- layer.fillTranslation = constantStyleValue;
+ layer.fillTranslation = constantExpression;
mbgl::style::PropertyValue<std::array<float, 2>> propertyValue = { { 1, 1 } };
XCTAssertEqual(rawLayer->getFillTranslate(), propertyValue,
- @"Setting fillTranslation to a constant value should update fill-translate.");
- XCTAssertEqualObjects(layer.fillTranslation, constantStyleValue,
- @"fillTranslation should round-trip constant values.");
-
- MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.fillTranslation = functionStyleValue;
-
- mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = { {{18, { 1, 1 }}} };
+ @"Setting fillTranslation to a constant value expression should update fill-translate.");
+ XCTAssertEqualObjects(layer.fillTranslation, constantExpression,
+ @"fillTranslation should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"{1, 1}"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.fillTranslation = functionExpression;
+
+ mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = {{
+ { -INFINITY, { 1, 1 } },
+ { 18, { 1, 1 } },
+ }};
propertyValue = mbgl::style::CameraFunction<std::array<float, 2>> { intervalStops };
XCTAssertEqual(rawLayer->getFillTranslate(), propertyValue,
- @"Setting fillTranslation to a camera function should update fill-translate.");
- XCTAssertEqualObjects(layer.fillTranslation, functionStyleValue,
- @"fillTranslation should round-trip camera functions.");
+ @"Setting fillTranslation to a camera expression should update fill-translate.");
+ XCTAssertEqualObjects(layer.fillTranslation, functionExpression,
+ @"fillTranslation should round-trip camera expressions.");
layer.fillTranslation = nil;
XCTAssertTrue(rawLayer->getFillTranslate().isUndefined(),
@"Unsetting fillTranslation should return fill-translate to the default value.");
- XCTAssertEqualObjects(layer.fillTranslation, defaultStyleValue,
+ XCTAssertEqualObjects(layer.fillTranslation, defaultExpression,
@"fillTranslation should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.fillTranslation = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.fillTranslation = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.fillTranslation = functionExpression, NSException, NSInvalidArgumentException, @"MGLFillLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.fillTranslation = functionExpression, NSException, NSInvalidArgumentException, @"MGLFillLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// fill-translate-anchor
{
XCTAssertTrue(rawLayer->getFillTranslateAnchor().isUndefined(),
@"fill-translate-anchor should be unset initially.");
- MGLStyleValue<NSValue *> *defaultStyleValue = layer.fillTranslationAnchor;
+ NSExpression *defaultExpression = layer.fillTranslationAnchor;
- MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLFillTranslationAnchor:MGLFillTranslationAnchorViewport]];
- layer.fillTranslationAnchor = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'viewport'"];
+ layer.fillTranslationAnchor = constantExpression;
mbgl::style::PropertyValue<mbgl::style::TranslateAnchorType> propertyValue = { mbgl::style::TranslateAnchorType::Viewport };
XCTAssertEqual(rawLayer->getFillTranslateAnchor(), propertyValue,
- @"Setting fillTranslationAnchor to a constant value should update fill-translate-anchor.");
- XCTAssertEqualObjects(layer.fillTranslationAnchor, constantStyleValue,
- @"fillTranslationAnchor should round-trip constant values.");
-
- MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.fillTranslationAnchor = functionStyleValue;
-
- mbgl::style::IntervalStops<mbgl::style::TranslateAnchorType> intervalStops = { {{18, mbgl::style::TranslateAnchorType::Viewport}} };
+ @"Setting fillTranslationAnchor to a constant value expression should update fill-translate-anchor.");
+ XCTAssertEqualObjects(layer.fillTranslationAnchor, constantExpression,
+ @"fillTranslationAnchor should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"'viewport'"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.fillTranslationAnchor = functionExpression;
+
+ mbgl::style::IntervalStops<mbgl::style::TranslateAnchorType> intervalStops = {{
+ { -INFINITY, mbgl::style::TranslateAnchorType::Viewport },
+ { 18, mbgl::style::TranslateAnchorType::Viewport },
+ }};
propertyValue = mbgl::style::CameraFunction<mbgl::style::TranslateAnchorType> { intervalStops };
XCTAssertEqual(rawLayer->getFillTranslateAnchor(), propertyValue,
- @"Setting fillTranslationAnchor to a camera function should update fill-translate-anchor.");
- XCTAssertEqualObjects(layer.fillTranslationAnchor, functionStyleValue,
- @"fillTranslationAnchor should round-trip camera functions.");
+ @"Setting fillTranslationAnchor to a camera expression should update fill-translate-anchor.");
+ XCTAssertEqualObjects(layer.fillTranslationAnchor, functionExpression,
+ @"fillTranslationAnchor should round-trip camera expressions.");
layer.fillTranslationAnchor = nil;
XCTAssertTrue(rawLayer->getFillTranslateAnchor().isUndefined(),
@"Unsetting fillTranslationAnchor should return fill-translate-anchor to the default value.");
- XCTAssertEqualObjects(layer.fillTranslationAnchor, defaultStyleValue,
+ XCTAssertEqualObjects(layer.fillTranslationAnchor, defaultExpression,
@"fillTranslationAnchor should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.fillTranslationAnchor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.fillTranslationAnchor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.fillTranslationAnchor = functionExpression, NSException, NSInvalidArgumentException, @"MGLFillLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.fillTranslationAnchor = functionExpression, NSException, NSInvalidArgumentException, @"MGLFillLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
}
diff --git a/platform/darwin/test/MGLHeatmapColorTests.mm b/platform/darwin/test/MGLHeatmapColorTests.mm
new file mode 100644
index 0000000000..8d44064d94
--- /dev/null
+++ b/platform/darwin/test/MGLHeatmapColorTests.mm
@@ -0,0 +1,61 @@
+#import <Mapbox/Mapbox.h>
+#import <XCTest/XCTest.h>
+
+#import "MGLStyleLayer_Private.h"
+
+#include <mbgl/style/layers/heatmap_layer.hpp>
+
+@interface MGLHeatmapColorTests : XCTestCase <MGLMapViewDelegate>
+@end
+
+@implementation MGLHeatmapColorTests
+
+- (void)testProperties {
+ MGLPointFeature *feature = [[MGLPointFeature alloc] init];
+ MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"sourceID" shape:feature options:nil];
+ MGLHeatmapStyleLayer *layer = [[MGLHeatmapStyleLayer alloc] initWithIdentifier:@"layerID" source:source];
+
+ auto rawLayer = layer.rawLayer->as<mbgl::style::HeatmapLayer>();
+
+ XCTAssertTrue(rawLayer->getHeatmapColor().isUndefined(),
+ @"heatmap-color should be unset initially.");
+ NSExpression *defaultExpression = layer.heatmapColor;
+
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
+ layer.heatmapColor = constantExpression;
+
+
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getHeatmapColor().evaluate(0.0), mbgl::Color::red(),
+ @"Setting heatmapColor to a constant value expression should update heatmap-color.");
+ XCTAssertEqualObjects(layer.heatmapColor, constantExpression,
+ @"heatmapColor should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
+ NSExpression *constantExpression2 = [NSExpression expressionWithFormat:@"%@", [MGLColor blueColor]];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($heatmapDensity, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@12: constantExpression2}];
+ layer.heatmapColor = functionExpression;
+
+ XCTAssertEqual(rawLayer->getHeatmapColor().evaluate(11.0), mbgl::Color::red(),
+ @"Setting heatmapColor to an expression depending on $heatmapDensity should update heatmap-color.");
+ XCTAssertEqual(rawLayer->getHeatmapColor().evaluate(12.0), mbgl::Color::blue(),
+ @"Setting heatmapColor to an expression depending on $heatmapDensity should update heatmap-color.");
+ XCTAssertEqualObjects(layer.heatmapColor, functionExpression,
+ @"heatmapColor should round-trip expressions depending on $heatmapDensity.");
+
+ layer.heatmapColor = nil;
+ XCTAssertTrue(rawLayer->getHeatmapColor().isUndefined(),
+ @"Unsetting heatmapColor should return heatmap-color to the default value.");
+ XCTAssertEqualObjects(layer.heatmapColor, defaultExpression,
+ @"heatmapColor should return the default value after being unset.");
+
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ XCTAssertThrowsSpecificNamed(layer.heatmapColor = functionExpression, NSException, NSInvalidArgumentException, @"MGLHeatmapLayer should raise an exception if a camera expression is applied to heatmapColor.");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.heatmapColor = functionExpression, NSException, NSInvalidArgumentException, @"MGLHeatmapLayer should raise an exception if a data expression is applied to heatmapColor.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.heatmapColor = functionExpression, NSException, NSInvalidArgumentException, @"MGLHeatmapLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+}
+
+@end
diff --git a/platform/darwin/test/MGLHeatmapStyleLayerTests.mm b/platform/darwin/test/MGLHeatmapStyleLayerTests.mm
new file mode 100644
index 0000000000..74121affd8
--- /dev/null
+++ b/platform/darwin/test/MGLHeatmapStyleLayerTests.mm
@@ -0,0 +1,296 @@
+// This file is generated.
+// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
+
+#import "MGLStyleLayerTests.h"
+#import "../../darwin/src/NSDate+MGLAdditions.h"
+
+#import "MGLStyleLayer_Private.h"
+
+#include <mbgl/style/layers/heatmap_layer.hpp>
+#include <mbgl/style/transition_options.hpp>
+
+@interface MGLHeatmapLayerTests : MGLStyleLayerTests
+@end
+
+@implementation MGLHeatmapLayerTests
+
++ (NSString *)layerType {
+ return @"heatmap";
+}
+
+- (void)testPredicates {
+ MGLPointFeature *feature = [[MGLPointFeature alloc] init];
+ MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"sourceID" shape:feature options:nil];
+ MGLHeatmapStyleLayer *layer = [[MGLHeatmapStyleLayer alloc] initWithIdentifier:@"layerID" source:source];
+
+ XCTAssertNil(layer.sourceLayerIdentifier);
+ layer.sourceLayerIdentifier = @"layerID";
+ XCTAssertEqualObjects(layer.sourceLayerIdentifier, @"layerID");
+ layer.sourceLayerIdentifier = nil;
+ XCTAssertNil(layer.sourceLayerIdentifier);
+
+ XCTAssertNil(layer.predicate);
+ layer.predicate = [NSPredicate predicateWithValue:NO];
+ XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithValue:NO]);
+ layer.predicate = nil;
+ XCTAssertNil(layer.predicate);
+}
+
+- (void)testProperties {
+ MGLPointFeature *feature = [[MGLPointFeature alloc] init];
+ MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"sourceID" shape:feature options:nil];
+
+ MGLHeatmapStyleLayer *layer = [[MGLHeatmapStyleLayer alloc] initWithIdentifier:@"layerID" source:source];
+ XCTAssertNotEqual(layer.rawLayer, nullptr);
+ XCTAssertTrue(layer.rawLayer->is<mbgl::style::HeatmapLayer>());
+ auto rawLayer = layer.rawLayer->as<mbgl::style::HeatmapLayer>();
+
+ MGLTransition transitionTest = MGLTransitionMake(5, 4);
+
+
+ // heatmap-intensity
+ {
+ XCTAssertTrue(rawLayer->getHeatmapIntensity().isUndefined(),
+ @"heatmap-intensity should be unset initially.");
+ NSExpression *defaultExpression = layer.heatmapIntensity;
+
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.heatmapIntensity = constantExpression;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getHeatmapIntensity(), propertyValue,
+ @"Setting heatmapIntensity to a constant value expression should update heatmap-intensity.");
+ XCTAssertEqualObjects(layer.heatmapIntensity, constantExpression,
+ @"heatmapIntensity should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.heatmapIntensity = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
+ propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
+
+ XCTAssertEqual(rawLayer->getHeatmapIntensity(), propertyValue,
+ @"Setting heatmapIntensity to a camera expression should update heatmap-intensity.");
+ XCTAssertEqualObjects(layer.heatmapIntensity, functionExpression,
+ @"heatmapIntensity should round-trip camera expressions.");
+
+
+
+ layer.heatmapIntensity = nil;
+ XCTAssertTrue(rawLayer->getHeatmapIntensity().isUndefined(),
+ @"Unsetting heatmapIntensity should return heatmap-intensity to the default value.");
+ XCTAssertEqualObjects(layer.heatmapIntensity, defaultExpression,
+ @"heatmapIntensity should return the default value after being unset.");
+
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.heatmapIntensity = functionExpression, NSException, NSInvalidArgumentException, @"MGLHeatmapLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.heatmapIntensity = functionExpression, NSException, NSInvalidArgumentException, @"MGLHeatmapLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ // Transition property test
+ layer.heatmapIntensityTransition = transitionTest;
+ auto toptions = rawLayer->getHeatmapIntensityTransition();
+ XCTAssert(toptions.delay && MGLTimeIntervalFromDuration(*toptions.delay) == transitionTest.delay);
+ XCTAssert(toptions.duration && MGLTimeIntervalFromDuration(*toptions.duration) == transitionTest.duration);
+
+ MGLTransition heatmapIntensityTransition = layer.heatmapIntensityTransition;
+ XCTAssertEqual(heatmapIntensityTransition.delay, transitionTest.delay);
+ XCTAssertEqual(heatmapIntensityTransition.duration, transitionTest.duration);
+ }
+
+ // heatmap-opacity
+ {
+ XCTAssertTrue(rawLayer->getHeatmapOpacity().isUndefined(),
+ @"heatmap-opacity should be unset initially.");
+ NSExpression *defaultExpression = layer.heatmapOpacity;
+
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.heatmapOpacity = constantExpression;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getHeatmapOpacity(), propertyValue,
+ @"Setting heatmapOpacity to a constant value expression should update heatmap-opacity.");
+ XCTAssertEqualObjects(layer.heatmapOpacity, constantExpression,
+ @"heatmapOpacity should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.heatmapOpacity = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
+ propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
+
+ XCTAssertEqual(rawLayer->getHeatmapOpacity(), propertyValue,
+ @"Setting heatmapOpacity to a camera expression should update heatmap-opacity.");
+ XCTAssertEqualObjects(layer.heatmapOpacity, functionExpression,
+ @"heatmapOpacity should round-trip camera expressions.");
+
+
+
+ layer.heatmapOpacity = nil;
+ XCTAssertTrue(rawLayer->getHeatmapOpacity().isUndefined(),
+ @"Unsetting heatmapOpacity should return heatmap-opacity to the default value.");
+ XCTAssertEqualObjects(layer.heatmapOpacity, defaultExpression,
+ @"heatmapOpacity should return the default value after being unset.");
+
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.heatmapOpacity = functionExpression, NSException, NSInvalidArgumentException, @"MGLHeatmapLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.heatmapOpacity = functionExpression, NSException, NSInvalidArgumentException, @"MGLHeatmapLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ // Transition property test
+ layer.heatmapOpacityTransition = transitionTest;
+ auto toptions = rawLayer->getHeatmapOpacityTransition();
+ XCTAssert(toptions.delay && MGLTimeIntervalFromDuration(*toptions.delay) == transitionTest.delay);
+ XCTAssert(toptions.duration && MGLTimeIntervalFromDuration(*toptions.duration) == transitionTest.duration);
+
+ MGLTransition heatmapOpacityTransition = layer.heatmapOpacityTransition;
+ XCTAssertEqual(heatmapOpacityTransition.delay, transitionTest.delay);
+ XCTAssertEqual(heatmapOpacityTransition.duration, transitionTest.duration);
+ }
+
+ // heatmap-radius
+ {
+ XCTAssertTrue(rawLayer->getHeatmapRadius().isUndefined(),
+ @"heatmap-radius should be unset initially.");
+ NSExpression *defaultExpression = layer.heatmapRadius;
+
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.heatmapRadius = constantExpression;
+ mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getHeatmapRadius(), propertyValue,
+ @"Setting heatmapRadius to a constant value expression should update heatmap-radius.");
+ XCTAssertEqualObjects(layer.heatmapRadius, constantExpression,
+ @"heatmapRadius should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.heatmapRadius = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
+ propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
+
+ XCTAssertEqual(rawLayer->getHeatmapRadius(), propertyValue,
+ @"Setting heatmapRadius to a camera expression should update heatmap-radius.");
+ XCTAssertEqualObjects(layer.heatmapRadius, functionExpression,
+ @"heatmapRadius should round-trip camera expressions.");
+
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.heatmapRadius = functionExpression;
+
+ mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
+ propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops };
+
+ XCTAssertEqual(rawLayer->getHeatmapRadius(), propertyValue,
+ @"Setting heatmapRadius to a data expression should update heatmap-radius.");
+ XCTAssertEqualObjects(layer.heatmapRadius, functionExpression,
+ @"heatmapRadius should round-trip data expressions.");
+
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.heatmapRadius = functionExpression;
+
+ std::map<float, float> innerStops { {18, 0xff} };
+ mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 };
+
+ propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops };
+
+ XCTAssertEqual(rawLayer->getHeatmapRadius(), propertyValue,
+ @"Setting heatmapRadius to a camera-data expression should update heatmap-radius.");
+ XCTAssertEqualObjects(layer.heatmapRadius, functionExpression,
+ @"heatmapRadius should round-trip camera-data expressions.");
+
+
+ layer.heatmapRadius = nil;
+ XCTAssertTrue(rawLayer->getHeatmapRadius().isUndefined(),
+ @"Unsetting heatmapRadius should return heatmap-radius to the default value.");
+ XCTAssertEqualObjects(layer.heatmapRadius, defaultExpression,
+ @"heatmapRadius should return the default value after being unset.");
+ // Transition property test
+ layer.heatmapRadiusTransition = transitionTest;
+ auto toptions = rawLayer->getHeatmapRadiusTransition();
+ XCTAssert(toptions.delay && MGLTimeIntervalFromDuration(*toptions.delay) == transitionTest.delay);
+ XCTAssert(toptions.duration && MGLTimeIntervalFromDuration(*toptions.duration) == transitionTest.duration);
+
+ MGLTransition heatmapRadiusTransition = layer.heatmapRadiusTransition;
+ XCTAssertEqual(heatmapRadiusTransition.delay, transitionTest.delay);
+ XCTAssertEqual(heatmapRadiusTransition.duration, transitionTest.duration);
+ }
+
+ // heatmap-weight
+ {
+ XCTAssertTrue(rawLayer->getHeatmapWeight().isUndefined(),
+ @"heatmap-weight should be unset initially.");
+ NSExpression *defaultExpression = layer.heatmapWeight;
+
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.heatmapWeight = constantExpression;
+ mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getHeatmapWeight(), propertyValue,
+ @"Setting heatmapWeight to a constant value expression should update heatmap-weight.");
+ XCTAssertEqualObjects(layer.heatmapWeight, constantExpression,
+ @"heatmapWeight should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.heatmapWeight = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
+ propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
+
+ XCTAssertEqual(rawLayer->getHeatmapWeight(), propertyValue,
+ @"Setting heatmapWeight to a camera expression should update heatmap-weight.");
+ XCTAssertEqualObjects(layer.heatmapWeight, functionExpression,
+ @"heatmapWeight should round-trip camera expressions.");
+
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.heatmapWeight = functionExpression;
+
+ mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
+ propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops };
+
+ XCTAssertEqual(rawLayer->getHeatmapWeight(), propertyValue,
+ @"Setting heatmapWeight to a data expression should update heatmap-weight.");
+ XCTAssertEqualObjects(layer.heatmapWeight, functionExpression,
+ @"heatmapWeight should round-trip data expressions.");
+
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.heatmapWeight = functionExpression;
+
+ std::map<float, float> innerStops { {18, 0xff} };
+ mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 };
+
+ propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops };
+
+ XCTAssertEqual(rawLayer->getHeatmapWeight(), propertyValue,
+ @"Setting heatmapWeight to a camera-data expression should update heatmap-weight.");
+ XCTAssertEqualObjects(layer.heatmapWeight, functionExpression,
+ @"heatmapWeight should round-trip camera-data expressions.");
+
+
+ layer.heatmapWeight = nil;
+ XCTAssertTrue(rawLayer->getHeatmapWeight().isUndefined(),
+ @"Unsetting heatmapWeight should return heatmap-weight to the default value.");
+ XCTAssertEqualObjects(layer.heatmapWeight, defaultExpression,
+ @"heatmapWeight should return the default value after being unset.");
+ }
+}
+
+- (void)testPropertyNames {
+ [self testPropertyName:@"heatmap-intensity" isBoolean:NO];
+ [self testPropertyName:@"heatmap-opacity" isBoolean:NO];
+ [self testPropertyName:@"heatmap-radius" isBoolean:NO];
+ [self testPropertyName:@"heatmap-weight" isBoolean:NO];
+}
+
+@end
diff --git a/platform/darwin/test/MGLHillshadeStyleLayerTests.mm b/platform/darwin/test/MGLHillshadeStyleLayerTests.mm
new file mode 100644
index 0000000000..b0b7ddd5ee
--- /dev/null
+++ b/platform/darwin/test/MGLHillshadeStyleLayerTests.mm
@@ -0,0 +1,348 @@
+// This file is generated.
+// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
+
+#import "MGLStyleLayerTests.h"
+#import "../../darwin/src/NSDate+MGLAdditions.h"
+
+#import "MGLStyleLayer_Private.h"
+
+#include <mbgl/style/layers/hillshade_layer.hpp>
+#include <mbgl/style/transition_options.hpp>
+
+@interface MGLHillshadeLayerTests : MGLStyleLayerTests
+@end
+
+@implementation MGLHillshadeLayerTests
+
++ (NSString *)layerType {
+ return @"hillshade";
+}
+
+- (void)testProperties {
+ MGLPointFeature *feature = [[MGLPointFeature alloc] init];
+ MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"sourceID" shape:feature options:nil];
+
+ MGLHillshadeStyleLayer *layer = [[MGLHillshadeStyleLayer alloc] initWithIdentifier:@"layerID" source:source];
+ XCTAssertNotEqual(layer.rawLayer, nullptr);
+ XCTAssertTrue(layer.rawLayer->is<mbgl::style::HillshadeLayer>());
+ auto rawLayer = layer.rawLayer->as<mbgl::style::HillshadeLayer>();
+
+ MGLTransition transitionTest = MGLTransitionMake(5, 4);
+
+
+ // hillshade-accent-color
+ {
+ XCTAssertTrue(rawLayer->getHillshadeAccentColor().isUndefined(),
+ @"hillshade-accent-color should be unset initially.");
+ NSExpression *defaultExpression = layer.hillshadeAccentColor;
+
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
+ layer.hillshadeAccentColor = constantExpression;
+ mbgl::style::PropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } };
+ XCTAssertEqual(rawLayer->getHillshadeAccentColor(), propertyValue,
+ @"Setting hillshadeAccentColor to a constant value expression should update hillshade-accent-color.");
+ XCTAssertEqualObjects(layer.hillshadeAccentColor, constantExpression,
+ @"hillshadeAccentColor should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.hillshadeAccentColor = functionExpression;
+
+ mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{
+ { -INFINITY, { 1, 0, 0, 1 } },
+ { 18, { 1, 0, 0, 1 } },
+ }};
+ propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops };
+
+ XCTAssertEqual(rawLayer->getHillshadeAccentColor(), propertyValue,
+ @"Setting hillshadeAccentColor to a camera expression should update hillshade-accent-color.");
+ XCTAssertEqualObjects(layer.hillshadeAccentColor, functionExpression,
+ @"hillshadeAccentColor should round-trip camera expressions.");
+
+
+
+ layer.hillshadeAccentColor = nil;
+ XCTAssertTrue(rawLayer->getHillshadeAccentColor().isUndefined(),
+ @"Unsetting hillshadeAccentColor should return hillshade-accent-color to the default value.");
+ XCTAssertEqualObjects(layer.hillshadeAccentColor, defaultExpression,
+ @"hillshadeAccentColor should return the default value after being unset.");
+
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.hillshadeAccentColor = functionExpression, NSException, NSInvalidArgumentException, @"MGLHillshadeLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.hillshadeAccentColor = functionExpression, NSException, NSInvalidArgumentException, @"MGLHillshadeLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ // Transition property test
+ layer.hillshadeAccentColorTransition = transitionTest;
+ auto toptions = rawLayer->getHillshadeAccentColorTransition();
+ XCTAssert(toptions.delay && MGLTimeIntervalFromDuration(*toptions.delay) == transitionTest.delay);
+ XCTAssert(toptions.duration && MGLTimeIntervalFromDuration(*toptions.duration) == transitionTest.duration);
+
+ MGLTransition hillshadeAccentColorTransition = layer.hillshadeAccentColorTransition;
+ XCTAssertEqual(hillshadeAccentColorTransition.delay, transitionTest.delay);
+ XCTAssertEqual(hillshadeAccentColorTransition.duration, transitionTest.duration);
+ }
+
+ // hillshade-exaggeration
+ {
+ XCTAssertTrue(rawLayer->getHillshadeExaggeration().isUndefined(),
+ @"hillshade-exaggeration should be unset initially.");
+ NSExpression *defaultExpression = layer.hillshadeExaggeration;
+
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.hillshadeExaggeration = constantExpression;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getHillshadeExaggeration(), propertyValue,
+ @"Setting hillshadeExaggeration to a constant value expression should update hillshade-exaggeration.");
+ XCTAssertEqualObjects(layer.hillshadeExaggeration, constantExpression,
+ @"hillshadeExaggeration should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.hillshadeExaggeration = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
+ propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
+
+ XCTAssertEqual(rawLayer->getHillshadeExaggeration(), propertyValue,
+ @"Setting hillshadeExaggeration to a camera expression should update hillshade-exaggeration.");
+ XCTAssertEqualObjects(layer.hillshadeExaggeration, functionExpression,
+ @"hillshadeExaggeration should round-trip camera expressions.");
+
+
+
+ layer.hillshadeExaggeration = nil;
+ XCTAssertTrue(rawLayer->getHillshadeExaggeration().isUndefined(),
+ @"Unsetting hillshadeExaggeration should return hillshade-exaggeration to the default value.");
+ XCTAssertEqualObjects(layer.hillshadeExaggeration, defaultExpression,
+ @"hillshadeExaggeration should return the default value after being unset.");
+
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.hillshadeExaggeration = functionExpression, NSException, NSInvalidArgumentException, @"MGLHillshadeLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.hillshadeExaggeration = functionExpression, NSException, NSInvalidArgumentException, @"MGLHillshadeLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ // Transition property test
+ layer.hillshadeExaggerationTransition = transitionTest;
+ auto toptions = rawLayer->getHillshadeExaggerationTransition();
+ XCTAssert(toptions.delay && MGLTimeIntervalFromDuration(*toptions.delay) == transitionTest.delay);
+ XCTAssert(toptions.duration && MGLTimeIntervalFromDuration(*toptions.duration) == transitionTest.duration);
+
+ MGLTransition hillshadeExaggerationTransition = layer.hillshadeExaggerationTransition;
+ XCTAssertEqual(hillshadeExaggerationTransition.delay, transitionTest.delay);
+ XCTAssertEqual(hillshadeExaggerationTransition.duration, transitionTest.duration);
+ }
+
+ // hillshade-highlight-color
+ {
+ XCTAssertTrue(rawLayer->getHillshadeHighlightColor().isUndefined(),
+ @"hillshade-highlight-color should be unset initially.");
+ NSExpression *defaultExpression = layer.hillshadeHighlightColor;
+
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
+ layer.hillshadeHighlightColor = constantExpression;
+ mbgl::style::PropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } };
+ XCTAssertEqual(rawLayer->getHillshadeHighlightColor(), propertyValue,
+ @"Setting hillshadeHighlightColor to a constant value expression should update hillshade-highlight-color.");
+ XCTAssertEqualObjects(layer.hillshadeHighlightColor, constantExpression,
+ @"hillshadeHighlightColor should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.hillshadeHighlightColor = functionExpression;
+
+ mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{
+ { -INFINITY, { 1, 0, 0, 1 } },
+ { 18, { 1, 0, 0, 1 } },
+ }};
+ propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops };
+
+ XCTAssertEqual(rawLayer->getHillshadeHighlightColor(), propertyValue,
+ @"Setting hillshadeHighlightColor to a camera expression should update hillshade-highlight-color.");
+ XCTAssertEqualObjects(layer.hillshadeHighlightColor, functionExpression,
+ @"hillshadeHighlightColor should round-trip camera expressions.");
+
+
+
+ layer.hillshadeHighlightColor = nil;
+ XCTAssertTrue(rawLayer->getHillshadeHighlightColor().isUndefined(),
+ @"Unsetting hillshadeHighlightColor should return hillshade-highlight-color to the default value.");
+ XCTAssertEqualObjects(layer.hillshadeHighlightColor, defaultExpression,
+ @"hillshadeHighlightColor should return the default value after being unset.");
+
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.hillshadeHighlightColor = functionExpression, NSException, NSInvalidArgumentException, @"MGLHillshadeLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.hillshadeHighlightColor = functionExpression, NSException, NSInvalidArgumentException, @"MGLHillshadeLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ // Transition property test
+ layer.hillshadeHighlightColorTransition = transitionTest;
+ auto toptions = rawLayer->getHillshadeHighlightColorTransition();
+ XCTAssert(toptions.delay && MGLTimeIntervalFromDuration(*toptions.delay) == transitionTest.delay);
+ XCTAssert(toptions.duration && MGLTimeIntervalFromDuration(*toptions.duration) == transitionTest.duration);
+
+ MGLTransition hillshadeHighlightColorTransition = layer.hillshadeHighlightColorTransition;
+ XCTAssertEqual(hillshadeHighlightColorTransition.delay, transitionTest.delay);
+ XCTAssertEqual(hillshadeHighlightColorTransition.duration, transitionTest.duration);
+ }
+
+ // hillshade-illumination-anchor
+ {
+ XCTAssertTrue(rawLayer->getHillshadeIlluminationAnchor().isUndefined(),
+ @"hillshade-illumination-anchor should be unset initially.");
+ NSExpression *defaultExpression = layer.hillshadeIlluminationAnchor;
+
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'viewport'"];
+ layer.hillshadeIlluminationAnchor = constantExpression;
+ mbgl::style::PropertyValue<mbgl::style::HillshadeIlluminationAnchorType> propertyValue = { mbgl::style::HillshadeIlluminationAnchorType::Viewport };
+ XCTAssertEqual(rawLayer->getHillshadeIlluminationAnchor(), propertyValue,
+ @"Setting hillshadeIlluminationAnchor to a constant value expression should update hillshade-illumination-anchor.");
+ XCTAssertEqualObjects(layer.hillshadeIlluminationAnchor, constantExpression,
+ @"hillshadeIlluminationAnchor should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"'viewport'"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.hillshadeIlluminationAnchor = functionExpression;
+
+ mbgl::style::IntervalStops<mbgl::style::HillshadeIlluminationAnchorType> intervalStops = {{
+ { -INFINITY, mbgl::style::HillshadeIlluminationAnchorType::Viewport },
+ { 18, mbgl::style::HillshadeIlluminationAnchorType::Viewport },
+ }};
+ propertyValue = mbgl::style::CameraFunction<mbgl::style::HillshadeIlluminationAnchorType> { intervalStops };
+
+ XCTAssertEqual(rawLayer->getHillshadeIlluminationAnchor(), propertyValue,
+ @"Setting hillshadeIlluminationAnchor to a camera expression should update hillshade-illumination-anchor.");
+ XCTAssertEqualObjects(layer.hillshadeIlluminationAnchor, functionExpression,
+ @"hillshadeIlluminationAnchor should round-trip camera expressions.");
+
+
+
+ layer.hillshadeIlluminationAnchor = nil;
+ XCTAssertTrue(rawLayer->getHillshadeIlluminationAnchor().isUndefined(),
+ @"Unsetting hillshadeIlluminationAnchor should return hillshade-illumination-anchor to the default value.");
+ XCTAssertEqualObjects(layer.hillshadeIlluminationAnchor, defaultExpression,
+ @"hillshadeIlluminationAnchor should return the default value after being unset.");
+
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.hillshadeIlluminationAnchor = functionExpression, NSException, NSInvalidArgumentException, @"MGLHillshadeLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.hillshadeIlluminationAnchor = functionExpression, NSException, NSInvalidArgumentException, @"MGLHillshadeLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ }
+
+ // hillshade-illumination-direction
+ {
+ XCTAssertTrue(rawLayer->getHillshadeIlluminationDirection().isUndefined(),
+ @"hillshade-illumination-direction should be unset initially.");
+ NSExpression *defaultExpression = layer.hillshadeIlluminationDirection;
+
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.hillshadeIlluminationDirection = constantExpression;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getHillshadeIlluminationDirection(), propertyValue,
+ @"Setting hillshadeIlluminationDirection to a constant value expression should update hillshade-illumination-direction.");
+ XCTAssertEqualObjects(layer.hillshadeIlluminationDirection, constantExpression,
+ @"hillshadeIlluminationDirection should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.hillshadeIlluminationDirection = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
+ propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
+
+ XCTAssertEqual(rawLayer->getHillshadeIlluminationDirection(), propertyValue,
+ @"Setting hillshadeIlluminationDirection to a camera expression should update hillshade-illumination-direction.");
+ XCTAssertEqualObjects(layer.hillshadeIlluminationDirection, functionExpression,
+ @"hillshadeIlluminationDirection should round-trip camera expressions.");
+
+
+
+ layer.hillshadeIlluminationDirection = nil;
+ XCTAssertTrue(rawLayer->getHillshadeIlluminationDirection().isUndefined(),
+ @"Unsetting hillshadeIlluminationDirection should return hillshade-illumination-direction to the default value.");
+ XCTAssertEqualObjects(layer.hillshadeIlluminationDirection, defaultExpression,
+ @"hillshadeIlluminationDirection should return the default value after being unset.");
+
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.hillshadeIlluminationDirection = functionExpression, NSException, NSInvalidArgumentException, @"MGLHillshadeLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.hillshadeIlluminationDirection = functionExpression, NSException, NSInvalidArgumentException, @"MGLHillshadeLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ }
+
+ // hillshade-shadow-color
+ {
+ XCTAssertTrue(rawLayer->getHillshadeShadowColor().isUndefined(),
+ @"hillshade-shadow-color should be unset initially.");
+ NSExpression *defaultExpression = layer.hillshadeShadowColor;
+
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
+ layer.hillshadeShadowColor = constantExpression;
+ mbgl::style::PropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } };
+ XCTAssertEqual(rawLayer->getHillshadeShadowColor(), propertyValue,
+ @"Setting hillshadeShadowColor to a constant value expression should update hillshade-shadow-color.");
+ XCTAssertEqualObjects(layer.hillshadeShadowColor, constantExpression,
+ @"hillshadeShadowColor should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.hillshadeShadowColor = functionExpression;
+
+ mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{
+ { -INFINITY, { 1, 0, 0, 1 } },
+ { 18, { 1, 0, 0, 1 } },
+ }};
+ propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops };
+
+ XCTAssertEqual(rawLayer->getHillshadeShadowColor(), propertyValue,
+ @"Setting hillshadeShadowColor to a camera expression should update hillshade-shadow-color.");
+ XCTAssertEqualObjects(layer.hillshadeShadowColor, functionExpression,
+ @"hillshadeShadowColor should round-trip camera expressions.");
+
+
+
+ layer.hillshadeShadowColor = nil;
+ XCTAssertTrue(rawLayer->getHillshadeShadowColor().isUndefined(),
+ @"Unsetting hillshadeShadowColor should return hillshade-shadow-color to the default value.");
+ XCTAssertEqualObjects(layer.hillshadeShadowColor, defaultExpression,
+ @"hillshadeShadowColor should return the default value after being unset.");
+
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.hillshadeShadowColor = functionExpression, NSException, NSInvalidArgumentException, @"MGLHillshadeLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.hillshadeShadowColor = functionExpression, NSException, NSInvalidArgumentException, @"MGLHillshadeLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ // Transition property test
+ layer.hillshadeShadowColorTransition = transitionTest;
+ auto toptions = rawLayer->getHillshadeShadowColorTransition();
+ XCTAssert(toptions.delay && MGLTimeIntervalFromDuration(*toptions.delay) == transitionTest.delay);
+ XCTAssert(toptions.duration && MGLTimeIntervalFromDuration(*toptions.duration) == transitionTest.duration);
+
+ MGLTransition hillshadeShadowColorTransition = layer.hillshadeShadowColorTransition;
+ XCTAssertEqual(hillshadeShadowColorTransition.delay, transitionTest.delay);
+ XCTAssertEqual(hillshadeShadowColorTransition.duration, transitionTest.duration);
+ }
+}
+
+- (void)testPropertyNames {
+ [self testPropertyName:@"hillshade-accent-color" isBoolean:NO];
+ [self testPropertyName:@"hillshade-exaggeration" isBoolean:NO];
+ [self testPropertyName:@"hillshade-highlight-color" isBoolean:NO];
+ [self testPropertyName:@"hillshade-illumination-anchor" isBoolean:NO];
+ [self testPropertyName:@"hillshade-illumination-direction" isBoolean:NO];
+ [self testPropertyName:@"hillshade-shadow-color" isBoolean:NO];
+}
+
+- (void)testValueAdditions {
+ XCTAssertEqual([NSValue valueWithMGLHillshadeIlluminationAnchor:MGLHillshadeIlluminationAnchorMap].MGLHillshadeIlluminationAnchorValue, MGLHillshadeIlluminationAnchorMap);
+ XCTAssertEqual([NSValue valueWithMGLHillshadeIlluminationAnchor:MGLHillshadeIlluminationAnchorViewport].MGLHillshadeIlluminationAnchorValue, MGLHillshadeIlluminationAnchorViewport);
+}
+
+@end
diff --git a/platform/darwin/test/MGLLightTest.mm b/platform/darwin/test/MGLLightTest.mm
index de64d57851..a51c59c725 100644
--- a/platform/darwin/test/MGLLightTest.mm
+++ b/platform/darwin/test/MGLLightTest.mm
@@ -27,29 +27,28 @@
{
mbgl::style::Light light;
MGLLight *mglLight = [[MGLLight alloc] initWithMBGLLight:&light];
- auto lightFromMGLlight = [mglLight mbglLight];
+ auto lightFromMGLLight = mglLight.mbglLight;
- XCTAssertEqual(light.getDefaultAnchor(), lightFromMGLlight.getAnchor());
- XCTAssert([mglLight.anchor isKindOfClass:[MGLConstantStyleValue class]], @"mglLight.anchor isn’t a MGLConstantStyleValue.");
- NSValue *anchorValue = ((MGLConstantStyleValue *)mglLight.anchor).rawValue;
- XCTAssertEqual(anchorValue.MGLLightAnchorValue, MGLLightAnchorViewport);
+ XCTAssertEqual(light.getDefaultAnchor(), lightFromMGLLight.getAnchor());
+ XCTAssertEqual(mglLight.anchor.expressionType, NSConstantValueExpressionType, @"mglLight.anchor isn’t a constant value expression.");
+ XCTAssertEqualObjects(mglLight.anchor.constantValue, @"viewport");
mbgl::style::PropertyValue<mbgl::style::LightAnchorType> propertyValue = { mbgl::style::LightAnchorType::Viewport };
light.setAnchor(propertyValue);
mglLight = [[MGLLight alloc] initWithMBGLLight:&light];
- lightFromMGLlight = [mglLight mbglLight];
+ lightFromMGLLight = mglLight.mbglLight;
- XCTAssertEqual(light.getAnchor(), lightFromMGLlight.getAnchor());
+ XCTAssertEqual(light.getAnchor(), lightFromMGLLight.getAnchor());
}
// position
{
mbgl::style::Light light;
MGLLight *mglLight = [[MGLLight alloc] initWithMBGLLight:&light];
- auto lightFromMGLlight = [mglLight mbglLight];
+ auto lightFromMGLLight = mglLight.mbglLight;
- XCTAssertEqual(light.getDefaultPosition(), lightFromMGLlight.getPosition());
- auto positionTransition = lightFromMGLlight.getPositionTransition();
+ XCTAssertEqual(light.getDefaultPosition(), lightFromMGLLight.getPosition());
+ auto positionTransition = lightFromMGLLight.getPositionTransition();
XCTAssert(positionTransition.delay && MGLTimeIntervalFromDuration(*positionTransition.delay) == defaultTransition.delay);
XCTAssert(positionTransition.duration && MGLTimeIntervalFromDuration(*positionTransition.duration) == defaultTransition.duration);
@@ -60,10 +59,10 @@
light.setPositionTransition(transitionOptions);
mglLight = [[MGLLight alloc] initWithMBGLLight:&light];
- lightFromMGLlight = [mglLight mbglLight];
+ lightFromMGLLight = mglLight.mbglLight;
- XCTAssertEqual(light.getPosition(), lightFromMGLlight.getPosition());
- positionTransition = lightFromMGLlight.getPositionTransition();
+ XCTAssertEqual(light.getPosition(), lightFromMGLLight.getPosition());
+ positionTransition = lightFromMGLLight.getPositionTransition();
XCTAssert(positionTransition.delay && MGLTimeIntervalFromDuration(*positionTransition.delay) == transition.delay);
XCTAssert(positionTransition.duration && MGLTimeIntervalFromDuration(*positionTransition.duration) == transition.duration);
@@ -73,10 +72,10 @@
{
mbgl::style::Light light;
MGLLight *mglLight = [[MGLLight alloc] initWithMBGLLight:&light];
- auto lightFromMGLlight = [mglLight mbglLight];
+ auto lightFromMGLLight = mglLight.mbglLight;
- XCTAssertEqual(light.getDefaultColor(), lightFromMGLlight.getColor());
- auto colorTransition = lightFromMGLlight.getColorTransition();
+ XCTAssertEqual(light.getDefaultColor(), lightFromMGLLight.getColor());
+ auto colorTransition = lightFromMGLLight.getColorTransition();
XCTAssert(colorTransition.delay && MGLTimeIntervalFromDuration(*colorTransition.delay) == defaultTransition.delay);
XCTAssert(colorTransition.duration && MGLTimeIntervalFromDuration(*colorTransition.duration) == defaultTransition.duration);
@@ -85,10 +84,10 @@
light.setColorTransition(transitionOptions);
mglLight = [[MGLLight alloc] initWithMBGLLight:&light];
- lightFromMGLlight = [mglLight mbglLight];
+ lightFromMGLLight = mglLight.mbglLight;
- XCTAssertEqual(light.getColor(), lightFromMGLlight.getColor());
- colorTransition = lightFromMGLlight.getColorTransition();
+ XCTAssertEqual(light.getColor(), lightFromMGLLight.getColor());
+ colorTransition = lightFromMGLLight.getColorTransition();
XCTAssert(colorTransition.delay && MGLTimeIntervalFromDuration(*colorTransition.delay) == transition.delay);
XCTAssert(colorTransition.duration && MGLTimeIntervalFromDuration(*colorTransition.duration) == transition.duration);
@@ -98,10 +97,10 @@
{
mbgl::style::Light light;
MGLLight *mglLight = [[MGLLight alloc] initWithMBGLLight:&light];
- auto lightFromMGLlight = [mglLight mbglLight];
+ auto lightFromMGLLight = mglLight.mbglLight;
- XCTAssertEqual(light.getDefaultIntensity(), lightFromMGLlight.getIntensity());
- auto intensityTransition = lightFromMGLlight.getIntensityTransition();
+ XCTAssertEqual(light.getDefaultIntensity(), lightFromMGLLight.getIntensity());
+ auto intensityTransition = lightFromMGLLight.getIntensityTransition();
XCTAssert(intensityTransition.delay && MGLTimeIntervalFromDuration(*intensityTransition.delay) == defaultTransition.delay);
XCTAssert(intensityTransition.duration && MGLTimeIntervalFromDuration(*intensityTransition.duration) == defaultTransition.duration);
@@ -110,10 +109,10 @@
light.setIntensityTransition(transitionOptions);
mglLight = [[MGLLight alloc] initWithMBGLLight:&light];
- lightFromMGLlight = [mglLight mbglLight];
+ lightFromMGLLight = mglLight.mbglLight;
- XCTAssertEqual(light.getIntensity(), lightFromMGLlight.getIntensity());
- intensityTransition = lightFromMGLlight.getIntensityTransition();
+ XCTAssertEqual(light.getIntensity(), lightFromMGLLight.getIntensity());
+ intensityTransition = lightFromMGLLight.getIntensityTransition();
XCTAssert(intensityTransition.delay && MGLTimeIntervalFromDuration(*intensityTransition.delay) == transition.delay);
XCTAssert(intensityTransition.duration && MGLTimeIntervalFromDuration(*intensityTransition.duration) == transition.duration);
diff --git a/platform/darwin/test/MGLLightTest.mm.ejs b/platform/darwin/test/MGLLightTest.mm.ejs
index 5b1f27d8d1..35ff58b6d5 100644
--- a/platform/darwin/test/MGLLightTest.mm.ejs
+++ b/platform/darwin/test/MGLLightTest.mm.ejs
@@ -32,19 +32,18 @@
{
mbgl::style::Light light;
MGLLight *mglLight = [[MGLLight alloc] initWithMBGLLight:&light];
- auto lightFromMGLlight = [mglLight mbglLight];
+ auto lightFromMGLLight = mglLight.mbglLight;
- XCTAssertEqual(light.getDefault<%- camelize(property.name) -%>(), lightFromMGLlight.get<%- camelize(property.name) -%>());
+ XCTAssertEqual(light.getDefault<%- camelize(property.name) -%>(), lightFromMGLLight.get<%- camelize(property.name) -%>());
<% if (property.transition) { -%>
- auto <%- camelizeWithLeadingLowercase(property.name) -%>Transition = lightFromMGLlight.get<%- camelize(property.name) -%>Transition();
+ auto <%- camelizeWithLeadingLowercase(property.name) -%>Transition = lightFromMGLLight.get<%- camelize(property.name) -%>Transition();
XCTAssert(<%- camelizeWithLeadingLowercase(property.name) -%>Transition.delay && MGLTimeIntervalFromDuration(*<%- camelizeWithLeadingLowercase(property.name) -%>Transition.delay) == defaultTransition.delay);
XCTAssert(<%- camelizeWithLeadingLowercase(property.name) -%>Transition.duration && MGLTimeIntervalFromDuration(*<%- camelizeWithLeadingLowercase(property.name) -%>Transition.duration) == defaultTransition.duration);
<% } -%>
<% if (property.type == "enum" && property.default) { -%>
- XCTAssert([mglLight.<%- camelizeWithLeadingLowercase(property.name) -%> isKindOfClass:[MGLConstantStyleValue class]], @"mglLight.<%- camelizeWithLeadingLowercase(property.name) -%> isn’t a MGLConstantStyleValue.");
- NSValue *<%- camelizeWithLeadingLowercase(property.name) -%>Value = ((MGLConstantStyleValue *)mglLight.<%- camelizeWithLeadingLowercase(property.name) -%>).rawValue;
- XCTAssertEqual(<%- camelizeWithLeadingLowercase(property.name) -%>Value.MGLLight<%- camelize(property.name) -%>Value, MGLLight<%- camelize(property.name) -%><%- camelize(property.default) -%>);
+ XCTAssertEqual(mglLight.<%- camelizeWithLeadingLowercase(property.name) -%>.expressionType, NSConstantValueExpressionType, @"mglLight.<%- camelizeWithLeadingLowercase(property.name) -%> isn’t a constant value expression.");
+ XCTAssertEqualObjects(mglLight.<%- camelizeWithLeadingLowercase(property.name) -%>.constantValue, @"<%- property.default -%>");
<% } -%>
<% if (property.type == "array") { -%>
@@ -60,11 +59,11 @@
<% } -%>
mglLight = [[MGLLight alloc] initWithMBGLLight:&light];
- lightFromMGLlight = [mglLight mbglLight];
+ lightFromMGLLight = mglLight.mbglLight;
- XCTAssertEqual(light.get<%- camelize(property.name) -%>(), lightFromMGLlight.get<%- camelize(property.name) -%>());
+ XCTAssertEqual(light.get<%- camelize(property.name) -%>(), lightFromMGLLight.get<%- camelize(property.name) -%>());
<% if (property.transition) { -%>
- <%- camelizeWithLeadingLowercase(property.name) -%>Transition = lightFromMGLlight.get<%- camelize(property.name) -%>Transition();
+ <%- camelizeWithLeadingLowercase(property.name) -%>Transition = lightFromMGLLight.get<%- camelize(property.name) -%>Transition();
XCTAssert(<%- camelizeWithLeadingLowercase(property.name) -%>Transition.delay && MGLTimeIntervalFromDuration(*<%- camelizeWithLeadingLowercase(property.name) -%>Transition.delay) == transition.delay);
XCTAssert(<%- camelizeWithLeadingLowercase(property.name) -%>Transition.duration && MGLTimeIntervalFromDuration(*<%- camelizeWithLeadingLowercase(property.name) -%>Transition.duration) == transition.duration);
diff --git a/platform/darwin/test/MGLLineStyleLayerTests.mm b/platform/darwin/test/MGLLineStyleLayerTests.mm
index 7e7926e22e..bf98e98320 100644
--- a/platform/darwin/test/MGLLineStyleLayerTests.mm
+++ b/platform/darwin/test/MGLLineStyleLayerTests.mm
@@ -52,72 +52,81 @@
{
XCTAssertTrue(rawLayer->getLineCap().isUndefined(),
@"line-cap should be unset initially.");
- MGLStyleValue<NSValue *> *defaultStyleValue = layer.lineCap;
+ NSExpression *defaultExpression = layer.lineCap;
- MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLLineCap:MGLLineCapSquare]];
- layer.lineCap = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'square'"];
+ layer.lineCap = constantExpression;
mbgl::style::PropertyValue<mbgl::style::LineCapType> propertyValue = { mbgl::style::LineCapType::Square };
XCTAssertEqual(rawLayer->getLineCap(), propertyValue,
- @"Setting lineCap to a constant value should update line-cap.");
- XCTAssertEqualObjects(layer.lineCap, constantStyleValue,
- @"lineCap should round-trip constant values.");
-
- MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.lineCap = functionStyleValue;
-
- mbgl::style::IntervalStops<mbgl::style::LineCapType> intervalStops = { {{18, mbgl::style::LineCapType::Square}} };
+ @"Setting lineCap to a constant value expression should update line-cap.");
+ XCTAssertEqualObjects(layer.lineCap, constantExpression,
+ @"lineCap should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"'square'"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.lineCap = functionExpression;
+
+ mbgl::style::IntervalStops<mbgl::style::LineCapType> intervalStops = {{
+ { -INFINITY, mbgl::style::LineCapType::Square },
+ { 18, mbgl::style::LineCapType::Square },
+ }};
propertyValue = mbgl::style::CameraFunction<mbgl::style::LineCapType> { intervalStops };
XCTAssertEqual(rawLayer->getLineCap(), propertyValue,
- @"Setting lineCap to a camera function should update line-cap.");
- XCTAssertEqualObjects(layer.lineCap, functionStyleValue,
- @"lineCap should round-trip camera functions.");
+ @"Setting lineCap to a camera expression should update line-cap.");
+ XCTAssertEqualObjects(layer.lineCap, functionExpression,
+ @"lineCap should round-trip camera expressions.");
layer.lineCap = nil;
XCTAssertTrue(rawLayer->getLineCap().isUndefined(),
@"Unsetting lineCap should return line-cap to the default value.");
- XCTAssertEqualObjects(layer.lineCap, defaultStyleValue,
+ XCTAssertEqualObjects(layer.lineCap, defaultExpression,
@"lineCap should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.lineCap = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.lineCap = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.lineCap = functionExpression, NSException, NSInvalidArgumentException, @"MGLLineLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.lineCap = functionExpression, NSException, NSInvalidArgumentException, @"MGLLineLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// line-join
{
XCTAssertTrue(rawLayer->getLineJoin().isUndefined(),
@"line-join should be unset initially.");
- MGLStyleValue<NSValue *> *defaultStyleValue = layer.lineJoin;
+ NSExpression *defaultExpression = layer.lineJoin;
- MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLLineJoin:MGLLineJoinMiter]];
- layer.lineJoin = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'miter'"];
+ layer.lineJoin = constantExpression;
mbgl::style::DataDrivenPropertyValue<mbgl::style::LineJoinType> propertyValue = { mbgl::style::LineJoinType::Miter };
XCTAssertEqual(rawLayer->getLineJoin(), propertyValue,
- @"Setting lineJoin to a constant value should update line-join.");
- XCTAssertEqualObjects(layer.lineJoin, constantStyleValue,
- @"lineJoin should round-trip constant values.");
-
- MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.lineJoin = functionStyleValue;
-
- mbgl::style::IntervalStops<mbgl::style::LineJoinType> intervalStops = { {{18, mbgl::style::LineJoinType::Miter}} };
+ @"Setting lineJoin to a constant value expression should update line-join.");
+ XCTAssertEqualObjects(layer.lineJoin, constantExpression,
+ @"lineJoin should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"'miter'"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.lineJoin = functionExpression;
+
+ mbgl::style::IntervalStops<mbgl::style::LineJoinType> intervalStops = {{
+ { -INFINITY, mbgl::style::LineJoinType::Miter },
+ { 18, mbgl::style::LineJoinType::Miter },
+ }};
propertyValue = mbgl::style::CameraFunction<mbgl::style::LineJoinType> { intervalStops };
XCTAssertEqual(rawLayer->getLineJoin(), propertyValue,
- @"Setting lineJoin to a camera function should update line-join.");
- XCTAssertEqualObjects(layer.lineJoin, functionStyleValue,
- @"lineJoin should round-trip camera functions.");
+ @"Setting lineJoin to a camera expression should update line-join.");
+ XCTAssertEqualObjects(layer.lineJoin, functionExpression,
+ @"lineJoin should round-trip camera expressions.");
layer.lineJoin = nil;
XCTAssertTrue(rawLayer->getLineJoin().isUndefined(),
@"Unsetting lineJoin should return line-join to the default value.");
- XCTAssertEqualObjects(layer.lineJoin, defaultStyleValue,
+ XCTAssertEqualObjects(layer.lineJoin, defaultExpression,
@"lineJoin should return the default value after being unset.");
}
@@ -125,118 +134,132 @@
{
XCTAssertTrue(rawLayer->getLineMiterLimit().isUndefined(),
@"line-miter-limit should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.lineMiterLimit;
+ NSExpression *defaultExpression = layer.lineMiterLimit;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.lineMiterLimit = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.lineMiterLimit = constantExpression;
mbgl::style::PropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getLineMiterLimit(), propertyValue,
- @"Setting lineMiterLimit to a constant value should update line-miter-limit.");
- XCTAssertEqualObjects(layer.lineMiterLimit, constantStyleValue,
- @"lineMiterLimit should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.lineMiterLimit = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting lineMiterLimit to a constant value expression should update line-miter-limit.");
+ XCTAssertEqualObjects(layer.lineMiterLimit, constantExpression,
+ @"lineMiterLimit should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.lineMiterLimit = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getLineMiterLimit(), propertyValue,
- @"Setting lineMiterLimit to a camera function should update line-miter-limit.");
- XCTAssertEqualObjects(layer.lineMiterLimit, functionStyleValue,
- @"lineMiterLimit should round-trip camera functions.");
+ @"Setting lineMiterLimit to a camera expression should update line-miter-limit.");
+ XCTAssertEqualObjects(layer.lineMiterLimit, functionExpression,
+ @"lineMiterLimit should round-trip camera expressions.");
layer.lineMiterLimit = nil;
XCTAssertTrue(rawLayer->getLineMiterLimit().isUndefined(),
@"Unsetting lineMiterLimit should return line-miter-limit to the default value.");
- XCTAssertEqualObjects(layer.lineMiterLimit, defaultStyleValue,
+ XCTAssertEqualObjects(layer.lineMiterLimit, defaultExpression,
@"lineMiterLimit should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.lineMiterLimit = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.lineMiterLimit = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.lineMiterLimit = functionExpression, NSException, NSInvalidArgumentException, @"MGLLineLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.lineMiterLimit = functionExpression, NSException, NSInvalidArgumentException, @"MGLLineLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// line-round-limit
{
XCTAssertTrue(rawLayer->getLineRoundLimit().isUndefined(),
@"line-round-limit should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.lineRoundLimit;
+ NSExpression *defaultExpression = layer.lineRoundLimit;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.lineRoundLimit = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.lineRoundLimit = constantExpression;
mbgl::style::PropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getLineRoundLimit(), propertyValue,
- @"Setting lineRoundLimit to a constant value should update line-round-limit.");
- XCTAssertEqualObjects(layer.lineRoundLimit, constantStyleValue,
- @"lineRoundLimit should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.lineRoundLimit = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting lineRoundLimit to a constant value expression should update line-round-limit.");
+ XCTAssertEqualObjects(layer.lineRoundLimit, constantExpression,
+ @"lineRoundLimit should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.lineRoundLimit = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getLineRoundLimit(), propertyValue,
- @"Setting lineRoundLimit to a camera function should update line-round-limit.");
- XCTAssertEqualObjects(layer.lineRoundLimit, functionStyleValue,
- @"lineRoundLimit should round-trip camera functions.");
+ @"Setting lineRoundLimit to a camera expression should update line-round-limit.");
+ XCTAssertEqualObjects(layer.lineRoundLimit, functionExpression,
+ @"lineRoundLimit should round-trip camera expressions.");
layer.lineRoundLimit = nil;
XCTAssertTrue(rawLayer->getLineRoundLimit().isUndefined(),
@"Unsetting lineRoundLimit should return line-round-limit to the default value.");
- XCTAssertEqualObjects(layer.lineRoundLimit, defaultStyleValue,
+ XCTAssertEqualObjects(layer.lineRoundLimit, defaultExpression,
@"lineRoundLimit should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.lineRoundLimit = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.lineRoundLimit = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.lineRoundLimit = functionExpression, NSException, NSInvalidArgumentException, @"MGLLineLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.lineRoundLimit = functionExpression, NSException, NSInvalidArgumentException, @"MGLLineLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// line-blur
{
XCTAssertTrue(rawLayer->getLineBlur().isUndefined(),
@"line-blur should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.lineBlur;
+ NSExpression *defaultExpression = layer.lineBlur;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.lineBlur = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.lineBlur = constantExpression;
mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getLineBlur(), propertyValue,
- @"Setting lineBlur to a constant value should update line-blur.");
- XCTAssertEqualObjects(layer.lineBlur, constantStyleValue,
- @"lineBlur should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.lineBlur = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting lineBlur to a constant value expression should update line-blur.");
+ XCTAssertEqualObjects(layer.lineBlur, constantExpression,
+ @"lineBlur should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.lineBlur = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getLineBlur(), propertyValue,
- @"Setting lineBlur to a camera function should update line-blur.");
- XCTAssertEqualObjects(layer.lineBlur, functionStyleValue,
- @"lineBlur should round-trip camera functions.");
+ @"Setting lineBlur to a camera expression should update line-blur.");
+ XCTAssertEqualObjects(layer.lineBlur, functionExpression,
+ @"lineBlur should round-trip camera expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
- layer.lineBlur = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.lineBlur = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getLineBlur(), propertyValue,
- @"Setting lineBlur to a source function should update line-blur.");
- XCTAssertEqualObjects(layer.lineBlur, functionStyleValue,
- @"lineBlur should round-trip source functions.");
+ @"Setting lineBlur to a data expression should update line-blur.");
+ XCTAssertEqualObjects(layer.lineBlur, functionExpression,
+ @"lineBlur should round-trip data expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
- layer.lineBlur = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.lineBlur = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 };
@@ -244,15 +267,15 @@
propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getLineBlur(), propertyValue,
- @"Setting lineBlur to a composite function should update line-blur.");
- XCTAssertEqualObjects(layer.lineBlur, functionStyleValue,
- @"lineBlur should round-trip composite functions.");
+ @"Setting lineBlur to a camera-data expression should update line-blur.");
+ XCTAssertEqualObjects(layer.lineBlur, functionExpression,
+ @"lineBlur should round-trip camera-data expressions.");
layer.lineBlur = nil;
XCTAssertTrue(rawLayer->getLineBlur().isUndefined(),
@"Unsetting lineBlur should return line-blur to the default value.");
- XCTAssertEqualObjects(layer.lineBlur, defaultStyleValue,
+ XCTAssertEqualObjects(layer.lineBlur, defaultExpression,
@"lineBlur should return the default value after being unset.");
// Transition property test
layer.lineBlurTransition = transitionTest;
@@ -269,40 +292,44 @@
{
XCTAssertTrue(rawLayer->getLineColor().isUndefined(),
@"line-color should be unset initially.");
- MGLStyleValue<MGLColor *> *defaultStyleValue = layer.lineColor;
+ NSExpression *defaultExpression = layer.lineColor;
- MGLStyleValue<MGLColor *> *constantStyleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]];
- layer.lineColor = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
+ layer.lineColor = constantExpression;
mbgl::style::DataDrivenPropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } };
XCTAssertEqual(rawLayer->getLineColor(), propertyValue,
- @"Setting lineColor to a constant value should update line-color.");
- XCTAssertEqualObjects(layer.lineColor, constantStyleValue,
- @"lineColor should round-trip constant values.");
-
- MGLStyleValue<MGLColor *> * functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.lineColor = functionStyleValue;
-
- mbgl::style::IntervalStops<mbgl::Color> intervalStops = { {{18, { 1, 0, 0, 1 }}} };
+ @"Setting lineColor to a constant value expression should update line-color.");
+ XCTAssertEqualObjects(layer.lineColor, constantExpression,
+ @"lineColor should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.lineColor = functionExpression;
+
+ mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{
+ { -INFINITY, { 1, 0, 0, 1 } },
+ { 18, { 1, 0, 0, 1 } },
+ }};
propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops };
XCTAssertEqual(rawLayer->getLineColor(), propertyValue,
- @"Setting lineColor to a camera function should update line-color.");
- XCTAssertEqualObjects(layer.lineColor, functionStyleValue,
- @"lineColor should round-trip camera functions.");
+ @"Setting lineColor to a camera expression should update line-color.");
+ XCTAssertEqualObjects(layer.lineColor, functionExpression,
+ @"lineColor should round-trip camera expressions.");
- functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
- layer.lineColor = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.lineColor = functionExpression;
mbgl::style::ExponentialStops<mbgl::Color> exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<mbgl::Color> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getLineColor(), propertyValue,
- @"Setting lineColor to a source function should update line-color.");
- XCTAssertEqualObjects(layer.lineColor, functionStyleValue,
- @"lineColor should round-trip source functions.");
+ @"Setting lineColor to a data expression should update line-color.");
+ XCTAssertEqualObjects(layer.lineColor, functionExpression,
+ @"lineColor should round-trip data expressions.");
- functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
- layer.lineColor = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.lineColor = functionExpression;
std::map<float, mbgl::Color> innerStops { {18, { 1, 0, 0, 1 }} };
mbgl::style::CompositeExponentialStops<mbgl::Color> compositeStops { { {10.0, innerStops} }, 1.0 };
@@ -310,15 +337,15 @@
propertyValue = mbgl::style::CompositeFunction<mbgl::Color> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getLineColor(), propertyValue,
- @"Setting lineColor to a composite function should update line-color.");
- XCTAssertEqualObjects(layer.lineColor, functionStyleValue,
- @"lineColor should round-trip composite functions.");
+ @"Setting lineColor to a camera-data expression should update line-color.");
+ XCTAssertEqualObjects(layer.lineColor, functionExpression,
+ @"lineColor should round-trip camera-data expressions.");
layer.lineColor = nil;
XCTAssertTrue(rawLayer->getLineColor().isUndefined(),
@"Unsetting lineColor should return line-color to the default value.");
- XCTAssertEqualObjects(layer.lineColor, defaultStyleValue,
+ XCTAssertEqualObjects(layer.lineColor, defaultExpression,
@"lineColor should return the default value after being unset.");
// Transition property test
layer.lineColorTransition = transitionTest;
@@ -335,79 +362,88 @@
{
XCTAssertTrue(rawLayer->getLineDasharray().isUndefined(),
@"line-dasharray should be unset initially.");
- MGLStyleValue<NSArray<NSNumber *> *> *defaultStyleValue = layer.lineDashPattern;
+ NSExpression *defaultExpression = layer.lineDashPattern;
- MGLStyleValue<NSArray<NSNumber *> *> *constantStyleValue = [MGLStyleValue<NSArray<NSNumber *> *> valueWithRawValue:@[@1, @2]];
- layer.lineDashPattern = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"{1, 2}"];
+ layer.lineDashPattern = constantExpression;
mbgl::style::PropertyValue<std::vector<float>> propertyValue = { {1, 2} };
XCTAssertEqual(rawLayer->getLineDasharray(), propertyValue,
- @"Setting lineDashPattern to a constant value should update line-dasharray.");
- XCTAssertEqualObjects(layer.lineDashPattern, constantStyleValue,
- @"lineDashPattern should round-trip constant values.");
-
- MGLStyleValue<NSArray<NSNumber *> *> * functionStyleValue = [MGLStyleValue<NSArray<NSNumber *> *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.lineDashPattern = functionStyleValue;
-
- mbgl::style::IntervalStops<std::vector<float>> intervalStops = { {{18, {1, 2}}} };
+ @"Setting lineDashPattern to a constant value expression should update line-dasharray.");
+ XCTAssertEqualObjects(layer.lineDashPattern, constantExpression,
+ @"lineDashPattern should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"{1, 2}"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.lineDashPattern = functionExpression;
+
+ mbgl::style::IntervalStops<std::vector<float>> intervalStops = {{
+ { -INFINITY, {1, 2} },
+ { 18, {1, 2} },
+ }};
propertyValue = mbgl::style::CameraFunction<std::vector<float>> { intervalStops };
XCTAssertEqual(rawLayer->getLineDasharray(), propertyValue,
- @"Setting lineDashPattern to a camera function should update line-dasharray.");
- XCTAssertEqualObjects(layer.lineDashPattern, functionStyleValue,
- @"lineDashPattern should round-trip camera functions.");
+ @"Setting lineDashPattern to a camera expression should update line-dasharray.");
+ XCTAssertEqualObjects(layer.lineDashPattern, functionExpression,
+ @"lineDashPattern should round-trip camera expressions.");
layer.lineDashPattern = nil;
XCTAssertTrue(rawLayer->getLineDasharray().isUndefined(),
@"Unsetting lineDashPattern should return line-dasharray to the default value.");
- XCTAssertEqualObjects(layer.lineDashPattern, defaultStyleValue,
+ XCTAssertEqualObjects(layer.lineDashPattern, defaultExpression,
@"lineDashPattern should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSArray<NSNumber *> *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.lineDashPattern = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSArray<NSNumber *> *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.lineDashPattern = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.lineDashPattern = functionExpression, NSException, NSInvalidArgumentException, @"MGLLineLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.lineDashPattern = functionExpression, NSException, NSInvalidArgumentException, @"MGLLineLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// line-gap-width
{
XCTAssertTrue(rawLayer->getLineGapWidth().isUndefined(),
@"line-gap-width should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.lineGapWidth;
+ NSExpression *defaultExpression = layer.lineGapWidth;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.lineGapWidth = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.lineGapWidth = constantExpression;
mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getLineGapWidth(), propertyValue,
- @"Setting lineGapWidth to a constant value should update line-gap-width.");
- XCTAssertEqualObjects(layer.lineGapWidth, constantStyleValue,
- @"lineGapWidth should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.lineGapWidth = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting lineGapWidth to a constant value expression should update line-gap-width.");
+ XCTAssertEqualObjects(layer.lineGapWidth, constantExpression,
+ @"lineGapWidth should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.lineGapWidth = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getLineGapWidth(), propertyValue,
- @"Setting lineGapWidth to a camera function should update line-gap-width.");
- XCTAssertEqualObjects(layer.lineGapWidth, functionStyleValue,
- @"lineGapWidth should round-trip camera functions.");
+ @"Setting lineGapWidth to a camera expression should update line-gap-width.");
+ XCTAssertEqualObjects(layer.lineGapWidth, functionExpression,
+ @"lineGapWidth should round-trip camera expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
- layer.lineGapWidth = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.lineGapWidth = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getLineGapWidth(), propertyValue,
- @"Setting lineGapWidth to a source function should update line-gap-width.");
- XCTAssertEqualObjects(layer.lineGapWidth, functionStyleValue,
- @"lineGapWidth should round-trip source functions.");
+ @"Setting lineGapWidth to a data expression should update line-gap-width.");
+ XCTAssertEqualObjects(layer.lineGapWidth, functionExpression,
+ @"lineGapWidth should round-trip data expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
- layer.lineGapWidth = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.lineGapWidth = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 };
@@ -415,15 +451,15 @@
propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getLineGapWidth(), propertyValue,
- @"Setting lineGapWidth to a composite function should update line-gap-width.");
- XCTAssertEqualObjects(layer.lineGapWidth, functionStyleValue,
- @"lineGapWidth should round-trip composite functions.");
+ @"Setting lineGapWidth to a camera-data expression should update line-gap-width.");
+ XCTAssertEqualObjects(layer.lineGapWidth, functionExpression,
+ @"lineGapWidth should round-trip camera-data expressions.");
layer.lineGapWidth = nil;
XCTAssertTrue(rawLayer->getLineGapWidth().isUndefined(),
@"Unsetting lineGapWidth should return line-gap-width to the default value.");
- XCTAssertEqualObjects(layer.lineGapWidth, defaultStyleValue,
+ XCTAssertEqualObjects(layer.lineGapWidth, defaultExpression,
@"lineGapWidth should return the default value after being unset.");
// Transition property test
layer.lineGapWidthTransition = transitionTest;
@@ -440,40 +476,44 @@
{
XCTAssertTrue(rawLayer->getLineOffset().isUndefined(),
@"line-offset should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.lineOffset;
+ NSExpression *defaultExpression = layer.lineOffset;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.lineOffset = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.lineOffset = constantExpression;
mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getLineOffset(), propertyValue,
- @"Setting lineOffset to a constant value should update line-offset.");
- XCTAssertEqualObjects(layer.lineOffset, constantStyleValue,
- @"lineOffset should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.lineOffset = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting lineOffset to a constant value expression should update line-offset.");
+ XCTAssertEqualObjects(layer.lineOffset, constantExpression,
+ @"lineOffset should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.lineOffset = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getLineOffset(), propertyValue,
- @"Setting lineOffset to a camera function should update line-offset.");
- XCTAssertEqualObjects(layer.lineOffset, functionStyleValue,
- @"lineOffset should round-trip camera functions.");
+ @"Setting lineOffset to a camera expression should update line-offset.");
+ XCTAssertEqualObjects(layer.lineOffset, functionExpression,
+ @"lineOffset should round-trip camera expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
- layer.lineOffset = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.lineOffset = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getLineOffset(), propertyValue,
- @"Setting lineOffset to a source function should update line-offset.");
- XCTAssertEqualObjects(layer.lineOffset, functionStyleValue,
- @"lineOffset should round-trip source functions.");
+ @"Setting lineOffset to a data expression should update line-offset.");
+ XCTAssertEqualObjects(layer.lineOffset, functionExpression,
+ @"lineOffset should round-trip data expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
- layer.lineOffset = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.lineOffset = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 };
@@ -481,15 +521,15 @@
propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getLineOffset(), propertyValue,
- @"Setting lineOffset to a composite function should update line-offset.");
- XCTAssertEqualObjects(layer.lineOffset, functionStyleValue,
- @"lineOffset should round-trip composite functions.");
+ @"Setting lineOffset to a camera-data expression should update line-offset.");
+ XCTAssertEqualObjects(layer.lineOffset, functionExpression,
+ @"lineOffset should round-trip camera-data expressions.");
layer.lineOffset = nil;
XCTAssertTrue(rawLayer->getLineOffset().isUndefined(),
@"Unsetting lineOffset should return line-offset to the default value.");
- XCTAssertEqualObjects(layer.lineOffset, defaultStyleValue,
+ XCTAssertEqualObjects(layer.lineOffset, defaultExpression,
@"lineOffset should return the default value after being unset.");
// Transition property test
layer.lineOffsetTransition = transitionTest;
@@ -506,40 +546,44 @@
{
XCTAssertTrue(rawLayer->getLineOpacity().isUndefined(),
@"line-opacity should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.lineOpacity;
+ NSExpression *defaultExpression = layer.lineOpacity;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.lineOpacity = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.lineOpacity = constantExpression;
mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getLineOpacity(), propertyValue,
- @"Setting lineOpacity to a constant value should update line-opacity.");
- XCTAssertEqualObjects(layer.lineOpacity, constantStyleValue,
- @"lineOpacity should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.lineOpacity = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting lineOpacity to a constant value expression should update line-opacity.");
+ XCTAssertEqualObjects(layer.lineOpacity, constantExpression,
+ @"lineOpacity should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.lineOpacity = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getLineOpacity(), propertyValue,
- @"Setting lineOpacity to a camera function should update line-opacity.");
- XCTAssertEqualObjects(layer.lineOpacity, functionStyleValue,
- @"lineOpacity should round-trip camera functions.");
+ @"Setting lineOpacity to a camera expression should update line-opacity.");
+ XCTAssertEqualObjects(layer.lineOpacity, functionExpression,
+ @"lineOpacity should round-trip camera expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
- layer.lineOpacity = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.lineOpacity = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getLineOpacity(), propertyValue,
- @"Setting lineOpacity to a source function should update line-opacity.");
- XCTAssertEqualObjects(layer.lineOpacity, functionStyleValue,
- @"lineOpacity should round-trip source functions.");
+ @"Setting lineOpacity to a data expression should update line-opacity.");
+ XCTAssertEqualObjects(layer.lineOpacity, functionExpression,
+ @"lineOpacity should round-trip data expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
- layer.lineOpacity = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.lineOpacity = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 };
@@ -547,15 +591,15 @@
propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getLineOpacity(), propertyValue,
- @"Setting lineOpacity to a composite function should update line-opacity.");
- XCTAssertEqualObjects(layer.lineOpacity, functionStyleValue,
- @"lineOpacity should round-trip composite functions.");
+ @"Setting lineOpacity to a camera-data expression should update line-opacity.");
+ XCTAssertEqualObjects(layer.lineOpacity, functionExpression,
+ @"lineOpacity should round-trip camera-data expressions.");
layer.lineOpacity = nil;
XCTAssertTrue(rawLayer->getLineOpacity().isUndefined(),
@"Unsetting lineOpacity should return line-opacity to the default value.");
- XCTAssertEqualObjects(layer.lineOpacity, defaultStyleValue,
+ XCTAssertEqualObjects(layer.lineOpacity, defaultExpression,
@"lineOpacity should return the default value after being unset.");
// Transition property test
layer.lineOpacityTransition = transitionTest;
@@ -572,39 +616,44 @@
{
XCTAssertTrue(rawLayer->getLinePattern().isUndefined(),
@"line-pattern should be unset initially.");
- MGLStyleValue<NSString *> *defaultStyleValue = layer.linePattern;
+ NSExpression *defaultExpression = layer.linePattern;
- MGLStyleValue<NSString *> *constantStyleValue = [MGLStyleValue<NSString *> valueWithRawValue:@"Line Pattern"];
- layer.linePattern = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'Line Pattern'"];
+ layer.linePattern = constantExpression;
mbgl::style::PropertyValue<std::string> propertyValue = { "Line Pattern" };
XCTAssertEqual(rawLayer->getLinePattern(), propertyValue,
- @"Setting linePattern to a constant value should update line-pattern.");
- XCTAssertEqualObjects(layer.linePattern, constantStyleValue,
- @"linePattern should round-trip constant values.");
-
- MGLStyleValue<NSString *> * functionStyleValue = [MGLStyleValue<NSString *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.linePattern = functionStyleValue;
-
- mbgl::style::IntervalStops<std::string> intervalStops = { {{18, "Line Pattern"}} };
+ @"Setting linePattern to a constant value expression should update line-pattern.");
+ XCTAssertEqualObjects(layer.linePattern, constantExpression,
+ @"linePattern should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"'Line Pattern'"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.linePattern = functionExpression;
+
+ mbgl::style::IntervalStops<std::string> intervalStops = {{
+ { -INFINITY, "Line Pattern" },
+ { 18, "Line Pattern" },
+ }};
propertyValue = mbgl::style::CameraFunction<std::string> { intervalStops };
XCTAssertEqual(rawLayer->getLinePattern(), propertyValue,
- @"Setting linePattern to a camera function should update line-pattern.");
- XCTAssertEqualObjects(layer.linePattern, functionStyleValue,
- @"linePattern should round-trip camera functions.");
+ @"Setting linePattern to a camera expression should update line-pattern.");
+ XCTAssertEqualObjects(layer.linePattern, functionExpression,
+ @"linePattern should round-trip camera expressions.");
layer.linePattern = nil;
XCTAssertTrue(rawLayer->getLinePattern().isUndefined(),
@"Unsetting linePattern should return line-pattern to the default value.");
- XCTAssertEqualObjects(layer.linePattern, defaultStyleValue,
+ XCTAssertEqualObjects(layer.linePattern, defaultExpression,
@"linePattern should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSString *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.linePattern = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSString *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.linePattern = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.linePattern = functionExpression, NSException, NSInvalidArgumentException, @"MGLLineLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.linePattern = functionExpression, NSException, NSInvalidArgumentException, @"MGLLineLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
// Transition property test
layer.linePatternTransition = transitionTest;
auto toptions = rawLayer->getLinePatternTransition();
@@ -620,124 +669,138 @@
{
XCTAssertTrue(rawLayer->getLineTranslate().isUndefined(),
@"line-translate should be unset initially.");
- MGLStyleValue<NSValue *> *defaultStyleValue = layer.lineTranslation;
+ NSExpression *defaultExpression = layer.lineTranslation;
- MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@",
#if TARGET_OS_IPHONE
[NSValue valueWithCGVector:CGVectorMake(1, 1)]
#else
[NSValue valueWithMGLVector:CGVectorMake(1, -1)]
#endif
];
- layer.lineTranslation = constantStyleValue;
+ layer.lineTranslation = constantExpression;
mbgl::style::PropertyValue<std::array<float, 2>> propertyValue = { { 1, 1 } };
XCTAssertEqual(rawLayer->getLineTranslate(), propertyValue,
- @"Setting lineTranslation to a constant value should update line-translate.");
- XCTAssertEqualObjects(layer.lineTranslation, constantStyleValue,
- @"lineTranslation should round-trip constant values.");
-
- MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.lineTranslation = functionStyleValue;
-
- mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = { {{18, { 1, 1 }}} };
+ @"Setting lineTranslation to a constant value expression should update line-translate.");
+ XCTAssertEqualObjects(layer.lineTranslation, constantExpression,
+ @"lineTranslation should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"{1, 1}"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.lineTranslation = functionExpression;
+
+ mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = {{
+ { -INFINITY, { 1, 1 } },
+ { 18, { 1, 1 } },
+ }};
propertyValue = mbgl::style::CameraFunction<std::array<float, 2>> { intervalStops };
XCTAssertEqual(rawLayer->getLineTranslate(), propertyValue,
- @"Setting lineTranslation to a camera function should update line-translate.");
- XCTAssertEqualObjects(layer.lineTranslation, functionStyleValue,
- @"lineTranslation should round-trip camera functions.");
+ @"Setting lineTranslation to a camera expression should update line-translate.");
+ XCTAssertEqualObjects(layer.lineTranslation, functionExpression,
+ @"lineTranslation should round-trip camera expressions.");
layer.lineTranslation = nil;
XCTAssertTrue(rawLayer->getLineTranslate().isUndefined(),
@"Unsetting lineTranslation should return line-translate to the default value.");
- XCTAssertEqualObjects(layer.lineTranslation, defaultStyleValue,
+ XCTAssertEqualObjects(layer.lineTranslation, defaultExpression,
@"lineTranslation should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.lineTranslation = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.lineTranslation = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.lineTranslation = functionExpression, NSException, NSInvalidArgumentException, @"MGLLineLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.lineTranslation = functionExpression, NSException, NSInvalidArgumentException, @"MGLLineLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// line-translate-anchor
{
XCTAssertTrue(rawLayer->getLineTranslateAnchor().isUndefined(),
@"line-translate-anchor should be unset initially.");
- MGLStyleValue<NSValue *> *defaultStyleValue = layer.lineTranslationAnchor;
+ NSExpression *defaultExpression = layer.lineTranslationAnchor;
- MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLLineTranslationAnchor:MGLLineTranslationAnchorViewport]];
- layer.lineTranslationAnchor = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'viewport'"];
+ layer.lineTranslationAnchor = constantExpression;
mbgl::style::PropertyValue<mbgl::style::TranslateAnchorType> propertyValue = { mbgl::style::TranslateAnchorType::Viewport };
XCTAssertEqual(rawLayer->getLineTranslateAnchor(), propertyValue,
- @"Setting lineTranslationAnchor to a constant value should update line-translate-anchor.");
- XCTAssertEqualObjects(layer.lineTranslationAnchor, constantStyleValue,
- @"lineTranslationAnchor should round-trip constant values.");
-
- MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.lineTranslationAnchor = functionStyleValue;
-
- mbgl::style::IntervalStops<mbgl::style::TranslateAnchorType> intervalStops = { {{18, mbgl::style::TranslateAnchorType::Viewport}} };
+ @"Setting lineTranslationAnchor to a constant value expression should update line-translate-anchor.");
+ XCTAssertEqualObjects(layer.lineTranslationAnchor, constantExpression,
+ @"lineTranslationAnchor should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"'viewport'"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.lineTranslationAnchor = functionExpression;
+
+ mbgl::style::IntervalStops<mbgl::style::TranslateAnchorType> intervalStops = {{
+ { -INFINITY, mbgl::style::TranslateAnchorType::Viewport },
+ { 18, mbgl::style::TranslateAnchorType::Viewport },
+ }};
propertyValue = mbgl::style::CameraFunction<mbgl::style::TranslateAnchorType> { intervalStops };
XCTAssertEqual(rawLayer->getLineTranslateAnchor(), propertyValue,
- @"Setting lineTranslationAnchor to a camera function should update line-translate-anchor.");
- XCTAssertEqualObjects(layer.lineTranslationAnchor, functionStyleValue,
- @"lineTranslationAnchor should round-trip camera functions.");
+ @"Setting lineTranslationAnchor to a camera expression should update line-translate-anchor.");
+ XCTAssertEqualObjects(layer.lineTranslationAnchor, functionExpression,
+ @"lineTranslationAnchor should round-trip camera expressions.");
layer.lineTranslationAnchor = nil;
XCTAssertTrue(rawLayer->getLineTranslateAnchor().isUndefined(),
@"Unsetting lineTranslationAnchor should return line-translate-anchor to the default value.");
- XCTAssertEqualObjects(layer.lineTranslationAnchor, defaultStyleValue,
+ XCTAssertEqualObjects(layer.lineTranslationAnchor, defaultExpression,
@"lineTranslationAnchor should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.lineTranslationAnchor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.lineTranslationAnchor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.lineTranslationAnchor = functionExpression, NSException, NSInvalidArgumentException, @"MGLLineLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.lineTranslationAnchor = functionExpression, NSException, NSInvalidArgumentException, @"MGLLineLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// line-width
{
XCTAssertTrue(rawLayer->getLineWidth().isUndefined(),
@"line-width should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.lineWidth;
+ NSExpression *defaultExpression = layer.lineWidth;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.lineWidth = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.lineWidth = constantExpression;
mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getLineWidth(), propertyValue,
- @"Setting lineWidth to a constant value should update line-width.");
- XCTAssertEqualObjects(layer.lineWidth, constantStyleValue,
- @"lineWidth should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.lineWidth = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting lineWidth to a constant value expression should update line-width.");
+ XCTAssertEqualObjects(layer.lineWidth, constantExpression,
+ @"lineWidth should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.lineWidth = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getLineWidth(), propertyValue,
- @"Setting lineWidth to a camera function should update line-width.");
- XCTAssertEqualObjects(layer.lineWidth, functionStyleValue,
- @"lineWidth should round-trip camera functions.");
+ @"Setting lineWidth to a camera expression should update line-width.");
+ XCTAssertEqualObjects(layer.lineWidth, functionExpression,
+ @"lineWidth should round-trip camera expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
- layer.lineWidth = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.lineWidth = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getLineWidth(), propertyValue,
- @"Setting lineWidth to a source function should update line-width.");
- XCTAssertEqualObjects(layer.lineWidth, functionStyleValue,
- @"lineWidth should round-trip source functions.");
+ @"Setting lineWidth to a data expression should update line-width.");
+ XCTAssertEqualObjects(layer.lineWidth, functionExpression,
+ @"lineWidth should round-trip data expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
- layer.lineWidth = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.lineWidth = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 };
@@ -745,15 +808,15 @@
propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getLineWidth(), propertyValue,
- @"Setting lineWidth to a composite function should update line-width.");
- XCTAssertEqualObjects(layer.lineWidth, functionStyleValue,
- @"lineWidth should round-trip composite functions.");
+ @"Setting lineWidth to a camera-data expression should update line-width.");
+ XCTAssertEqualObjects(layer.lineWidth, functionExpression,
+ @"lineWidth should round-trip camera-data expressions.");
layer.lineWidth = nil;
XCTAssertTrue(rawLayer->getLineWidth().isUndefined(),
@"Unsetting lineWidth should return line-width to the default value.");
- XCTAssertEqualObjects(layer.lineWidth, defaultStyleValue,
+ XCTAssertEqualObjects(layer.lineWidth, defaultExpression,
@"lineWidth should return the default value after being unset.");
// Transition property test
layer.lineWidthTransition = transitionTest;
diff --git a/platform/darwin/test/MGLNSStringAdditionsTests.m b/platform/darwin/test/MGLNSStringAdditionsTests.m
index 03503b7f8a..2a8715d991 100644
--- a/platform/darwin/test/MGLNSStringAdditionsTests.m
+++ b/platform/darwin/test/MGLNSStringAdditionsTests.m
@@ -39,4 +39,38 @@
XCTAssertEqualObjects([@"Improve This iPhone" mgl_titleCasedStringWithLocale:locale], @"Improve This iPhone");
}
+- (void)testTransliteratedString {
+ if (@available(iOS 9.0, *)) {
+ XCTAssertEqualObjects([@"Portland" mgl_stringByTransliteratingIntoScript:@"Latn"], @"Portland");
+ XCTAssertEqualObjects([@"Portland" mgl_stringByTransliteratingIntoScript:@"Hans"], @"Portland");
+ XCTAssertEqualObjects([@"Portland" mgl_stringByTransliteratingIntoScript:@"Cyrl"], @"Портланд");
+ XCTAssertEqualObjects([@"Portland" mgl_stringByTransliteratingIntoScript:@"Arab"], @"پُرتلَند");
+ XCTAssertEqualObjects([@"Portland" mgl_stringByTransliteratingIntoScript:@"Fake"], @"Portland");
+
+ XCTAssertEqualObjects([@"北京" mgl_stringByTransliteratingIntoScript:@"Latn"], @"běi jīng");
+ XCTAssertEqualObjects([@"北京" mgl_stringByTransliteratingIntoScript:@"Hans"], @"北京");
+ XCTAssertEqualObjects([@"北京" mgl_stringByTransliteratingIntoScript:@"Cyrl"], @"бе̌и йӣнг");
+ XCTAssertEqualObjects([@"北京" mgl_stringByTransliteratingIntoScript:@"Arab"], @"بِِ̌ جِينگ");
+ XCTAssertEqualObjects([@"北京" mgl_stringByTransliteratingIntoScript:@"Fake"], @"北京");
+
+ XCTAssertEqualObjects([@"Mосква" mgl_stringByTransliteratingIntoScript:@"Latn"], @"Moskva");
+ XCTAssertEqualObjects([@"Mосква" mgl_stringByTransliteratingIntoScript:@"Hans"], @"Mосква");
+ XCTAssertEqualObjects([@"Mосква" mgl_stringByTransliteratingIntoScript:@"Cyrl"], @"Москва");
+ XCTAssertEqualObjects([@"Mосква" mgl_stringByTransliteratingIntoScript:@"Arab"], @"مُسكڤَ");
+ XCTAssertEqualObjects([@"Mосква" mgl_stringByTransliteratingIntoScript:@"Fake"], @"Mосква");
+
+ XCTAssertEqualObjects([@"ロンドン" mgl_stringByTransliteratingIntoScript:@"Latn"], @"rondon");
+ XCTAssertEqualObjects([@"ロンドン" mgl_stringByTransliteratingIntoScript:@"Hans"], @"ロンドン");
+ XCTAssertEqualObjects([@"ロンドン" mgl_stringByTransliteratingIntoScript:@"Cyrl"], @"рондон");
+ XCTAssertEqualObjects([@"ロンドン" mgl_stringByTransliteratingIntoScript:@"Arab"], @"رُندُن");
+ XCTAssertEqualObjects([@"ロンドン" mgl_stringByTransliteratingIntoScript:@"Fake"], @"ロンドン");
+ } else {
+ XCTAssertEqualObjects([@"Made-up Place" mgl_stringByTransliteratingIntoScript:@"Latn"], @"Made-up Place");
+ XCTAssertEqualObjects([@"Made-up Place" mgl_stringByTransliteratingIntoScript:@"Hans"], @"Made-up Place");
+ XCTAssertEqualObjects([@"Made-up Place" mgl_stringByTransliteratingIntoScript:@"Cyrl"], @"Made-up Place");
+ XCTAssertEqualObjects([@"Made-up Place" mgl_stringByTransliteratingIntoScript:@"Arab"], @"Made-up Place");
+ XCTAssertEqualObjects([@"Made-up Place" mgl_stringByTransliteratingIntoScript:@"Fake"], @"Made-up Place");
+ }
+}
+
@end
diff --git a/platform/darwin/test/MGLPredicateTests.mm b/platform/darwin/test/MGLPredicateTests.mm
index 6e6951dcdd..d8cad0b166 100644
--- a/platform/darwin/test/MGLPredicateTests.mm
+++ b/platform/darwin/test/MGLPredicateTests.mm
@@ -571,4 +571,11 @@ namespace mbgl {
}
}
+- (void)testComparisonExpressionArray {
+ {
+ NSArray *expected = @[@"==", @1, @2];
+ XCTAssertEqualObjects([NSPredicate predicateWithFormat:@"1 = 2"].mgl_jsonExpressionObject, expected);
+ }
+}
+
@end
diff --git a/platform/darwin/test/MGLRasterStyleLayerTests.mm b/platform/darwin/test/MGLRasterStyleLayerTests.mm
index 7b0757eeb6..5ded23ee3c 100644
--- a/platform/darwin/test/MGLRasterStyleLayerTests.mm
+++ b/platform/darwin/test/MGLRasterStyleLayerTests.mm
@@ -34,117 +34,132 @@
{
XCTAssertTrue(rawLayer->getRasterBrightnessMax().isUndefined(),
@"raster-brightness-max should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.maximumRasterBrightness;
+ NSExpression *defaultExpression = layer.maximumRasterBrightness;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.maximumRasterBrightness = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.maximumRasterBrightness = constantExpression;
mbgl::style::PropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getRasterBrightnessMax(), propertyValue,
- @"Setting maximumRasterBrightness to a constant value should update raster-brightness-max.");
- XCTAssertEqualObjects(layer.maximumRasterBrightness, constantStyleValue,
- @"maximumRasterBrightness should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.maximumRasterBrightness = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting maximumRasterBrightness to a constant value expression should update raster-brightness-max.");
+ XCTAssertEqualObjects(layer.maximumRasterBrightness, constantExpression,
+ @"maximumRasterBrightness should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.maximumRasterBrightness = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getRasterBrightnessMax(), propertyValue,
- @"Setting maximumRasterBrightness to a camera function should update raster-brightness-max.");
- XCTAssertEqualObjects(layer.maximumRasterBrightness, functionStyleValue,
- @"maximumRasterBrightness should round-trip camera functions.");
+ @"Setting maximumRasterBrightness to a camera expression should update raster-brightness-max.");
+ XCTAssertEqualObjects(layer.maximumRasterBrightness, functionExpression,
+ @"maximumRasterBrightness should round-trip camera expressions.");
layer.maximumRasterBrightness = nil;
XCTAssertTrue(rawLayer->getRasterBrightnessMax().isUndefined(),
@"Unsetting maximumRasterBrightness should return raster-brightness-max to the default value.");
- XCTAssertEqualObjects(layer.maximumRasterBrightness, defaultStyleValue,
+ XCTAssertEqualObjects(layer.maximumRasterBrightness, defaultExpression,
@"maximumRasterBrightness should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.maximumRasterBrightness = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.maximumRasterBrightness = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.maximumRasterBrightness = functionExpression, NSException, NSInvalidArgumentException, @"MGLRasterLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.maximumRasterBrightness = functionExpression, NSException, NSInvalidArgumentException, @"MGLRasterLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// raster-brightness-min
{
XCTAssertTrue(rawLayer->getRasterBrightnessMin().isUndefined(),
@"raster-brightness-min should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.minimumRasterBrightness;
+ NSExpression *defaultExpression = layer.minimumRasterBrightness;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.minimumRasterBrightness = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.minimumRasterBrightness = constantExpression;
mbgl::style::PropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getRasterBrightnessMin(), propertyValue,
- @"Setting minimumRasterBrightness to a constant value should update raster-brightness-min.");
- XCTAssertEqualObjects(layer.minimumRasterBrightness, constantStyleValue,
- @"minimumRasterBrightness should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.minimumRasterBrightness = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting minimumRasterBrightness to a constant value expression should update raster-brightness-min.");
+ XCTAssertEqualObjects(layer.minimumRasterBrightness, constantExpression,
+ @"minimumRasterBrightness should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.minimumRasterBrightness = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getRasterBrightnessMin(), propertyValue,
- @"Setting minimumRasterBrightness to a camera function should update raster-brightness-min.");
- XCTAssertEqualObjects(layer.minimumRasterBrightness, functionStyleValue,
- @"minimumRasterBrightness should round-trip camera functions.");
+ @"Setting minimumRasterBrightness to a camera expression should update raster-brightness-min.");
+ XCTAssertEqualObjects(layer.minimumRasterBrightness, functionExpression,
+ @"minimumRasterBrightness should round-trip camera expressions.");
layer.minimumRasterBrightness = nil;
XCTAssertTrue(rawLayer->getRasterBrightnessMin().isUndefined(),
@"Unsetting minimumRasterBrightness should return raster-brightness-min to the default value.");
- XCTAssertEqualObjects(layer.minimumRasterBrightness, defaultStyleValue,
+ XCTAssertEqualObjects(layer.minimumRasterBrightness, defaultExpression,
@"minimumRasterBrightness should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.minimumRasterBrightness = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.minimumRasterBrightness = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.minimumRasterBrightness = functionExpression, NSException, NSInvalidArgumentException, @"MGLRasterLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.minimumRasterBrightness = functionExpression, NSException, NSInvalidArgumentException, @"MGLRasterLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// raster-contrast
{
XCTAssertTrue(rawLayer->getRasterContrast().isUndefined(),
@"raster-contrast should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.rasterContrast;
+ NSExpression *defaultExpression = layer.rasterContrast;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.rasterContrast = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.rasterContrast = constantExpression;
mbgl::style::PropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getRasterContrast(), propertyValue,
- @"Setting rasterContrast to a constant value should update raster-contrast.");
- XCTAssertEqualObjects(layer.rasterContrast, constantStyleValue,
- @"rasterContrast should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.rasterContrast = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting rasterContrast to a constant value expression should update raster-contrast.");
+ XCTAssertEqualObjects(layer.rasterContrast, constantExpression,
+ @"rasterContrast should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.rasterContrast = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getRasterContrast(), propertyValue,
- @"Setting rasterContrast to a camera function should update raster-contrast.");
- XCTAssertEqualObjects(layer.rasterContrast, functionStyleValue,
- @"rasterContrast should round-trip camera functions.");
+ @"Setting rasterContrast to a camera expression should update raster-contrast.");
+ XCTAssertEqualObjects(layer.rasterContrast, functionExpression,
+ @"rasterContrast should round-trip camera expressions.");
layer.rasterContrast = nil;
XCTAssertTrue(rawLayer->getRasterContrast().isUndefined(),
@"Unsetting rasterContrast should return raster-contrast to the default value.");
- XCTAssertEqualObjects(layer.rasterContrast, defaultStyleValue,
+ XCTAssertEqualObjects(layer.rasterContrast, defaultExpression,
@"rasterContrast should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.rasterContrast = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.rasterContrast = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.rasterContrast = functionExpression, NSException, NSInvalidArgumentException, @"MGLRasterLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.rasterContrast = functionExpression, NSException, NSInvalidArgumentException, @"MGLRasterLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
// Transition property test
layer.rasterContrastTransition = transitionTest;
auto toptions = rawLayer->getRasterContrastTransition();
@@ -160,126 +175,132 @@
{
XCTAssertTrue(rawLayer->getRasterFadeDuration().isUndefined(),
@"raster-fade-duration should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.rasterFadeDuration;
+ NSExpression *defaultExpression = layer.rasterFadeDuration;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.rasterFadeDuration = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.rasterFadeDuration = constantExpression;
mbgl::style::PropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getRasterFadeDuration(), propertyValue,
- @"Setting rasterFadeDuration to a constant value should update raster-fade-duration.");
- XCTAssertEqualObjects(layer.rasterFadeDuration, constantStyleValue,
- @"rasterFadeDuration should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.rasterFadeDuration = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting rasterFadeDuration to a constant value expression should update raster-fade-duration.");
+ XCTAssertEqualObjects(layer.rasterFadeDuration, constantExpression,
+ @"rasterFadeDuration should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.rasterFadeDuration = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getRasterFadeDuration(), propertyValue,
- @"Setting rasterFadeDuration to a camera function should update raster-fade-duration.");
- XCTAssertEqualObjects(layer.rasterFadeDuration, functionStyleValue,
- @"rasterFadeDuration should round-trip camera functions.");
+ @"Setting rasterFadeDuration to a camera expression should update raster-fade-duration.");
+ XCTAssertEqualObjects(layer.rasterFadeDuration, functionExpression,
+ @"rasterFadeDuration should round-trip camera expressions.");
layer.rasterFadeDuration = nil;
XCTAssertTrue(rawLayer->getRasterFadeDuration().isUndefined(),
@"Unsetting rasterFadeDuration should return raster-fade-duration to the default value.");
- XCTAssertEqualObjects(layer.rasterFadeDuration, defaultStyleValue,
+ XCTAssertEqualObjects(layer.rasterFadeDuration, defaultExpression,
@"rasterFadeDuration should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.rasterFadeDuration = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.rasterFadeDuration = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- // Transition property test
- layer.rasterFadeDurationTransition = transitionTest;
- auto toptions = rawLayer->getRasterFadeDurationTransition();
- XCTAssert(toptions.delay && MGLTimeIntervalFromDuration(*toptions.delay) == transitionTest.delay);
- XCTAssert(toptions.duration && MGLTimeIntervalFromDuration(*toptions.duration) == transitionTest.duration);
-
- MGLTransition rasterFadeDurationTransition = layer.rasterFadeDurationTransition;
- XCTAssertEqual(rasterFadeDurationTransition.delay, transitionTest.delay);
- XCTAssertEqual(rasterFadeDurationTransition.duration, transitionTest.duration);
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.rasterFadeDuration = functionExpression, NSException, NSInvalidArgumentException, @"MGLRasterLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.rasterFadeDuration = functionExpression, NSException, NSInvalidArgumentException, @"MGLRasterLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// raster-hue-rotate
{
XCTAssertTrue(rawLayer->getRasterHueRotate().isUndefined(),
@"raster-hue-rotate should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.rasterHueRotation;
+ NSExpression *defaultExpression = layer.rasterHueRotation;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.rasterHueRotation = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.rasterHueRotation = constantExpression;
mbgl::style::PropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getRasterHueRotate(), propertyValue,
- @"Setting rasterHueRotation to a constant value should update raster-hue-rotate.");
- XCTAssertEqualObjects(layer.rasterHueRotation, constantStyleValue,
- @"rasterHueRotation should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.rasterHueRotation = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting rasterHueRotation to a constant value expression should update raster-hue-rotate.");
+ XCTAssertEqualObjects(layer.rasterHueRotation, constantExpression,
+ @"rasterHueRotation should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.rasterHueRotation = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getRasterHueRotate(), propertyValue,
- @"Setting rasterHueRotation to a camera function should update raster-hue-rotate.");
- XCTAssertEqualObjects(layer.rasterHueRotation, functionStyleValue,
- @"rasterHueRotation should round-trip camera functions.");
+ @"Setting rasterHueRotation to a camera expression should update raster-hue-rotate.");
+ XCTAssertEqualObjects(layer.rasterHueRotation, functionExpression,
+ @"rasterHueRotation should round-trip camera expressions.");
layer.rasterHueRotation = nil;
XCTAssertTrue(rawLayer->getRasterHueRotate().isUndefined(),
@"Unsetting rasterHueRotation should return raster-hue-rotate to the default value.");
- XCTAssertEqualObjects(layer.rasterHueRotation, defaultStyleValue,
+ XCTAssertEqualObjects(layer.rasterHueRotation, defaultExpression,
@"rasterHueRotation should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.rasterHueRotation = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.rasterHueRotation = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.rasterHueRotation = functionExpression, NSException, NSInvalidArgumentException, @"MGLRasterLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.rasterHueRotation = functionExpression, NSException, NSInvalidArgumentException, @"MGLRasterLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// raster-opacity
{
XCTAssertTrue(rawLayer->getRasterOpacity().isUndefined(),
@"raster-opacity should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.rasterOpacity;
+ NSExpression *defaultExpression = layer.rasterOpacity;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.rasterOpacity = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.rasterOpacity = constantExpression;
mbgl::style::PropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getRasterOpacity(), propertyValue,
- @"Setting rasterOpacity to a constant value should update raster-opacity.");
- XCTAssertEqualObjects(layer.rasterOpacity, constantStyleValue,
- @"rasterOpacity should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.rasterOpacity = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting rasterOpacity to a constant value expression should update raster-opacity.");
+ XCTAssertEqualObjects(layer.rasterOpacity, constantExpression,
+ @"rasterOpacity should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.rasterOpacity = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getRasterOpacity(), propertyValue,
- @"Setting rasterOpacity to a camera function should update raster-opacity.");
- XCTAssertEqualObjects(layer.rasterOpacity, functionStyleValue,
- @"rasterOpacity should round-trip camera functions.");
+ @"Setting rasterOpacity to a camera expression should update raster-opacity.");
+ XCTAssertEqualObjects(layer.rasterOpacity, functionExpression,
+ @"rasterOpacity should round-trip camera expressions.");
layer.rasterOpacity = nil;
XCTAssertTrue(rawLayer->getRasterOpacity().isUndefined(),
@"Unsetting rasterOpacity should return raster-opacity to the default value.");
- XCTAssertEqualObjects(layer.rasterOpacity, defaultStyleValue,
+ XCTAssertEqualObjects(layer.rasterOpacity, defaultExpression,
@"rasterOpacity should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.rasterOpacity = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.rasterOpacity = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.rasterOpacity = functionExpression, NSException, NSInvalidArgumentException, @"MGLRasterLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.rasterOpacity = functionExpression, NSException, NSInvalidArgumentException, @"MGLRasterLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
// Transition property test
layer.rasterOpacityTransition = transitionTest;
auto toptions = rawLayer->getRasterOpacityTransition();
@@ -295,39 +316,44 @@
{
XCTAssertTrue(rawLayer->getRasterSaturation().isUndefined(),
@"raster-saturation should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.rasterSaturation;
+ NSExpression *defaultExpression = layer.rasterSaturation;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.rasterSaturation = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.rasterSaturation = constantExpression;
mbgl::style::PropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getRasterSaturation(), propertyValue,
- @"Setting rasterSaturation to a constant value should update raster-saturation.");
- XCTAssertEqualObjects(layer.rasterSaturation, constantStyleValue,
- @"rasterSaturation should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.rasterSaturation = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting rasterSaturation to a constant value expression should update raster-saturation.");
+ XCTAssertEqualObjects(layer.rasterSaturation, constantExpression,
+ @"rasterSaturation should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.rasterSaturation = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getRasterSaturation(), propertyValue,
- @"Setting rasterSaturation to a camera function should update raster-saturation.");
- XCTAssertEqualObjects(layer.rasterSaturation, functionStyleValue,
- @"rasterSaturation should round-trip camera functions.");
+ @"Setting rasterSaturation to a camera expression should update raster-saturation.");
+ XCTAssertEqualObjects(layer.rasterSaturation, functionExpression,
+ @"rasterSaturation should round-trip camera expressions.");
layer.rasterSaturation = nil;
XCTAssertTrue(rawLayer->getRasterSaturation().isUndefined(),
@"Unsetting rasterSaturation should return raster-saturation to the default value.");
- XCTAssertEqualObjects(layer.rasterSaturation, defaultStyleValue,
+ XCTAssertEqualObjects(layer.rasterSaturation, defaultExpression,
@"rasterSaturation should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.rasterSaturation = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.rasterSaturation = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.rasterSaturation = functionExpression, NSException, NSInvalidArgumentException, @"MGLRasterLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.rasterSaturation = functionExpression, NSException, NSInvalidArgumentException, @"MGLRasterLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
// Transition property test
layer.rasterSaturationTransition = transitionTest;
auto toptions = rawLayer->getRasterSaturationTransition();
diff --git a/platform/darwin/test/MGLSDKTestHelpers.swift b/platform/darwin/test/MGLSDKTestHelpers.swift
index f21041782e..576e3da86b 100644
--- a/platform/darwin/test/MGLSDKTestHelpers.swift
+++ b/platform/darwin/test/MGLSDKTestHelpers.swift
@@ -1,3 +1,4 @@
+import XCTest
import Foundation
class MGLSDKTestHelpers {
diff --git a/platform/darwin/test/MGLShapeSourceTests.mm b/platform/darwin/test/MGLShapeSourceTests.mm
index 561af7f3d0..868dca730a 100644
--- a/platform/darwin/test/MGLShapeSourceTests.mm
+++ b/platform/darwin/test/MGLShapeSourceTests.mm
@@ -2,6 +2,7 @@
#import <Mapbox/Mapbox.h>
#import "MGLFeature_Private.h"
+#import "MGLAbstractShapeSource_Private.h"
#import "MGLShapeSource_Private.h"
#import "MGLSource_Private.h"
@@ -297,7 +298,10 @@
// when a shape is included in the features array
MGLPolygon *polygon = [MGLPolygon polygonWithCoordinates:coordinates count:sizeof(coordinates)/sizeof(coordinates[0]) interiorPolygons:nil];
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wobjc-literal-conversion"
XCTAssertThrowsSpecificNamed([[MGLShapeSource alloc] initWithIdentifier:@"source-id-invalid" features:@[polygon] options:nil], NSException, NSInvalidArgumentException, @"Shape source should raise an exception if a shape is sent to the features initializer");
+#pragma clang diagnostic pop
}
- (void)testMGLShapeSourceWithShapesConvenienceInitializer {
diff --git a/platform/darwin/test/MGLStyleLayerTests.h b/platform/darwin/test/MGLStyleLayerTests.h
index f0b889f022..28c316c8ba 100644
--- a/platform/darwin/test/MGLStyleLayerTests.h
+++ b/platform/darwin/test/MGLStyleLayerTests.h
@@ -1,6 +1,9 @@
#import <Mapbox/Mapbox.h>
#import <XCTest/XCTest.h>
+#define MGLConstantExpression(constant) \
+ [NSExpression expressionForConstantValue:constant]
+
@interface MGLStyleLayerTests : XCTestCase <MGLMapViewDelegate>
@property (nonatomic, copy, readonly, class) NSString *layerType;
diff --git a/platform/darwin/test/MGLStyleLayerTests.mm.ejs b/platform/darwin/test/MGLStyleLayerTests.mm.ejs
index 5fdfc3d44e..e17501ed18 100644
--- a/platform/darwin/test/MGLStyleLayerTests.mm.ejs
+++ b/platform/darwin/test/MGLStyleLayerTests.mm.ejs
@@ -23,7 +23,7 @@
return @"<%- type %>";
}
-<% if (type !== 'background' && type !== 'raster') { -%>
+<% if (type !== 'background' && type !== 'raster' && type !== 'hillshade') { -%>
- (void)testPredicates {
MGLPointFeature *feature = [[MGLPointFeature alloc] init];
MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"sourceID" shape:feature options:nil];
@@ -59,50 +59,55 @@
MGLTransition transitionTest = MGLTransitionMake(5, 4);
<% for (const property of properties) { -%>
+<% if (property.name === 'heatmap-color') continue; -%>
// <%- originalPropertyName(property) %>
{
XCTAssertTrue(rawLayer->get<%- camelize(originalPropertyName(property)) %>().isUndefined(),
@"<%- originalPropertyName(property) %> should be unset initially.");
- MGLStyleValue<<%- propertyType(property) %>> *defaultStyleValue = layer.<%- objCName(property) %>;
+ NSExpression *defaultExpression = layer.<%- objCName(property) %>;
- MGLStyleValue<<%- propertyType(property) %>> *constantStyleValue = [MGLStyleValue<<%- propertyType(property) %>> valueWithRawValue:<%- objCTestValue(property, type, 3) %>];
- layer.<%- objCName(property) %> = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:<%- objCTestValue(property, type, true, 3) %>];
+ layer.<%- objCName(property) %> = constantExpression;
<% if (property["property-function"]) { -%>
mbgl::style::DataDrivenPropertyValue<<%- mbglType(property) %>> propertyValue = { <%- mbglTestValue(property, type) %> };
<% } else { -%>
mbgl::style::PropertyValue<<%- mbglType(property) %>> propertyValue = { <%- mbglTestValue(property, type) %> };
<% } -%>
XCTAssertEqual(rawLayer->get<%- camelize(originalPropertyName(property)) %>(), propertyValue,
- @"Setting <%- objCName(property) %> to a constant value should update <%- originalPropertyName(property) %>.");
- XCTAssertEqualObjects(layer.<%- objCName(property) %>, constantStyleValue,
- @"<%- objCName(property) %> should round-trip constant values.");
-
- MGLStyleValue<<%- propertyType(property) %>> * functionStyleValue = [MGLStyleValue<<%- propertyType(property) %>> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.<%- objCName(property) %> = functionStyleValue;
-
- mbgl::style::IntervalStops<<%- mbglType(property) %>> intervalStops = { {{18, <%- mbglTestValue(property, type) %>}} };
+ @"Setting <%- objCName(property) %> to a constant value expression should update <%- originalPropertyName(property) %>.");
+ XCTAssertEqualObjects(layer.<%- objCName(property) %>, constantExpression,
+ @"<%- objCName(property) %> should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:<%- objCTestValue(property, type, false, 3) %>];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.<%- objCName(property) %> = functionExpression;
+
+ mbgl::style::IntervalStops<<%- mbglType(property) %>> intervalStops = {{
+ { -INFINITY, <%- mbglTestValue(property, type) %> },
+ { 18, <%- mbglTestValue(property, type) %> },
+ }};
propertyValue = mbgl::style::CameraFunction<<%- mbglType(property) %>> { intervalStops };
XCTAssertEqual(rawLayer->get<%- camelize(originalPropertyName(property)) %>(), propertyValue,
- @"Setting <%- objCName(property) %> to a camera function should update <%- originalPropertyName(property) %>.");
- XCTAssertEqualObjects(layer.<%- objCName(property) %>, functionStyleValue,
- @"<%- objCName(property) %> should round-trip camera functions.");
+ @"Setting <%- objCName(property) %> to a camera expression should update <%- originalPropertyName(property) %>.");
+ XCTAssertEqualObjects(layer.<%- objCName(property) %>, functionExpression,
+ @"<%- objCName(property) %> should round-trip camera expressions.");
<% if (property["property-function"] && isInterpolatable(property)) { -%>
- functionStyleValue = [MGLStyleValue<<%- propertyType(property) %>> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
- layer.<%- objCName(property) %> = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.<%- objCName(property) %> = functionExpression;
mbgl::style::ExponentialStops<<%- mbglType(property) %>> exponentialStops = { {{18, <%- mbglTestValue(property, type) %>}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<<%- mbglType(property) %>> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->get<%- camelize(originalPropertyName(property)) %>(), propertyValue,
- @"Setting <%- objCName(property) %> to a source function should update <%- originalPropertyName(property) %>.");
- XCTAssertEqualObjects(layer.<%- objCName(property) %>, functionStyleValue,
- @"<%- objCName(property) %> should round-trip source functions.");
+ @"Setting <%- objCName(property) %> to a data expression should update <%- originalPropertyName(property) %>.");
+ XCTAssertEqualObjects(layer.<%- objCName(property) %>, functionExpression,
+ @"<%- objCName(property) %> should round-trip data expressions.");
- functionStyleValue = [MGLStyleValue<<%- propertyType(property) %>> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
- layer.<%- objCName(property) %> = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.<%- objCName(property) %> = functionExpression;
std::map<float, <%- mbglType(property) %>> innerStops { {18, <%- mbglTestValue(property, type) %>} };
mbgl::style::CompositeExponentialStops<<%- mbglType(property) %>> compositeStops { { {10.0, innerStops} }, 1.0 };
@@ -110,24 +115,25 @@
propertyValue = mbgl::style::CompositeFunction<<%- mbglType(property) %>> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->get<%- camelize(originalPropertyName(property)) %>(), propertyValue,
- @"Setting <%- objCName(property) %> to a composite function should update <%- originalPropertyName(property) %>.");
- XCTAssertEqualObjects(layer.<%- objCName(property) %>, functionStyleValue,
- @"<%- objCName(property) %> should round-trip composite functions.");
+ @"Setting <%- objCName(property) %> to a camera-data expression should update <%- originalPropertyName(property) %>.");
+ XCTAssertEqualObjects(layer.<%- objCName(property) %>, functionExpression,
+ @"<%- objCName(property) %> should round-trip camera-data expressions.");
<% } -%>
<% if (!property.required) { -%>
layer.<%- objCName(property) %> = nil;
XCTAssertTrue(rawLayer->get<%- camelize(originalPropertyName(property)) %>().isUndefined(),
@"Unsetting <%- objCName(property) %> should return <%- originalPropertyName(property) %> to the default value.");
- XCTAssertEqualObjects(layer.<%- objCName(property) %>, defaultStyleValue,
+ XCTAssertEqualObjects(layer.<%- objCName(property) %>, defaultExpression,
@"<%- objCName(property) %> should return the default value after being unset.");
<% } -%>
<% if (!property["property-function"]) { -%>
- functionStyleValue = [MGLStyleValue<<%- propertyType(property) %>> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.<%- objCName(property) %> = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<<%- propertyType(property) %>> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.<%- objCName(property) %> = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.<%- objCName(property) %> = functionExpression, NSException, NSInvalidArgumentException, @"MGL<%- camelize(type) %>Layer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.<%- objCName(property) %> = functionExpression, NSException, NSInvalidArgumentException, @"MGL<%- camelize(type) %>Layer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
<% } -%>
<% if (property["transition"] && !property.original) { -%>
// Transition property test
@@ -146,6 +152,7 @@
- (void)testPropertyNames {
<% for (const property of properties) { -%>
+<% if (property.name === 'heatmap-color') continue; -%>
[self testPropertyName:@"<%- property.getter || property.name %>" isBoolean:<%- property.type === 'boolean' ? 'YES' : 'NO' %>];
<% } -%>
}
diff --git a/platform/darwin/test/MGLStyleValueTests.h b/platform/darwin/test/MGLStyleValueTests.h
deleted file mode 100644
index a563de39f0..0000000000
--- a/platform/darwin/test/MGLStyleValueTests.h
+++ /dev/null
@@ -1,4 +0,0 @@
-#import <XCTest/XCTest.h>
-
-@interface MGLStyleValueTests : XCTestCase
-@end
diff --git a/platform/darwin/test/MGLStyleValueTests.m b/platform/darwin/test/MGLStyleValueTests.m
deleted file mode 100644
index cd6eec8324..0000000000
--- a/platform/darwin/test/MGLStyleValueTests.m
+++ /dev/null
@@ -1,113 +0,0 @@
-#import <XCTest/XCTest.h>
-#import <Mapbox/Mapbox.h>
-
-@interface MGLStyleValueTests : XCTestCase
-@end
-
-@implementation MGLStyleValueTests
-
-- (void)testStoplessFunction {
- XCTAssertThrowsSpecificNamed([MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential cameraStops:@{} options:nil], NSException, NSInvalidArgumentException, @"Stopless function should raise an exception");
-}
-
-- (void)testDeprecatedFunctions {
- MGLShapeSource *shapeSource = [[MGLShapeSource alloc] initWithIdentifier:@"test"
- shape:nil
- options:nil];
- MGLSymbolStyleLayer *symbolStyleLayer = [[MGLSymbolStyleLayer alloc] initWithIdentifier:@"symbolLayer"
- source:shapeSource];
- MGLCircleStyleLayer *circleStyleLayer = [[MGLCircleStyleLayer alloc] initWithIdentifier:@"circleLayer"
- source:shapeSource];
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- // deprecated function, stops with float values
- NSDictionary<NSNumber *, MGLStyleValue<NSNumber *> *> *stops = @{
- @1: [MGLStyleValue<NSNumber *> valueWithRawValue:@0],
- @2: [MGLStyleValue<NSNumber *> valueWithRawValue:@1],
- @3: [MGLStyleValue<NSNumber *> valueWithRawValue:@2],
- @4: [MGLStyleValue<NSNumber *> valueWithRawValue:@0],
- };
- MGLStyleValue<NSNumber *> *iconHaloBlurStyleValue =
- [MGLStyleValue<NSNumber *> valueWithInterpolationBase:1.0 stops:stops];
- symbolStyleLayer.iconHaloBlur = iconHaloBlurStyleValue;
- XCTAssertEqualObjects(symbolStyleLayer.iconHaloBlur, iconHaloBlurStyleValue);
-
- // deprecated function, stops with boolean values
- stops = @{
- @1: [MGLStyleValue<NSNumber *> valueWithRawValue:@YES],
- @2: [MGLStyleValue<NSNumber *> valueWithRawValue:@NO],
- @3: [MGLStyleValue<NSNumber *> valueWithRawValue:@YES],
- @4: [MGLStyleValue<NSNumber *> valueWithRawValue:@NO],
- };
- MGLStyleValue<NSNumber *> *iconAllowsOverlapStyleValue =
- [MGLStyleValue<NSNumber *> valueWithInterpolationBase:1.0 stops:stops];
- symbolStyleLayer.iconAllowsOverlap = iconAllowsOverlapStyleValue;
- // iconAllowsOverlap is boolean so mgl and mbgl conversions will coerce the developers stops into interval stops
- MGLStyleValue<NSNumber *> *expectedIconAllowsOverlapStyleValue =
- [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval
- cameraStops:stops
- options:nil];
- XCTAssertEqualObjects(symbolStyleLayer.iconAllowsOverlap, expectedIconAllowsOverlapStyleValue);
-
- ///
- // creating and using MGLStyleFunctions directly
- ///
-
- NSDictionary<NSNumber *, MGLStyleValue<NSNumber *> *> *circleRadiusStops = @{
- @0: [MGLStyleValue<NSNumber *> valueWithRawValue:@10],
- @20: [MGLStyleValue<NSNumber *> valueWithRawValue:@5],
- };
- MGLStyleFunction<NSNumber *> *circleRadiusFunction =
- [MGLStyleFunction<NSNumber *> functionWithInterpolationBase:1.0
- stops:circleRadiusStops];
- circleStyleLayer.circleRadius = circleRadiusFunction;
- MGLStyleValue<NSNumber *> *expectedCircleRadiusFunction =
- [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential
- cameraStops:circleRadiusStops
- options:nil];
- // setting a data driven property to an MGLStyleFunction should return an exponential camera function
- XCTAssertEqualObjects(circleStyleLayer.circleRadius, expectedCircleRadiusFunction);
-
- CGVector circleTranslationOne = CGVectorMake(100, 0);
- CGVector circleTranslationTwo = CGVectorMake(0, 0);
-#if TARGET_OS_IPHONE
- NSValue *circleTranslationValueOne = [NSValue valueWithCGVector:circleTranslationOne];
- NSValue *circleTranslationValueTwo = [NSValue valueWithCGVector:circleTranslationTwo];
-#else
- NSValue *circleTranslationValueOne = [NSValue value:&circleTranslationOne withObjCType:@encode(CGVector)];
- NSValue *circleTranslationValueTwo = [NSValue value:&circleTranslationTwo withObjCType:@encode(CGVector)];
-#endif
-
- NSDictionary<NSNumber *, MGLStyleValue<NSValue *> *> *circleTranslationStops = @{
- @0: [MGLStyleValue<NSValue *> valueWithRawValue:circleTranslationValueOne],
- @10: [MGLStyleValue<NSValue *> valueWithRawValue:circleTranslationValueTwo],
- };
- MGLStyleFunction<NSValue *> *circleTranslationFunction =
- [MGLStyleFunction<NSValue *> functionWithInterpolationBase:1.0
- stops:circleTranslationStops];
- circleStyleLayer.circleTranslation = circleTranslationFunction;
- MGLStyleValue<NSValue *> *expectedCircleTranslationFunction =
- [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeExponential
- cameraStops:circleTranslationStops
- options:nil];
- // setting a non-data driven, interpolatable property to an MGLStyleFunction should return an exponential camera function
- XCTAssertEqualObjects(circleStyleLayer.circleTranslation, expectedCircleTranslationFunction);
-
- NSDictionary<NSNumber *, MGLStyleValue<NSNumber *> *> *iconOptionalStops = @{
- @0: [MGLStyleValue<NSNumber *> valueWithRawValue:@NO],
- @20: [MGLStyleValue<NSNumber *> valueWithRawValue:@YES],
- };
- MGLStyleFunction<NSNumber *> *iconOptionalFunction =
- [MGLStyleFunction<NSNumber *> valueWithInterpolationBase:1.0
- stops:iconOptionalStops];
- symbolStyleLayer.iconOptional = iconOptionalFunction;
- MGLStyleValue<NSNumber *> *expectedIconOptionalFunction =
- [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval
- cameraStops:iconOptionalStops
- options:nil];
- XCTAssertEqualObjects(symbolStyleLayer.iconOptional, expectedIconOptionalFunction);
-#pragma clang diagnostic pop
-}
-
-@end
diff --git a/platform/darwin/test/MGLStyleValueTests.swift b/platform/darwin/test/MGLStyleValueTests.swift
deleted file mode 100644
index c559037588..0000000000
--- a/platform/darwin/test/MGLStyleValueTests.swift
+++ /dev/null
@@ -1,362 +0,0 @@
-import XCTest
-import Mapbox
-
-#if os(iOS) || os(watchOS) || os(tvOS)
-typealias MGLColor = UIColor
-#elseif os(macOS)
-typealias MGLColor = NSColor
-#endif
-
-#if swift(>=3.2)
-#else
-func XCTAssertEqual<T: FloatingPoint>(_ lhs: @autoclosure () throws -> T, _ rhs: @autoclosure () throws -> T, accuracy: T) {
- XCTAssertEqualWithAccuracy(lhs, rhs, accuracy: accuracy)
-}
-#endif
-
-extension MGLStyleValueTests {
-
- struct Color {
- var red: CGFloat = 0
- var green: CGFloat = 0
- var blue: CGFloat = 0
- var alpha: CGFloat = 0
- }
-
- func assertColorsEqualWithAccuracy(_ actual: MGLColor, _ expected: MGLColor, accuracy: Float = 1/255) {
- var actualColor = Color()
- var expectedColor = Color()
-
- actual.getRed(&actualColor.red, green: &actualColor.green, blue: &actualColor.blue, alpha: &actualColor.alpha)
- expected.getRed(&expectedColor.red, green: &expectedColor.green, blue: &expectedColor.blue, alpha: &expectedColor.alpha)
-
- XCTAssertEqual(Float(actualColor.red), Float(expectedColor.red), accuracy: accuracy)
- XCTAssertEqual(Float(actualColor.green), Float(expectedColor.green), accuracy: accuracy)
- XCTAssertEqual(Float(actualColor.blue), Float(expectedColor.blue), accuracy: accuracy)
- XCTAssertEqual(Float(actualColor.alpha), Float(expectedColor.alpha), accuracy: accuracy)
- }
-
- func assertColorValuesEqual(_ actual: MGLStyleValue<MGLColor>, _ expected: MGLStyleValue<MGLColor>) {
- guard type(of: actual) == type(of: expected) else {
- XCTFail("Expected \(type(of: expected)), but found \(type(of: actual)) instead.")
- return
- }
-
- if let actualConstant = actual as? MGLConstantStyleValue<MGLColor> {
- assertColorsEqualWithAccuracy(actualConstant.rawValue, (expected as! MGLConstantStyleValue<MGLColor>).rawValue)
- } else if let actualFunction = actual as? MGLStyleFunction<MGLColor>,
- let expectedFunction = expected as? MGLStyleFunction<MGLColor> {
-
- // unless we have stops, there's no need for a custom comparison - default to plain == assertion
- guard let actualStops = actualFunction.stops, let expectedStops = expectedFunction.stops else {
- XCTAssertEqual(actualFunction, expectedFunction)
- return
- }
-
- guard expectedStops is [String: Any] || expectedStops is [Float:Any] else {
- XCTFail("Stop levels must be String or Float.")
- return
- }
-
- XCTAssertEqual(actualFunction.interpolationBase, expectedFunction.interpolationBase)
- XCTAssertEqual(actualFunction.interpolationMode, expectedFunction.interpolationMode)
- if let actualFunction = actualFunction as? MGLSourceStyleFunction<MGLColor>,
- let expectedFunction = expectedFunction as? MGLSourceStyleFunction<MGLColor> {
- XCTAssertEqual(actualFunction.defaultValue, expectedFunction.defaultValue)
- } else if let actualFunction = actualFunction as? MGLCompositeStyleFunction<MGLColor>,
- let expectedFunction = expectedFunction as? MGLCompositeStyleFunction<MGLColor> {
- XCTAssertEqual(actualFunction.defaultValue, expectedFunction.defaultValue)
- }
-
- func assertStopEqual (_ actualValue: Any?, _ expectedValue: Any?) {
- guard type(of: actualValue) == type(of: expectedValue) else {
- XCTFail("Expected stop value of type \(type(of: expectedValue)), but found \(type(of: actualValue)) instead.")
- return
- }
- if let actualValue = actualValue as? MGLConstantStyleValue<MGLColor>,
- let expectedValue = expectedValue as? MGLConstantStyleValue<MGLColor> {
- assertColorsEqualWithAccuracy(actualValue.rawValue, expectedValue.rawValue)
- } else if let actualValue = actualValue as? MGLConstantStyleValue<AnyObject>,
- let expectedValue = expectedValue as? MGLConstantStyleValue<AnyObject> {
- XCTAssertEqual(actualValue, expectedValue)
- } else {
- XCTFail("Unsupported stop value type \(type(of: actualValue)).")
- }
- }
-
- XCTAssertEqual(actualStops.count, expectedStops.count)
- if let actualStops = actualStops as? [String:Any], let expectedStops = expectedStops as? [String: Any] {
- for (key, value) in actualStops {
- assertStopEqual(value, expectedStops[key])
- }
- } else if let actualStops = actualStops as? [Float:Any], let expectedStops = expectedStops as? [Float:Any] {
- for (key, value) in actualStops {
- assertStopEqual(value, expectedStops[key])
- }
- } else {
- XCTFail("Expected stops of type \(type(of: Array(expectedStops.keys)).Index.self), but found \(type(of: Array(actualStops.keys)).Index.self) instead.")
- return
- }
- } else {
- XCTFail("MGLStyleValue<MGLColor> must be either a constant or a style function.")
- }
- }
-
- func testConstantValues() {
- let shapeSource = MGLShapeSource(identifier: "source", shape: nil, options: nil)
- let symbolStyleLayer = MGLSymbolStyleLayer(identifier: "symbolLayer", source: shapeSource)
- let circleStyleLayer = MGLCircleStyleLayer(identifier: "circleLayer", source: shapeSource)
-
- // Boolean
- symbolStyleLayer.iconAllowsOverlap = MGLConstantStyleValue(rawValue: true)
- XCTAssertEqual((symbolStyleLayer.iconAllowsOverlap as! MGLConstantStyleValue<NSNumber>).rawValue, true)
-
- // Number
- symbolStyleLayer.iconHaloWidth = MGLConstantStyleValue(rawValue: 3)
- XCTAssertEqual((symbolStyleLayer.iconHaloWidth as! MGLConstantStyleValue<NSNumber>).rawValue, 3)
-
- // String
- symbolStyleLayer.text = MGLConstantStyleValue(rawValue: "{name}")
- XCTAssertEqual((symbolStyleLayer.text as! MGLConstantStyleValue<NSString>).rawValue, "{name}")
-
- var circleTranslationOne = CGVector(dx: 100, dy: 0)
- let circleTranslationValueOne = NSValue(bytes: &circleTranslationOne, objCType: "{CGVector=dd}")
-
- // non-data-driven (interpolatable property value), set to constant style value
- let expectedCircleTranslationValue = MGLStyleValue<NSValue>(rawValue: circleTranslationValueOne)
- circleStyleLayer.circleTranslation = expectedCircleTranslationValue
- XCTAssertEqual(circleStyleLayer.circleTranslation, expectedCircleTranslationValue)
-
- // non-data-driven (enumeration property value), set to constant style value
- let expectedCircleScaleAlignmentValue = MGLStyleValue<NSValue>(rawValue: NSValue(mglCircleScaleAlignment: .map))
- circleStyleLayer.circleScaleAlignment = expectedCircleScaleAlignmentValue
- XCTAssertEqual(circleStyleLayer.circleScaleAlignment, expectedCircleScaleAlignmentValue)
- }
-
- func testFunctionsWithNonDataDrivenProperties() {
- let shapeSource = MGLShapeSource(identifier: "test", shape: nil, options: nil)
- let circleStyleLayer = MGLCircleStyleLayer(identifier: "circleLayer", source: shapeSource)
-
- var circleTranslationOne = CGVector(dx: 100, dy: 0)
- let circleTranslationValueOne = NSValue(bytes: &circleTranslationOne, objCType: "{CGVector=dd}")
- var circleTranslationTwo = CGVector(dx: 0, dy: 0)
- let circleTranslationValueTwo = NSValue(bytes: &circleTranslationTwo, objCType: "{CGVector=dd}")
-
- let circleTranslationStops : [Float:MGLStyleValue<NSValue>] = [
- 0: MGLStyleValue<NSValue>(rawValue: circleTranslationValueOne),
- 10: MGLStyleValue<NSValue>(rawValue: circleTranslationValueTwo)
- ]
-
- // non-data-driven (interpolatable property value), camera function with CGVector (NSValue) stop values
- let expectedCircleTranslationValue = MGLStyleValue<NSValue>(
- interpolationMode: .interval,
- cameraStops: circleTranslationStops,
- options: nil
- )
- circleStyleLayer.circleTranslation = expectedCircleTranslationValue
- XCTAssertEqual(circleStyleLayer.circleTranslation, expectedCircleTranslationValue)
-
- // non-data-driven (enumeration property value), camera function with MGLCircleScaleAlignment enum (NSValue) stop values
- let scaleAlignmentStops : [Float:MGLStyleValue<NSValue>] = [
- 0: MGLStyleValue(rawValue: NSValue(mglCircleScaleAlignment: .map)),
- 10: MGLStyleValue(rawValue: NSValue(mglCircleScaleAlignment: .viewport))
- ]
- let expectedCircleScaleAlignmentValue = MGLStyleValue<NSValue>(
- interpolationMode: .interval,
- cameraStops: scaleAlignmentStops,
- options: nil
- )
- circleStyleLayer.circleScaleAlignment = expectedCircleScaleAlignmentValue
- XCTAssertEqual(circleStyleLayer.circleScaleAlignment, expectedCircleScaleAlignmentValue)
- }
-
- func testFunctionsWithDataDrivenProperties() {
- let shapeSource = MGLShapeSource(identifier: "test", shape: nil, options: nil)
- let circleStyleLayer = MGLCircleStyleLayer(identifier: "circleLayer", source: shapeSource)
-
- // data-driven, camera function with exponential color stop values
- let redGreenStops : [Float:MGLStyleValue<MGLColor>] = [
- 0: MGLStyleValue<MGLColor>(rawValue: .red),
- 10: MGLStyleValue<MGLColor>(rawValue: .red),
- 15: MGLStyleValue<MGLColor>(rawValue: .green)
- ]
- let expectedCircleColorValue = MGLStyleValue<MGLColor>(
- interpolationMode: .exponential,
- cameraStops: redGreenStops,
- options: [.interpolationBase: 10.0]
- )
- circleStyleLayer.circleColor = expectedCircleColorValue
- assertColorValuesEqual(circleStyleLayer.circleColor as! MGLStyleFunction<MGLColor>, expectedCircleColorValue as! MGLStyleFunction<MGLColor>)
-
- // data-driven, source function with categorical color stop values with string attribute keys
- let redOnlyStops = [
- "red": MGLStyleValue<MGLColor>(rawValue: .red)
- ]
- let expectedRedCategoricalValue = MGLStyleValue<MGLColor>(
- interpolationMode: .categorical,
- sourceStops: redOnlyStops,
- attributeName: "red",
- options: [.defaultValue: MGLStyleValue<MGLColor>(rawValue: .cyan)]
- )
- circleStyleLayer.circleColor = expectedRedCategoricalValue
- assertColorValuesEqual(circleStyleLayer.circleColor, expectedRedCategoricalValue)
-
- // data-driven, source function with categorical color stop values with integer attribute keys
- let greenOrangeStops : [Float:MGLStyleValue<MGLColor>] = [
- 0: MGLStyleValue<MGLColor>(rawValue: .green),
- 100: MGLStyleValue<MGLColor>(rawValue: .orange)
- ]
- let expectedGreenOrangeCategoricalValue = MGLStyleValue<MGLColor>(
- interpolationMode: .categorical,
- sourceStops: greenOrangeStops,
- attributeName: "temp",
- options: [.defaultValue: MGLStyleValue<MGLColor>(rawValue: .red)]
- )
- circleStyleLayer.circleColor = expectedGreenOrangeCategoricalValue
- assertColorValuesEqual(circleStyleLayer.circleColor, expectedGreenOrangeCategoricalValue)
-
- // data-driven, source function with exponential color stop values
- let expectedRedGreenSourceExponentialValue = MGLStyleValue<MGLColor>(
- interpolationMode: .exponential,
- sourceStops: redGreenStops,
- attributeName: "temp",
- options: nil
- )
- circleStyleLayer.circleColor = expectedRedGreenSourceExponentialValue
- assertColorValuesEqual(circleStyleLayer.circleColor, expectedRedGreenSourceExponentialValue)
-
- // data-driven, identity source function
- let expectedSourceIdentityValue = MGLStyleValue<MGLColor>(
- interpolationMode: .identity,
- sourceStops: nil,
- attributeName: "size",
- options: [.defaultValue: MGLStyleValue<MGLColor>(rawValue: .green)]
- )
- circleStyleLayer.circleColor = expectedSourceIdentityValue
- assertColorValuesEqual(circleStyleLayer.circleColor, expectedSourceIdentityValue)
-
- // data-driven, source function with categorical color stop values with boolean attribute keys
- let booleanCategoricalStops = [
- false: MGLStyleValue<NSNumber>(rawValue: 0),
- true: MGLStyleValue<NSNumber>(rawValue: 2)
- ]
- let expectedCircleBlurCategoricalValue = MGLStyleValue<NSNumber>(
- interpolationMode: .categorical,
- sourceStops: booleanCategoricalStops,
- attributeName: "fuzzy",
- options: [.defaultValue: MGLStyleValue<NSNumber>(rawValue: 42)]
- )
- circleStyleLayer.circleBlur = expectedCircleBlurCategoricalValue
- XCTAssertEqual(circleStyleLayer.circleBlur, expectedCircleBlurCategoricalValue)
-
- // data-driven, composite function with inner categorical color stop values with string attribute keys nested in outer camera stops
- let smallRadius = MGLStyleValue<NSNumber>(rawValue: 5)
- let mediumRadius = MGLStyleValue<NSNumber>(rawValue: 10)
- let largeRadius = MGLStyleValue<NSNumber>(rawValue: 20)
- let radiusCompositeCategoricalStops: [Float: [String: MGLStyleValue<NSNumber>]] = [
- 0: ["green": smallRadius],
- 10: ["green": smallRadius],
- 15: ["green": largeRadius],
- 20: ["green": largeRadius]
- ]
- let defaultRadius = MGLStyleValue<NSNumber>(rawValue: 2)
- let expectedCompositeCategoricalValue = MGLStyleValue<NSNumber>(
- interpolationMode: .categorical,
- compositeStops: radiusCompositeCategoricalStops,
- attributeName: "color",
- options: [.defaultValue: defaultRadius]
- )
- circleStyleLayer.circleRadius = expectedCompositeCategoricalValue
-
- var compositeValue = circleStyleLayer.circleRadius as! MGLCompositeStyleFunction
- var expectedCompositeValue = expectedCompositeCategoricalValue as! MGLCompositeStyleFunction
- XCTAssertEqual(compositeValue.attributeName, expectedCompositeValue.attributeName)
- XCTAssertEqual(compositeValue.stops as NSDictionary, radiusCompositeCategoricalStops as NSDictionary)
- XCTAssertEqual(compositeValue.interpolationMode, expectedCompositeValue.interpolationMode)
- XCTAssertEqual(compositeValue.defaultValue, expectedCompositeValue.defaultValue)
-
- // data-driven, composite function with inner exponential color stop values nested in outer camera stops
- let radiusCompositeExponentialOrIntervalStops: [Float: [Float: MGLStyleValue<NSNumber>]] = [
- 0: [0: MGLStyleValue<NSNumber>(rawValue: 5)],
- 10: [200: MGLStyleValue<NSNumber>(rawValue: 5)],
- 20: [200: MGLStyleValue<NSNumber>(rawValue: 20)]
- ]
-
- let expectedStops = [
- 0: [0: MGLStyleValue<NSNumber>(rawValue: 5)],
- 10: [200: MGLStyleValue<NSNumber>(rawValue: 5)],
- 20: [200: MGLStyleValue<NSNumber>(rawValue: 20)]
- ]
- circleStyleLayer.circleRadius = MGLStyleValue<NSNumber>(
- interpolationMode: .exponential,
- compositeStops: [
- 0: [0: MGLStyleValue<NSNumber>(rawValue: 5)],
- 10: [200: MGLStyleValue<NSNumber>(rawValue: 5)],
- 20: [200: MGLStyleValue<NSNumber>(rawValue: 20)]
- ],
- attributeName: "temp",
- options: [.defaultValue: mediumRadius]
- )
-
- let expectedCompositeExponentialValue = MGLStyleValue<NSNumber>(
- interpolationMode: .exponential,
- compositeStops: radiusCompositeExponentialOrIntervalStops,
- attributeName: "temp",
- options: [.defaultValue: mediumRadius]
- )
-
- compositeValue = circleStyleLayer.circleRadius as! MGLCompositeStyleFunction
- expectedCompositeValue = expectedCompositeExponentialValue as! MGLCompositeStyleFunction
- XCTAssertEqual(compositeValue.attributeName, expectedCompositeValue.attributeName)
- XCTAssertEqual(compositeValue.stops as NSDictionary, expectedStops as NSDictionary)
- XCTAssertEqual(compositeValue.interpolationMode, expectedCompositeValue.interpolationMode)
- XCTAssertEqual(compositeValue.defaultValue, expectedCompositeValue.defaultValue)
-
- // get a value back
- if let returnedCircleRadius = circleStyleLayer.circleRadius as? MGLCompositeStyleFunction<NSNumber> {
- if let returnedStops = returnedCircleRadius.stops as NSDictionary? as? [NSNumber: [NSNumber: MGLStyleValue<NSNumber>]] {
- let lhs: MGLStyleValue<NSNumber> = returnedStops[0]!.values.first!
- let rhs: MGLStyleValue<NSNumber> = radiusCompositeExponentialOrIntervalStops[0]!.values.first!
- XCTAssertEqual(lhs, rhs)
- }
- }
-
- // get value back as base class
- if let returnedCircleRadius = circleStyleLayer.circleRadius as? MGLStyleFunction<NSNumber> {
- if let returnedStops = returnedCircleRadius.stops as NSDictionary? as? [NSNumber: [NSNumber: MGLStyleValue<NSNumber>]] {
- let lhs: MGLStyleValue<NSNumber> = returnedStops[0]!.values.first!
- let rhs: MGLStyleValue<NSNumber> = radiusCompositeExponentialOrIntervalStops[0]!.values.first!
- XCTAssertEqual(lhs, rhs)
- }
- }
-
- // data-driven, composite function with inner interval color stop values nested in outer camera stops
- let expectedCompositeIntervalValue = MGLStyleValue<NSNumber>(
- interpolationMode: .interval,
- compositeStops: [
-
- 10: [200: MGLStyleValue<NSNumber>(rawValue: 5)],
- 20: [200: MGLStyleValue<NSNumber>(rawValue: 20)]
- ],
- attributeName: "temp",
- options: nil
- )
- circleStyleLayer.circleRadius = MGLStyleValue<NSNumber>(
- interpolationMode: .interval,
- compositeStops: [
- 0: [0: MGLStyleValue<NSNumber>(rawValue: 5)],
- 10: [200: MGLStyleValue<NSNumber>(rawValue: 5)],
- 20: [200: MGLStyleValue<NSNumber>(rawValue: 20)]
- ],
- attributeName: "temp",
- options: nil
- )
-
- compositeValue = circleStyleLayer.circleRadius as! MGLCompositeStyleFunction
- expectedCompositeValue = expectedCompositeIntervalValue as! MGLCompositeStyleFunction
- XCTAssertEqual(compositeValue.attributeName, expectedCompositeValue.attributeName)
- XCTAssertEqual(compositeValue.stops as NSDictionary, expectedStops as NSDictionary)
- XCTAssertEqual(compositeValue.interpolationMode, expectedCompositeValue.interpolationMode)
- XCTAssertEqual(compositeValue.defaultValue, expectedCompositeValue.defaultValue)
- }
-}
diff --git a/platform/darwin/test/MGLSymbolStyleLayerTests.mm b/platform/darwin/test/MGLSymbolStyleLayerTests.mm
index 1ac86dd402..05e091b5c8 100644
--- a/platform/darwin/test/MGLSymbolStyleLayerTests.mm
+++ b/platform/darwin/test/MGLSymbolStyleLayerTests.mm
@@ -52,72 +52,81 @@
{
XCTAssertTrue(rawLayer->getIconAllowOverlap().isUndefined(),
@"icon-allow-overlap should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.iconAllowsOverlap;
+ NSExpression *defaultExpression = layer.iconAllowsOverlap;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@YES];
- layer.iconAllowsOverlap = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"true"];
+ layer.iconAllowsOverlap = constantExpression;
mbgl::style::PropertyValue<bool> propertyValue = { true };
XCTAssertEqual(rawLayer->getIconAllowOverlap(), propertyValue,
- @"Setting iconAllowsOverlap to a constant value should update icon-allow-overlap.");
- XCTAssertEqualObjects(layer.iconAllowsOverlap, constantStyleValue,
- @"iconAllowsOverlap should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.iconAllowsOverlap = functionStyleValue;
-
- mbgl::style::IntervalStops<bool> intervalStops = { {{18, true}} };
+ @"Setting iconAllowsOverlap to a constant value expression should update icon-allow-overlap.");
+ XCTAssertEqualObjects(layer.iconAllowsOverlap, constantExpression,
+ @"iconAllowsOverlap should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"true"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.iconAllowsOverlap = functionExpression;
+
+ mbgl::style::IntervalStops<bool> intervalStops = {{
+ { -INFINITY, true },
+ { 18, true },
+ }};
propertyValue = mbgl::style::CameraFunction<bool> { intervalStops };
XCTAssertEqual(rawLayer->getIconAllowOverlap(), propertyValue,
- @"Setting iconAllowsOverlap to a camera function should update icon-allow-overlap.");
- XCTAssertEqualObjects(layer.iconAllowsOverlap, functionStyleValue,
- @"iconAllowsOverlap should round-trip camera functions.");
+ @"Setting iconAllowsOverlap to a camera expression should update icon-allow-overlap.");
+ XCTAssertEqualObjects(layer.iconAllowsOverlap, functionExpression,
+ @"iconAllowsOverlap should round-trip camera expressions.");
layer.iconAllowsOverlap = nil;
XCTAssertTrue(rawLayer->getIconAllowOverlap().isUndefined(),
@"Unsetting iconAllowsOverlap should return icon-allow-overlap to the default value.");
- XCTAssertEqualObjects(layer.iconAllowsOverlap, defaultStyleValue,
+ XCTAssertEqualObjects(layer.iconAllowsOverlap, defaultExpression,
@"iconAllowsOverlap should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.iconAllowsOverlap = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.iconAllowsOverlap = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.iconAllowsOverlap = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.iconAllowsOverlap = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// icon-anchor
{
XCTAssertTrue(rawLayer->getIconAnchor().isUndefined(),
@"icon-anchor should be unset initially.");
- MGLStyleValue<NSValue *> *defaultStyleValue = layer.iconAnchor;
+ NSExpression *defaultExpression = layer.iconAnchor;
- MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLIconAnchor:MGLIconAnchorBottomRight]];
- layer.iconAnchor = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'bottom-right'"];
+ layer.iconAnchor = constantExpression;
mbgl::style::DataDrivenPropertyValue<mbgl::style::SymbolAnchorType> propertyValue = { mbgl::style::SymbolAnchorType::BottomRight };
XCTAssertEqual(rawLayer->getIconAnchor(), propertyValue,
- @"Setting iconAnchor to a constant value should update icon-anchor.");
- XCTAssertEqualObjects(layer.iconAnchor, constantStyleValue,
- @"iconAnchor should round-trip constant values.");
-
- MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.iconAnchor = functionStyleValue;
-
- mbgl::style::IntervalStops<mbgl::style::SymbolAnchorType> intervalStops = { {{18, mbgl::style::SymbolAnchorType::BottomRight}} };
+ @"Setting iconAnchor to a constant value expression should update icon-anchor.");
+ XCTAssertEqualObjects(layer.iconAnchor, constantExpression,
+ @"iconAnchor should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"'bottom-right'"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.iconAnchor = functionExpression;
+
+ mbgl::style::IntervalStops<mbgl::style::SymbolAnchorType> intervalStops = {{
+ { -INFINITY, mbgl::style::SymbolAnchorType::BottomRight },
+ { 18, mbgl::style::SymbolAnchorType::BottomRight },
+ }};
propertyValue = mbgl::style::CameraFunction<mbgl::style::SymbolAnchorType> { intervalStops };
XCTAssertEqual(rawLayer->getIconAnchor(), propertyValue,
- @"Setting iconAnchor to a camera function should update icon-anchor.");
- XCTAssertEqualObjects(layer.iconAnchor, functionStyleValue,
- @"iconAnchor should round-trip camera functions.");
+ @"Setting iconAnchor to a camera expression should update icon-anchor.");
+ XCTAssertEqualObjects(layer.iconAnchor, functionExpression,
+ @"iconAnchor should round-trip camera expressions.");
layer.iconAnchor = nil;
XCTAssertTrue(rawLayer->getIconAnchor().isUndefined(),
@"Unsetting iconAnchor should return icon-anchor to the default value.");
- XCTAssertEqualObjects(layer.iconAnchor, defaultStyleValue,
+ XCTAssertEqualObjects(layer.iconAnchor, defaultExpression,
@"iconAnchor should return the default value after being unset.");
}
@@ -125,72 +134,81 @@
{
XCTAssertTrue(rawLayer->getIconIgnorePlacement().isUndefined(),
@"icon-ignore-placement should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.iconIgnoresPlacement;
+ NSExpression *defaultExpression = layer.iconIgnoresPlacement;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@YES];
- layer.iconIgnoresPlacement = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"true"];
+ layer.iconIgnoresPlacement = constantExpression;
mbgl::style::PropertyValue<bool> propertyValue = { true };
XCTAssertEqual(rawLayer->getIconIgnorePlacement(), propertyValue,
- @"Setting iconIgnoresPlacement to a constant value should update icon-ignore-placement.");
- XCTAssertEqualObjects(layer.iconIgnoresPlacement, constantStyleValue,
- @"iconIgnoresPlacement should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.iconIgnoresPlacement = functionStyleValue;
-
- mbgl::style::IntervalStops<bool> intervalStops = { {{18, true}} };
+ @"Setting iconIgnoresPlacement to a constant value expression should update icon-ignore-placement.");
+ XCTAssertEqualObjects(layer.iconIgnoresPlacement, constantExpression,
+ @"iconIgnoresPlacement should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"true"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.iconIgnoresPlacement = functionExpression;
+
+ mbgl::style::IntervalStops<bool> intervalStops = {{
+ { -INFINITY, true },
+ { 18, true },
+ }};
propertyValue = mbgl::style::CameraFunction<bool> { intervalStops };
XCTAssertEqual(rawLayer->getIconIgnorePlacement(), propertyValue,
- @"Setting iconIgnoresPlacement to a camera function should update icon-ignore-placement.");
- XCTAssertEqualObjects(layer.iconIgnoresPlacement, functionStyleValue,
- @"iconIgnoresPlacement should round-trip camera functions.");
+ @"Setting iconIgnoresPlacement to a camera expression should update icon-ignore-placement.");
+ XCTAssertEqualObjects(layer.iconIgnoresPlacement, functionExpression,
+ @"iconIgnoresPlacement should round-trip camera expressions.");
layer.iconIgnoresPlacement = nil;
XCTAssertTrue(rawLayer->getIconIgnorePlacement().isUndefined(),
@"Unsetting iconIgnoresPlacement should return icon-ignore-placement to the default value.");
- XCTAssertEqualObjects(layer.iconIgnoresPlacement, defaultStyleValue,
+ XCTAssertEqualObjects(layer.iconIgnoresPlacement, defaultExpression,
@"iconIgnoresPlacement should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.iconIgnoresPlacement = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.iconIgnoresPlacement = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.iconIgnoresPlacement = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.iconIgnoresPlacement = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// icon-image
{
XCTAssertTrue(rawLayer->getIconImage().isUndefined(),
@"icon-image should be unset initially.");
- MGLStyleValue<NSString *> *defaultStyleValue = layer.iconImageName;
+ NSExpression *defaultExpression = layer.iconImageName;
- MGLStyleValue<NSString *> *constantStyleValue = [MGLStyleValue<NSString *> valueWithRawValue:@"Icon Image"];
- layer.iconImageName = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'Icon Image'"];
+ layer.iconImageName = constantExpression;
mbgl::style::DataDrivenPropertyValue<std::string> propertyValue = { "Icon Image" };
XCTAssertEqual(rawLayer->getIconImage(), propertyValue,
- @"Setting iconImageName to a constant value should update icon-image.");
- XCTAssertEqualObjects(layer.iconImageName, constantStyleValue,
- @"iconImageName should round-trip constant values.");
-
- MGLStyleValue<NSString *> * functionStyleValue = [MGLStyleValue<NSString *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.iconImageName = functionStyleValue;
-
- mbgl::style::IntervalStops<std::string> intervalStops = { {{18, "Icon Image"}} };
+ @"Setting iconImageName to a constant value expression should update icon-image.");
+ XCTAssertEqualObjects(layer.iconImageName, constantExpression,
+ @"iconImageName should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"'Icon Image'"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.iconImageName = functionExpression;
+
+ mbgl::style::IntervalStops<std::string> intervalStops = {{
+ { -INFINITY, "Icon Image" },
+ { 18, "Icon Image" },
+ }};
propertyValue = mbgl::style::CameraFunction<std::string> { intervalStops };
XCTAssertEqual(rawLayer->getIconImage(), propertyValue,
- @"Setting iconImageName to a camera function should update icon-image.");
- XCTAssertEqualObjects(layer.iconImageName, functionStyleValue,
- @"iconImageName should round-trip camera functions.");
+ @"Setting iconImageName to a camera expression should update icon-image.");
+ XCTAssertEqualObjects(layer.iconImageName, functionExpression,
+ @"iconImageName should round-trip camera expressions.");
layer.iconImageName = nil;
XCTAssertTrue(rawLayer->getIconImage().isUndefined(),
@"Unsetting iconImageName should return icon-image to the default value.");
- XCTAssertEqualObjects(layer.iconImageName, defaultStyleValue,
+ XCTAssertEqualObjects(layer.iconImageName, defaultExpression,
@"iconImageName should return the default value after being unset.");
}
@@ -198,46 +216,50 @@
{
XCTAssertTrue(rawLayer->getIconOffset().isUndefined(),
@"icon-offset should be unset initially.");
- MGLStyleValue<NSValue *> *defaultStyleValue = layer.iconOffset;
+ NSExpression *defaultExpression = layer.iconOffset;
- MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@",
#if TARGET_OS_IPHONE
[NSValue valueWithCGVector:CGVectorMake(1, 1)]
#else
[NSValue valueWithMGLVector:CGVectorMake(1, -1)]
#endif
];
- layer.iconOffset = constantStyleValue;
+ layer.iconOffset = constantExpression;
mbgl::style::DataDrivenPropertyValue<std::array<float, 2>> propertyValue = { { 1, 1 } };
XCTAssertEqual(rawLayer->getIconOffset(), propertyValue,
- @"Setting iconOffset to a constant value should update icon-offset.");
- XCTAssertEqualObjects(layer.iconOffset, constantStyleValue,
- @"iconOffset should round-trip constant values.");
-
- MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.iconOffset = functionStyleValue;
-
- mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = { {{18, { 1, 1 }}} };
+ @"Setting iconOffset to a constant value expression should update icon-offset.");
+ XCTAssertEqualObjects(layer.iconOffset, constantExpression,
+ @"iconOffset should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"{1, 1}"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.iconOffset = functionExpression;
+
+ mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = {{
+ { -INFINITY, { 1, 1 } },
+ { 18, { 1, 1 } },
+ }};
propertyValue = mbgl::style::CameraFunction<std::array<float, 2>> { intervalStops };
XCTAssertEqual(rawLayer->getIconOffset(), propertyValue,
- @"Setting iconOffset to a camera function should update icon-offset.");
- XCTAssertEqualObjects(layer.iconOffset, functionStyleValue,
- @"iconOffset should round-trip camera functions.");
+ @"Setting iconOffset to a camera expression should update icon-offset.");
+ XCTAssertEqualObjects(layer.iconOffset, functionExpression,
+ @"iconOffset should round-trip camera expressions.");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
- layer.iconOffset = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.iconOffset = functionExpression;
mbgl::style::ExponentialStops<std::array<float, 2>> exponentialStops = { {{18, { 1, 1 }}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<std::array<float, 2>> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getIconOffset(), propertyValue,
- @"Setting iconOffset to a source function should update icon-offset.");
- XCTAssertEqualObjects(layer.iconOffset, functionStyleValue,
- @"iconOffset should round-trip source functions.");
+ @"Setting iconOffset to a data expression should update icon-offset.");
+ XCTAssertEqualObjects(layer.iconOffset, functionExpression,
+ @"iconOffset should round-trip data expressions.");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
- layer.iconOffset = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.iconOffset = functionExpression;
std::map<float, std::array<float, 2>> innerStops { {18, { 1, 1 }} };
mbgl::style::CompositeExponentialStops<std::array<float, 2>> compositeStops { { {10.0, innerStops} }, 1.0 };
@@ -245,15 +267,15 @@
propertyValue = mbgl::style::CompositeFunction<std::array<float, 2>> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getIconOffset(), propertyValue,
- @"Setting iconOffset to a composite function should update icon-offset.");
- XCTAssertEqualObjects(layer.iconOffset, functionStyleValue,
- @"iconOffset should round-trip composite functions.");
+ @"Setting iconOffset to a camera-data expression should update icon-offset.");
+ XCTAssertEqualObjects(layer.iconOffset, functionExpression,
+ @"iconOffset should round-trip camera-data expressions.");
layer.iconOffset = nil;
XCTAssertTrue(rawLayer->getIconOffset().isUndefined(),
@"Unsetting iconOffset should return icon-offset to the default value.");
- XCTAssertEqualObjects(layer.iconOffset, defaultStyleValue,
+ XCTAssertEqualObjects(layer.iconOffset, defaultExpression,
@"iconOffset should return the default value after being unset.");
}
@@ -261,157 +283,176 @@
{
XCTAssertTrue(rawLayer->getIconOptional().isUndefined(),
@"icon-optional should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.iconOptional;
+ NSExpression *defaultExpression = layer.iconOptional;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@YES];
- layer.iconOptional = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"true"];
+ layer.iconOptional = constantExpression;
mbgl::style::PropertyValue<bool> propertyValue = { true };
XCTAssertEqual(rawLayer->getIconOptional(), propertyValue,
- @"Setting iconOptional to a constant value should update icon-optional.");
- XCTAssertEqualObjects(layer.iconOptional, constantStyleValue,
- @"iconOptional should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.iconOptional = functionStyleValue;
-
- mbgl::style::IntervalStops<bool> intervalStops = { {{18, true}} };
+ @"Setting iconOptional to a constant value expression should update icon-optional.");
+ XCTAssertEqualObjects(layer.iconOptional, constantExpression,
+ @"iconOptional should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"true"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.iconOptional = functionExpression;
+
+ mbgl::style::IntervalStops<bool> intervalStops = {{
+ { -INFINITY, true },
+ { 18, true },
+ }};
propertyValue = mbgl::style::CameraFunction<bool> { intervalStops };
XCTAssertEqual(rawLayer->getIconOptional(), propertyValue,
- @"Setting iconOptional to a camera function should update icon-optional.");
- XCTAssertEqualObjects(layer.iconOptional, functionStyleValue,
- @"iconOptional should round-trip camera functions.");
+ @"Setting iconOptional to a camera expression should update icon-optional.");
+ XCTAssertEqualObjects(layer.iconOptional, functionExpression,
+ @"iconOptional should round-trip camera expressions.");
layer.iconOptional = nil;
XCTAssertTrue(rawLayer->getIconOptional().isUndefined(),
@"Unsetting iconOptional should return icon-optional to the default value.");
- XCTAssertEqualObjects(layer.iconOptional, defaultStyleValue,
+ XCTAssertEqualObjects(layer.iconOptional, defaultExpression,
@"iconOptional should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.iconOptional = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.iconOptional = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.iconOptional = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.iconOptional = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// icon-padding
{
XCTAssertTrue(rawLayer->getIconPadding().isUndefined(),
@"icon-padding should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.iconPadding;
+ NSExpression *defaultExpression = layer.iconPadding;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.iconPadding = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.iconPadding = constantExpression;
mbgl::style::PropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getIconPadding(), propertyValue,
- @"Setting iconPadding to a constant value should update icon-padding.");
- XCTAssertEqualObjects(layer.iconPadding, constantStyleValue,
- @"iconPadding should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.iconPadding = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting iconPadding to a constant value expression should update icon-padding.");
+ XCTAssertEqualObjects(layer.iconPadding, constantExpression,
+ @"iconPadding should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.iconPadding = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getIconPadding(), propertyValue,
- @"Setting iconPadding to a camera function should update icon-padding.");
- XCTAssertEqualObjects(layer.iconPadding, functionStyleValue,
- @"iconPadding should round-trip camera functions.");
+ @"Setting iconPadding to a camera expression should update icon-padding.");
+ XCTAssertEqualObjects(layer.iconPadding, functionExpression,
+ @"iconPadding should round-trip camera expressions.");
layer.iconPadding = nil;
XCTAssertTrue(rawLayer->getIconPadding().isUndefined(),
@"Unsetting iconPadding should return icon-padding to the default value.");
- XCTAssertEqualObjects(layer.iconPadding, defaultStyleValue,
+ XCTAssertEqualObjects(layer.iconPadding, defaultExpression,
@"iconPadding should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.iconPadding = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.iconPadding = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.iconPadding = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.iconPadding = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// icon-pitch-alignment
{
XCTAssertTrue(rawLayer->getIconPitchAlignment().isUndefined(),
@"icon-pitch-alignment should be unset initially.");
- MGLStyleValue<NSValue *> *defaultStyleValue = layer.iconPitchAlignment;
+ NSExpression *defaultExpression = layer.iconPitchAlignment;
- MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLIconPitchAlignment:MGLIconPitchAlignmentAuto]];
- layer.iconPitchAlignment = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'auto'"];
+ layer.iconPitchAlignment = constantExpression;
mbgl::style::PropertyValue<mbgl::style::AlignmentType> propertyValue = { mbgl::style::AlignmentType::Auto };
XCTAssertEqual(rawLayer->getIconPitchAlignment(), propertyValue,
- @"Setting iconPitchAlignment to a constant value should update icon-pitch-alignment.");
- XCTAssertEqualObjects(layer.iconPitchAlignment, constantStyleValue,
- @"iconPitchAlignment should round-trip constant values.");
-
- MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.iconPitchAlignment = functionStyleValue;
-
- mbgl::style::IntervalStops<mbgl::style::AlignmentType> intervalStops = { {{18, mbgl::style::AlignmentType::Auto}} };
+ @"Setting iconPitchAlignment to a constant value expression should update icon-pitch-alignment.");
+ XCTAssertEqualObjects(layer.iconPitchAlignment, constantExpression,
+ @"iconPitchAlignment should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"'auto'"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.iconPitchAlignment = functionExpression;
+
+ mbgl::style::IntervalStops<mbgl::style::AlignmentType> intervalStops = {{
+ { -INFINITY, mbgl::style::AlignmentType::Auto },
+ { 18, mbgl::style::AlignmentType::Auto },
+ }};
propertyValue = mbgl::style::CameraFunction<mbgl::style::AlignmentType> { intervalStops };
XCTAssertEqual(rawLayer->getIconPitchAlignment(), propertyValue,
- @"Setting iconPitchAlignment to a camera function should update icon-pitch-alignment.");
- XCTAssertEqualObjects(layer.iconPitchAlignment, functionStyleValue,
- @"iconPitchAlignment should round-trip camera functions.");
+ @"Setting iconPitchAlignment to a camera expression should update icon-pitch-alignment.");
+ XCTAssertEqualObjects(layer.iconPitchAlignment, functionExpression,
+ @"iconPitchAlignment should round-trip camera expressions.");
layer.iconPitchAlignment = nil;
XCTAssertTrue(rawLayer->getIconPitchAlignment().isUndefined(),
@"Unsetting iconPitchAlignment should return icon-pitch-alignment to the default value.");
- XCTAssertEqualObjects(layer.iconPitchAlignment, defaultStyleValue,
+ XCTAssertEqualObjects(layer.iconPitchAlignment, defaultExpression,
@"iconPitchAlignment should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.iconPitchAlignment = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.iconPitchAlignment = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.iconPitchAlignment = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.iconPitchAlignment = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// icon-rotate
{
XCTAssertTrue(rawLayer->getIconRotate().isUndefined(),
@"icon-rotate should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.iconRotation;
+ NSExpression *defaultExpression = layer.iconRotation;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.iconRotation = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.iconRotation = constantExpression;
mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getIconRotate(), propertyValue,
- @"Setting iconRotation to a constant value should update icon-rotate.");
- XCTAssertEqualObjects(layer.iconRotation, constantStyleValue,
- @"iconRotation should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.iconRotation = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting iconRotation to a constant value expression should update icon-rotate.");
+ XCTAssertEqualObjects(layer.iconRotation, constantExpression,
+ @"iconRotation should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.iconRotation = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getIconRotate(), propertyValue,
- @"Setting iconRotation to a camera function should update icon-rotate.");
- XCTAssertEqualObjects(layer.iconRotation, functionStyleValue,
- @"iconRotation should round-trip camera functions.");
+ @"Setting iconRotation to a camera expression should update icon-rotate.");
+ XCTAssertEqualObjects(layer.iconRotation, functionExpression,
+ @"iconRotation should round-trip camera expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
- layer.iconRotation = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.iconRotation = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getIconRotate(), propertyValue,
- @"Setting iconRotation to a source function should update icon-rotate.");
- XCTAssertEqualObjects(layer.iconRotation, functionStyleValue,
- @"iconRotation should round-trip source functions.");
+ @"Setting iconRotation to a data expression should update icon-rotate.");
+ XCTAssertEqualObjects(layer.iconRotation, functionExpression,
+ @"iconRotation should round-trip data expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
- layer.iconRotation = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.iconRotation = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 };
@@ -419,15 +460,15 @@
propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getIconRotate(), propertyValue,
- @"Setting iconRotation to a composite function should update icon-rotate.");
- XCTAssertEqualObjects(layer.iconRotation, functionStyleValue,
- @"iconRotation should round-trip composite functions.");
+ @"Setting iconRotation to a camera-data expression should update icon-rotate.");
+ XCTAssertEqualObjects(layer.iconRotation, functionExpression,
+ @"iconRotation should round-trip camera-data expressions.");
layer.iconRotation = nil;
XCTAssertTrue(rawLayer->getIconRotate().isUndefined(),
@"Unsetting iconRotation should return icon-rotate to the default value.");
- XCTAssertEqualObjects(layer.iconRotation, defaultStyleValue,
+ XCTAssertEqualObjects(layer.iconRotation, defaultExpression,
@"iconRotation should return the default value after being unset.");
}
@@ -435,79 +476,88 @@
{
XCTAssertTrue(rawLayer->getIconRotationAlignment().isUndefined(),
@"icon-rotation-alignment should be unset initially.");
- MGLStyleValue<NSValue *> *defaultStyleValue = layer.iconRotationAlignment;
+ NSExpression *defaultExpression = layer.iconRotationAlignment;
- MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLIconRotationAlignment:MGLIconRotationAlignmentAuto]];
- layer.iconRotationAlignment = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'auto'"];
+ layer.iconRotationAlignment = constantExpression;
mbgl::style::PropertyValue<mbgl::style::AlignmentType> propertyValue = { mbgl::style::AlignmentType::Auto };
XCTAssertEqual(rawLayer->getIconRotationAlignment(), propertyValue,
- @"Setting iconRotationAlignment to a constant value should update icon-rotation-alignment.");
- XCTAssertEqualObjects(layer.iconRotationAlignment, constantStyleValue,
- @"iconRotationAlignment should round-trip constant values.");
-
- MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.iconRotationAlignment = functionStyleValue;
-
- mbgl::style::IntervalStops<mbgl::style::AlignmentType> intervalStops = { {{18, mbgl::style::AlignmentType::Auto}} };
+ @"Setting iconRotationAlignment to a constant value expression should update icon-rotation-alignment.");
+ XCTAssertEqualObjects(layer.iconRotationAlignment, constantExpression,
+ @"iconRotationAlignment should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"'auto'"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.iconRotationAlignment = functionExpression;
+
+ mbgl::style::IntervalStops<mbgl::style::AlignmentType> intervalStops = {{
+ { -INFINITY, mbgl::style::AlignmentType::Auto },
+ { 18, mbgl::style::AlignmentType::Auto },
+ }};
propertyValue = mbgl::style::CameraFunction<mbgl::style::AlignmentType> { intervalStops };
XCTAssertEqual(rawLayer->getIconRotationAlignment(), propertyValue,
- @"Setting iconRotationAlignment to a camera function should update icon-rotation-alignment.");
- XCTAssertEqualObjects(layer.iconRotationAlignment, functionStyleValue,
- @"iconRotationAlignment should round-trip camera functions.");
+ @"Setting iconRotationAlignment to a camera expression should update icon-rotation-alignment.");
+ XCTAssertEqualObjects(layer.iconRotationAlignment, functionExpression,
+ @"iconRotationAlignment should round-trip camera expressions.");
layer.iconRotationAlignment = nil;
XCTAssertTrue(rawLayer->getIconRotationAlignment().isUndefined(),
@"Unsetting iconRotationAlignment should return icon-rotation-alignment to the default value.");
- XCTAssertEqualObjects(layer.iconRotationAlignment, defaultStyleValue,
+ XCTAssertEqualObjects(layer.iconRotationAlignment, defaultExpression,
@"iconRotationAlignment should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.iconRotationAlignment = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.iconRotationAlignment = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.iconRotationAlignment = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.iconRotationAlignment = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// icon-size
{
XCTAssertTrue(rawLayer->getIconSize().isUndefined(),
@"icon-size should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.iconScale;
+ NSExpression *defaultExpression = layer.iconScale;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.iconScale = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.iconScale = constantExpression;
mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getIconSize(), propertyValue,
- @"Setting iconScale to a constant value should update icon-size.");
- XCTAssertEqualObjects(layer.iconScale, constantStyleValue,
- @"iconScale should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.iconScale = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting iconScale to a constant value expression should update icon-size.");
+ XCTAssertEqualObjects(layer.iconScale, constantExpression,
+ @"iconScale should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.iconScale = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getIconSize(), propertyValue,
- @"Setting iconScale to a camera function should update icon-size.");
- XCTAssertEqualObjects(layer.iconScale, functionStyleValue,
- @"iconScale should round-trip camera functions.");
+ @"Setting iconScale to a camera expression should update icon-size.");
+ XCTAssertEqualObjects(layer.iconScale, functionExpression,
+ @"iconScale should round-trip camera expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
- layer.iconScale = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.iconScale = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getIconSize(), propertyValue,
- @"Setting iconScale to a source function should update icon-size.");
- XCTAssertEqualObjects(layer.iconScale, functionStyleValue,
- @"iconScale should round-trip source functions.");
+ @"Setting iconScale to a data expression should update icon-size.");
+ XCTAssertEqualObjects(layer.iconScale, functionExpression,
+ @"iconScale should round-trip data expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
- layer.iconScale = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.iconScale = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 };
@@ -515,15 +565,15 @@
propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getIconSize(), propertyValue,
- @"Setting iconScale to a composite function should update icon-size.");
- XCTAssertEqualObjects(layer.iconScale, functionStyleValue,
- @"iconScale should round-trip composite functions.");
+ @"Setting iconScale to a camera-data expression should update icon-size.");
+ XCTAssertEqualObjects(layer.iconScale, functionExpression,
+ @"iconScale should round-trip camera-data expressions.");
layer.iconScale = nil;
XCTAssertTrue(rawLayer->getIconSize().isUndefined(),
@"Unsetting iconScale should return icon-size to the default value.");
- XCTAssertEqualObjects(layer.iconScale, defaultStyleValue,
+ XCTAssertEqualObjects(layer.iconScale, defaultExpression,
@"iconScale should return the default value after being unset.");
}
@@ -531,241 +581,270 @@
{
XCTAssertTrue(rawLayer->getIconTextFit().isUndefined(),
@"icon-text-fit should be unset initially.");
- MGLStyleValue<NSValue *> *defaultStyleValue = layer.iconTextFit;
+ NSExpression *defaultExpression = layer.iconTextFit;
- MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLIconTextFit:MGLIconTextFitBoth]];
- layer.iconTextFit = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'both'"];
+ layer.iconTextFit = constantExpression;
mbgl::style::PropertyValue<mbgl::style::IconTextFitType> propertyValue = { mbgl::style::IconTextFitType::Both };
XCTAssertEqual(rawLayer->getIconTextFit(), propertyValue,
- @"Setting iconTextFit to a constant value should update icon-text-fit.");
- XCTAssertEqualObjects(layer.iconTextFit, constantStyleValue,
- @"iconTextFit should round-trip constant values.");
-
- MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.iconTextFit = functionStyleValue;
-
- mbgl::style::IntervalStops<mbgl::style::IconTextFitType> intervalStops = { {{18, mbgl::style::IconTextFitType::Both}} };
+ @"Setting iconTextFit to a constant value expression should update icon-text-fit.");
+ XCTAssertEqualObjects(layer.iconTextFit, constantExpression,
+ @"iconTextFit should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"'both'"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.iconTextFit = functionExpression;
+
+ mbgl::style::IntervalStops<mbgl::style::IconTextFitType> intervalStops = {{
+ { -INFINITY, mbgl::style::IconTextFitType::Both },
+ { 18, mbgl::style::IconTextFitType::Both },
+ }};
propertyValue = mbgl::style::CameraFunction<mbgl::style::IconTextFitType> { intervalStops };
XCTAssertEqual(rawLayer->getIconTextFit(), propertyValue,
- @"Setting iconTextFit to a camera function should update icon-text-fit.");
- XCTAssertEqualObjects(layer.iconTextFit, functionStyleValue,
- @"iconTextFit should round-trip camera functions.");
+ @"Setting iconTextFit to a camera expression should update icon-text-fit.");
+ XCTAssertEqualObjects(layer.iconTextFit, functionExpression,
+ @"iconTextFit should round-trip camera expressions.");
layer.iconTextFit = nil;
XCTAssertTrue(rawLayer->getIconTextFit().isUndefined(),
@"Unsetting iconTextFit should return icon-text-fit to the default value.");
- XCTAssertEqualObjects(layer.iconTextFit, defaultStyleValue,
+ XCTAssertEqualObjects(layer.iconTextFit, defaultExpression,
@"iconTextFit should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.iconTextFit = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.iconTextFit = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.iconTextFit = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.iconTextFit = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// icon-text-fit-padding
{
XCTAssertTrue(rawLayer->getIconTextFitPadding().isUndefined(),
@"icon-text-fit-padding should be unset initially.");
- MGLStyleValue<NSValue *> *defaultStyleValue = layer.iconTextFitPadding;
+ NSExpression *defaultExpression = layer.iconTextFitPadding;
- MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@",
#if TARGET_OS_IPHONE
[NSValue valueWithUIEdgeInsets:UIEdgeInsetsMake(1, 1, 1, 1)]
#else
[NSValue valueWithEdgeInsets:NSEdgeInsetsMake(1, 1, 1, 1)]
#endif
];
- layer.iconTextFitPadding = constantStyleValue;
+ layer.iconTextFitPadding = constantExpression;
mbgl::style::PropertyValue<std::array<float, 4>> propertyValue = { { 1, 1, 1, 1 } };
XCTAssertEqual(rawLayer->getIconTextFitPadding(), propertyValue,
- @"Setting iconTextFitPadding to a constant value should update icon-text-fit-padding.");
- XCTAssertEqualObjects(layer.iconTextFitPadding, constantStyleValue,
- @"iconTextFitPadding should round-trip constant values.");
-
- MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.iconTextFitPadding = functionStyleValue;
-
- mbgl::style::IntervalStops<std::array<float, 4>> intervalStops = { {{18, { 1, 1, 1, 1 }}} };
+ @"Setting iconTextFitPadding to a constant value expression should update icon-text-fit-padding.");
+ XCTAssertEqualObjects(layer.iconTextFitPadding, constantExpression,
+ @"iconTextFitPadding should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"{1, 1, 1, 1}"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.iconTextFitPadding = functionExpression;
+
+ mbgl::style::IntervalStops<std::array<float, 4>> intervalStops = {{
+ { -INFINITY, { 1, 1, 1, 1 } },
+ { 18, { 1, 1, 1, 1 } },
+ }};
propertyValue = mbgl::style::CameraFunction<std::array<float, 4>> { intervalStops };
XCTAssertEqual(rawLayer->getIconTextFitPadding(), propertyValue,
- @"Setting iconTextFitPadding to a camera function should update icon-text-fit-padding.");
- XCTAssertEqualObjects(layer.iconTextFitPadding, functionStyleValue,
- @"iconTextFitPadding should round-trip camera functions.");
+ @"Setting iconTextFitPadding to a camera expression should update icon-text-fit-padding.");
+ XCTAssertEqualObjects(layer.iconTextFitPadding, functionExpression,
+ @"iconTextFitPadding should round-trip camera expressions.");
layer.iconTextFitPadding = nil;
XCTAssertTrue(rawLayer->getIconTextFitPadding().isUndefined(),
@"Unsetting iconTextFitPadding should return icon-text-fit-padding to the default value.");
- XCTAssertEqualObjects(layer.iconTextFitPadding, defaultStyleValue,
+ XCTAssertEqualObjects(layer.iconTextFitPadding, defaultExpression,
@"iconTextFitPadding should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.iconTextFitPadding = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.iconTextFitPadding = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.iconTextFitPadding = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.iconTextFitPadding = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// icon-keep-upright
{
XCTAssertTrue(rawLayer->getIconKeepUpright().isUndefined(),
@"icon-keep-upright should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.keepsIconUpright;
+ NSExpression *defaultExpression = layer.keepsIconUpright;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@YES];
- layer.keepsIconUpright = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"true"];
+ layer.keepsIconUpright = constantExpression;
mbgl::style::PropertyValue<bool> propertyValue = { true };
XCTAssertEqual(rawLayer->getIconKeepUpright(), propertyValue,
- @"Setting keepsIconUpright to a constant value should update icon-keep-upright.");
- XCTAssertEqualObjects(layer.keepsIconUpright, constantStyleValue,
- @"keepsIconUpright should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.keepsIconUpright = functionStyleValue;
-
- mbgl::style::IntervalStops<bool> intervalStops = { {{18, true}} };
+ @"Setting keepsIconUpright to a constant value expression should update icon-keep-upright.");
+ XCTAssertEqualObjects(layer.keepsIconUpright, constantExpression,
+ @"keepsIconUpright should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"true"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.keepsIconUpright = functionExpression;
+
+ mbgl::style::IntervalStops<bool> intervalStops = {{
+ { -INFINITY, true },
+ { 18, true },
+ }};
propertyValue = mbgl::style::CameraFunction<bool> { intervalStops };
XCTAssertEqual(rawLayer->getIconKeepUpright(), propertyValue,
- @"Setting keepsIconUpright to a camera function should update icon-keep-upright.");
- XCTAssertEqualObjects(layer.keepsIconUpright, functionStyleValue,
- @"keepsIconUpright should round-trip camera functions.");
+ @"Setting keepsIconUpright to a camera expression should update icon-keep-upright.");
+ XCTAssertEqualObjects(layer.keepsIconUpright, functionExpression,
+ @"keepsIconUpright should round-trip camera expressions.");
layer.keepsIconUpright = nil;
XCTAssertTrue(rawLayer->getIconKeepUpright().isUndefined(),
@"Unsetting keepsIconUpright should return icon-keep-upright to the default value.");
- XCTAssertEqualObjects(layer.keepsIconUpright, defaultStyleValue,
+ XCTAssertEqualObjects(layer.keepsIconUpright, defaultExpression,
@"keepsIconUpright should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.keepsIconUpright = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.keepsIconUpright = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.keepsIconUpright = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.keepsIconUpright = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// text-keep-upright
{
XCTAssertTrue(rawLayer->getTextKeepUpright().isUndefined(),
@"text-keep-upright should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.keepsTextUpright;
+ NSExpression *defaultExpression = layer.keepsTextUpright;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@NO];
- layer.keepsTextUpright = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"false"];
+ layer.keepsTextUpright = constantExpression;
mbgl::style::PropertyValue<bool> propertyValue = { false };
XCTAssertEqual(rawLayer->getTextKeepUpright(), propertyValue,
- @"Setting keepsTextUpright to a constant value should update text-keep-upright.");
- XCTAssertEqualObjects(layer.keepsTextUpright, constantStyleValue,
- @"keepsTextUpright should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.keepsTextUpright = functionStyleValue;
-
- mbgl::style::IntervalStops<bool> intervalStops = { {{18, false}} };
+ @"Setting keepsTextUpright to a constant value expression should update text-keep-upright.");
+ XCTAssertEqualObjects(layer.keepsTextUpright, constantExpression,
+ @"keepsTextUpright should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"false"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.keepsTextUpright = functionExpression;
+
+ mbgl::style::IntervalStops<bool> intervalStops = {{
+ { -INFINITY, false },
+ { 18, false },
+ }};
propertyValue = mbgl::style::CameraFunction<bool> { intervalStops };
XCTAssertEqual(rawLayer->getTextKeepUpright(), propertyValue,
- @"Setting keepsTextUpright to a camera function should update text-keep-upright.");
- XCTAssertEqualObjects(layer.keepsTextUpright, functionStyleValue,
- @"keepsTextUpright should round-trip camera functions.");
+ @"Setting keepsTextUpright to a camera expression should update text-keep-upright.");
+ XCTAssertEqualObjects(layer.keepsTextUpright, functionExpression,
+ @"keepsTextUpright should round-trip camera expressions.");
layer.keepsTextUpright = nil;
XCTAssertTrue(rawLayer->getTextKeepUpright().isUndefined(),
@"Unsetting keepsTextUpright should return text-keep-upright to the default value.");
- XCTAssertEqualObjects(layer.keepsTextUpright, defaultStyleValue,
+ XCTAssertEqualObjects(layer.keepsTextUpright, defaultExpression,
@"keepsTextUpright should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.keepsTextUpright = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.keepsTextUpright = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.keepsTextUpright = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.keepsTextUpright = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// text-max-angle
{
XCTAssertTrue(rawLayer->getTextMaxAngle().isUndefined(),
@"text-max-angle should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.maximumTextAngle;
+ NSExpression *defaultExpression = layer.maximumTextAngle;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.maximumTextAngle = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.maximumTextAngle = constantExpression;
mbgl::style::PropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getTextMaxAngle(), propertyValue,
- @"Setting maximumTextAngle to a constant value should update text-max-angle.");
- XCTAssertEqualObjects(layer.maximumTextAngle, constantStyleValue,
- @"maximumTextAngle should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.maximumTextAngle = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting maximumTextAngle to a constant value expression should update text-max-angle.");
+ XCTAssertEqualObjects(layer.maximumTextAngle, constantExpression,
+ @"maximumTextAngle should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.maximumTextAngle = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getTextMaxAngle(), propertyValue,
- @"Setting maximumTextAngle to a camera function should update text-max-angle.");
- XCTAssertEqualObjects(layer.maximumTextAngle, functionStyleValue,
- @"maximumTextAngle should round-trip camera functions.");
+ @"Setting maximumTextAngle to a camera expression should update text-max-angle.");
+ XCTAssertEqualObjects(layer.maximumTextAngle, functionExpression,
+ @"maximumTextAngle should round-trip camera expressions.");
layer.maximumTextAngle = nil;
XCTAssertTrue(rawLayer->getTextMaxAngle().isUndefined(),
@"Unsetting maximumTextAngle should return text-max-angle to the default value.");
- XCTAssertEqualObjects(layer.maximumTextAngle, defaultStyleValue,
+ XCTAssertEqualObjects(layer.maximumTextAngle, defaultExpression,
@"maximumTextAngle should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.maximumTextAngle = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.maximumTextAngle = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.maximumTextAngle = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.maximumTextAngle = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// text-max-width
{
XCTAssertTrue(rawLayer->getTextMaxWidth().isUndefined(),
@"text-max-width should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.maximumTextWidth;
+ NSExpression *defaultExpression = layer.maximumTextWidth;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.maximumTextWidth = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.maximumTextWidth = constantExpression;
mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getTextMaxWidth(), propertyValue,
- @"Setting maximumTextWidth to a constant value should update text-max-width.");
- XCTAssertEqualObjects(layer.maximumTextWidth, constantStyleValue,
- @"maximumTextWidth should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.maximumTextWidth = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting maximumTextWidth to a constant value expression should update text-max-width.");
+ XCTAssertEqualObjects(layer.maximumTextWidth, constantExpression,
+ @"maximumTextWidth should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.maximumTextWidth = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getTextMaxWidth(), propertyValue,
- @"Setting maximumTextWidth to a camera function should update text-max-width.");
- XCTAssertEqualObjects(layer.maximumTextWidth, functionStyleValue,
- @"maximumTextWidth should round-trip camera functions.");
+ @"Setting maximumTextWidth to a camera expression should update text-max-width.");
+ XCTAssertEqualObjects(layer.maximumTextWidth, functionExpression,
+ @"maximumTextWidth should round-trip camera expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
- layer.maximumTextWidth = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.maximumTextWidth = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getTextMaxWidth(), propertyValue,
- @"Setting maximumTextWidth to a source function should update text-max-width.");
- XCTAssertEqualObjects(layer.maximumTextWidth, functionStyleValue,
- @"maximumTextWidth should round-trip source functions.");
+ @"Setting maximumTextWidth to a data expression should update text-max-width.");
+ XCTAssertEqualObjects(layer.maximumTextWidth, functionExpression,
+ @"maximumTextWidth should round-trip data expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
- layer.maximumTextWidth = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.maximumTextWidth = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 };
@@ -773,15 +852,15 @@
propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getTextMaxWidth(), propertyValue,
- @"Setting maximumTextWidth to a composite function should update text-max-width.");
- XCTAssertEqualObjects(layer.maximumTextWidth, functionStyleValue,
- @"maximumTextWidth should round-trip composite functions.");
+ @"Setting maximumTextWidth to a camera-data expression should update text-max-width.");
+ XCTAssertEqualObjects(layer.maximumTextWidth, functionExpression,
+ @"maximumTextWidth should round-trip camera-data expressions.");
layer.maximumTextWidth = nil;
XCTAssertTrue(rawLayer->getTextMaxWidth().isUndefined(),
@"Unsetting maximumTextWidth should return text-max-width to the default value.");
- XCTAssertEqualObjects(layer.maximumTextWidth, defaultStyleValue,
+ XCTAssertEqualObjects(layer.maximumTextWidth, defaultExpression,
@"maximumTextWidth should return the default value after being unset.");
}
@@ -789,150 +868,169 @@
{
XCTAssertTrue(rawLayer->getSymbolAvoidEdges().isUndefined(),
@"symbol-avoid-edges should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.symbolAvoidsEdges;
+ NSExpression *defaultExpression = layer.symbolAvoidsEdges;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@YES];
- layer.symbolAvoidsEdges = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"true"];
+ layer.symbolAvoidsEdges = constantExpression;
mbgl::style::PropertyValue<bool> propertyValue = { true };
XCTAssertEqual(rawLayer->getSymbolAvoidEdges(), propertyValue,
- @"Setting symbolAvoidsEdges to a constant value should update symbol-avoid-edges.");
- XCTAssertEqualObjects(layer.symbolAvoidsEdges, constantStyleValue,
- @"symbolAvoidsEdges should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.symbolAvoidsEdges = functionStyleValue;
-
- mbgl::style::IntervalStops<bool> intervalStops = { {{18, true}} };
+ @"Setting symbolAvoidsEdges to a constant value expression should update symbol-avoid-edges.");
+ XCTAssertEqualObjects(layer.symbolAvoidsEdges, constantExpression,
+ @"symbolAvoidsEdges should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"true"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.symbolAvoidsEdges = functionExpression;
+
+ mbgl::style::IntervalStops<bool> intervalStops = {{
+ { -INFINITY, true },
+ { 18, true },
+ }};
propertyValue = mbgl::style::CameraFunction<bool> { intervalStops };
XCTAssertEqual(rawLayer->getSymbolAvoidEdges(), propertyValue,
- @"Setting symbolAvoidsEdges to a camera function should update symbol-avoid-edges.");
- XCTAssertEqualObjects(layer.symbolAvoidsEdges, functionStyleValue,
- @"symbolAvoidsEdges should round-trip camera functions.");
+ @"Setting symbolAvoidsEdges to a camera expression should update symbol-avoid-edges.");
+ XCTAssertEqualObjects(layer.symbolAvoidsEdges, functionExpression,
+ @"symbolAvoidsEdges should round-trip camera expressions.");
layer.symbolAvoidsEdges = nil;
XCTAssertTrue(rawLayer->getSymbolAvoidEdges().isUndefined(),
@"Unsetting symbolAvoidsEdges should return symbol-avoid-edges to the default value.");
- XCTAssertEqualObjects(layer.symbolAvoidsEdges, defaultStyleValue,
+ XCTAssertEqualObjects(layer.symbolAvoidsEdges, defaultExpression,
@"symbolAvoidsEdges should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.symbolAvoidsEdges = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.symbolAvoidsEdges = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.symbolAvoidsEdges = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.symbolAvoidsEdges = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// symbol-placement
{
XCTAssertTrue(rawLayer->getSymbolPlacement().isUndefined(),
@"symbol-placement should be unset initially.");
- MGLStyleValue<NSValue *> *defaultStyleValue = layer.symbolPlacement;
+ NSExpression *defaultExpression = layer.symbolPlacement;
- MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLSymbolPlacement:MGLSymbolPlacementLine]];
- layer.symbolPlacement = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'line'"];
+ layer.symbolPlacement = constantExpression;
mbgl::style::PropertyValue<mbgl::style::SymbolPlacementType> propertyValue = { mbgl::style::SymbolPlacementType::Line };
XCTAssertEqual(rawLayer->getSymbolPlacement(), propertyValue,
- @"Setting symbolPlacement to a constant value should update symbol-placement.");
- XCTAssertEqualObjects(layer.symbolPlacement, constantStyleValue,
- @"symbolPlacement should round-trip constant values.");
-
- MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.symbolPlacement = functionStyleValue;
-
- mbgl::style::IntervalStops<mbgl::style::SymbolPlacementType> intervalStops = { {{18, mbgl::style::SymbolPlacementType::Line}} };
+ @"Setting symbolPlacement to a constant value expression should update symbol-placement.");
+ XCTAssertEqualObjects(layer.symbolPlacement, constantExpression,
+ @"symbolPlacement should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"'line'"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.symbolPlacement = functionExpression;
+
+ mbgl::style::IntervalStops<mbgl::style::SymbolPlacementType> intervalStops = {{
+ { -INFINITY, mbgl::style::SymbolPlacementType::Line },
+ { 18, mbgl::style::SymbolPlacementType::Line },
+ }};
propertyValue = mbgl::style::CameraFunction<mbgl::style::SymbolPlacementType> { intervalStops };
XCTAssertEqual(rawLayer->getSymbolPlacement(), propertyValue,
- @"Setting symbolPlacement to a camera function should update symbol-placement.");
- XCTAssertEqualObjects(layer.symbolPlacement, functionStyleValue,
- @"symbolPlacement should round-trip camera functions.");
+ @"Setting symbolPlacement to a camera expression should update symbol-placement.");
+ XCTAssertEqualObjects(layer.symbolPlacement, functionExpression,
+ @"symbolPlacement should round-trip camera expressions.");
layer.symbolPlacement = nil;
XCTAssertTrue(rawLayer->getSymbolPlacement().isUndefined(),
@"Unsetting symbolPlacement should return symbol-placement to the default value.");
- XCTAssertEqualObjects(layer.symbolPlacement, defaultStyleValue,
+ XCTAssertEqualObjects(layer.symbolPlacement, defaultExpression,
@"symbolPlacement should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.symbolPlacement = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.symbolPlacement = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.symbolPlacement = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.symbolPlacement = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// symbol-spacing
{
XCTAssertTrue(rawLayer->getSymbolSpacing().isUndefined(),
@"symbol-spacing should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.symbolSpacing;
+ NSExpression *defaultExpression = layer.symbolSpacing;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.symbolSpacing = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.symbolSpacing = constantExpression;
mbgl::style::PropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getSymbolSpacing(), propertyValue,
- @"Setting symbolSpacing to a constant value should update symbol-spacing.");
- XCTAssertEqualObjects(layer.symbolSpacing, constantStyleValue,
- @"symbolSpacing should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.symbolSpacing = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting symbolSpacing to a constant value expression should update symbol-spacing.");
+ XCTAssertEqualObjects(layer.symbolSpacing, constantExpression,
+ @"symbolSpacing should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.symbolSpacing = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getSymbolSpacing(), propertyValue,
- @"Setting symbolSpacing to a camera function should update symbol-spacing.");
- XCTAssertEqualObjects(layer.symbolSpacing, functionStyleValue,
- @"symbolSpacing should round-trip camera functions.");
+ @"Setting symbolSpacing to a camera expression should update symbol-spacing.");
+ XCTAssertEqualObjects(layer.symbolSpacing, functionExpression,
+ @"symbolSpacing should round-trip camera expressions.");
layer.symbolSpacing = nil;
XCTAssertTrue(rawLayer->getSymbolSpacing().isUndefined(),
@"Unsetting symbolSpacing should return symbol-spacing to the default value.");
- XCTAssertEqualObjects(layer.symbolSpacing, defaultStyleValue,
+ XCTAssertEqualObjects(layer.symbolSpacing, defaultExpression,
@"symbolSpacing should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.symbolSpacing = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.symbolSpacing = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.symbolSpacing = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.symbolSpacing = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// text-field
{
XCTAssertTrue(rawLayer->getTextField().isUndefined(),
@"text-field should be unset initially.");
- MGLStyleValue<NSString *> *defaultStyleValue = layer.text;
+ NSExpression *defaultExpression = layer.text;
- MGLStyleValue<NSString *> *constantStyleValue = [MGLStyleValue<NSString *> valueWithRawValue:@"Text Field"];
- layer.text = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'Text Field'"];
+ layer.text = constantExpression;
mbgl::style::DataDrivenPropertyValue<std::string> propertyValue = { "Text Field" };
XCTAssertEqual(rawLayer->getTextField(), propertyValue,
- @"Setting text to a constant value should update text-field.");
- XCTAssertEqualObjects(layer.text, constantStyleValue,
- @"text should round-trip constant values.");
-
- MGLStyleValue<NSString *> * functionStyleValue = [MGLStyleValue<NSString *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.text = functionStyleValue;
-
- mbgl::style::IntervalStops<std::string> intervalStops = { {{18, "Text Field"}} };
+ @"Setting text to a constant value expression should update text-field.");
+ XCTAssertEqualObjects(layer.text, constantExpression,
+ @"text should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"'Text Field'"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.text = functionExpression;
+
+ mbgl::style::IntervalStops<std::string> intervalStops = {{
+ { -INFINITY, "Text Field" },
+ { 18, "Text Field" },
+ }};
propertyValue = mbgl::style::CameraFunction<std::string> { intervalStops };
XCTAssertEqual(rawLayer->getTextField(), propertyValue,
- @"Setting text to a camera function should update text-field.");
- XCTAssertEqualObjects(layer.text, functionStyleValue,
- @"text should round-trip camera functions.");
+ @"Setting text to a camera expression should update text-field.");
+ XCTAssertEqualObjects(layer.text, functionExpression,
+ @"text should round-trip camera expressions.");
layer.text = nil;
XCTAssertTrue(rawLayer->getTextField().isUndefined(),
@"Unsetting text should return text-field to the default value.");
- XCTAssertEqualObjects(layer.text, defaultStyleValue,
+ XCTAssertEqualObjects(layer.text, defaultExpression,
@"text should return the default value after being unset.");
}
@@ -940,72 +1038,81 @@
{
XCTAssertTrue(rawLayer->getTextAllowOverlap().isUndefined(),
@"text-allow-overlap should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.textAllowsOverlap;
+ NSExpression *defaultExpression = layer.textAllowsOverlap;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@YES];
- layer.textAllowsOverlap = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"true"];
+ layer.textAllowsOverlap = constantExpression;
mbgl::style::PropertyValue<bool> propertyValue = { true };
XCTAssertEqual(rawLayer->getTextAllowOverlap(), propertyValue,
- @"Setting textAllowsOverlap to a constant value should update text-allow-overlap.");
- XCTAssertEqualObjects(layer.textAllowsOverlap, constantStyleValue,
- @"textAllowsOverlap should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.textAllowsOverlap = functionStyleValue;
-
- mbgl::style::IntervalStops<bool> intervalStops = { {{18, true}} };
+ @"Setting textAllowsOverlap to a constant value expression should update text-allow-overlap.");
+ XCTAssertEqualObjects(layer.textAllowsOverlap, constantExpression,
+ @"textAllowsOverlap should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"true"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.textAllowsOverlap = functionExpression;
+
+ mbgl::style::IntervalStops<bool> intervalStops = {{
+ { -INFINITY, true },
+ { 18, true },
+ }};
propertyValue = mbgl::style::CameraFunction<bool> { intervalStops };
XCTAssertEqual(rawLayer->getTextAllowOverlap(), propertyValue,
- @"Setting textAllowsOverlap to a camera function should update text-allow-overlap.");
- XCTAssertEqualObjects(layer.textAllowsOverlap, functionStyleValue,
- @"textAllowsOverlap should round-trip camera functions.");
+ @"Setting textAllowsOverlap to a camera expression should update text-allow-overlap.");
+ XCTAssertEqualObjects(layer.textAllowsOverlap, functionExpression,
+ @"textAllowsOverlap should round-trip camera expressions.");
layer.textAllowsOverlap = nil;
XCTAssertTrue(rawLayer->getTextAllowOverlap().isUndefined(),
@"Unsetting textAllowsOverlap should return text-allow-overlap to the default value.");
- XCTAssertEqualObjects(layer.textAllowsOverlap, defaultStyleValue,
+ XCTAssertEqualObjects(layer.textAllowsOverlap, defaultExpression,
@"textAllowsOverlap should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.textAllowsOverlap = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.textAllowsOverlap = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.textAllowsOverlap = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.textAllowsOverlap = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// text-anchor
{
XCTAssertTrue(rawLayer->getTextAnchor().isUndefined(),
@"text-anchor should be unset initially.");
- MGLStyleValue<NSValue *> *defaultStyleValue = layer.textAnchor;
+ NSExpression *defaultExpression = layer.textAnchor;
- MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLTextAnchor:MGLTextAnchorBottomRight]];
- layer.textAnchor = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'bottom-right'"];
+ layer.textAnchor = constantExpression;
mbgl::style::DataDrivenPropertyValue<mbgl::style::SymbolAnchorType> propertyValue = { mbgl::style::SymbolAnchorType::BottomRight };
XCTAssertEqual(rawLayer->getTextAnchor(), propertyValue,
- @"Setting textAnchor to a constant value should update text-anchor.");
- XCTAssertEqualObjects(layer.textAnchor, constantStyleValue,
- @"textAnchor should round-trip constant values.");
-
- MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.textAnchor = functionStyleValue;
-
- mbgl::style::IntervalStops<mbgl::style::SymbolAnchorType> intervalStops = { {{18, mbgl::style::SymbolAnchorType::BottomRight}} };
+ @"Setting textAnchor to a constant value expression should update text-anchor.");
+ XCTAssertEqualObjects(layer.textAnchor, constantExpression,
+ @"textAnchor should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"'bottom-right'"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.textAnchor = functionExpression;
+
+ mbgl::style::IntervalStops<mbgl::style::SymbolAnchorType> intervalStops = {{
+ { -INFINITY, mbgl::style::SymbolAnchorType::BottomRight },
+ { 18, mbgl::style::SymbolAnchorType::BottomRight },
+ }};
propertyValue = mbgl::style::CameraFunction<mbgl::style::SymbolAnchorType> { intervalStops };
XCTAssertEqual(rawLayer->getTextAnchor(), propertyValue,
- @"Setting textAnchor to a camera function should update text-anchor.");
- XCTAssertEqualObjects(layer.textAnchor, functionStyleValue,
- @"textAnchor should round-trip camera functions.");
+ @"Setting textAnchor to a camera expression should update text-anchor.");
+ XCTAssertEqualObjects(layer.textAnchor, functionExpression,
+ @"textAnchor should round-trip camera expressions.");
layer.textAnchor = nil;
XCTAssertTrue(rawLayer->getTextAnchor().isUndefined(),
@"Unsetting textAnchor should return text-anchor to the default value.");
- XCTAssertEqualObjects(layer.textAnchor, defaultStyleValue,
+ XCTAssertEqualObjects(layer.textAnchor, defaultExpression,
@"textAnchor should return the default value after being unset.");
}
@@ -1013,79 +1120,82 @@
{
XCTAssertTrue(rawLayer->getTextFont().isUndefined(),
@"text-font should be unset initially.");
- MGLStyleValue<NSArray<NSString *> *> *defaultStyleValue = layer.textFontNames;
+ NSExpression *defaultExpression = layer.textFontNames;
- MGLStyleValue<NSArray<NSString *> *> *constantStyleValue = [MGLStyleValue<NSArray<NSString *> *> valueWithRawValue:@[@"Text Font", @"Tnof Txet"]];
- layer.textFontNames = constantStyleValue;
- mbgl::style::PropertyValue<std::vector<std::string>> propertyValue = { { "Text Font", "Tnof Txet" } };
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"{'Text Font', 'Tnof Txet'}"];
+ layer.textFontNames = constantExpression;
+ mbgl::style::DataDrivenPropertyValue<std::vector<std::string>> propertyValue = { { "Text Font", "Tnof Txet" } };
XCTAssertEqual(rawLayer->getTextFont(), propertyValue,
- @"Setting textFontNames to a constant value should update text-font.");
- XCTAssertEqualObjects(layer.textFontNames, constantStyleValue,
- @"textFontNames should round-trip constant values.");
-
- MGLStyleValue<NSArray<NSString *> *> * functionStyleValue = [MGLStyleValue<NSArray<NSString *> *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.textFontNames = functionStyleValue;
-
- mbgl::style::IntervalStops<std::vector<std::string>> intervalStops = { {{18, { "Text Font", "Tnof Txet" }}} };
+ @"Setting textFontNames to a constant value expression should update text-font.");
+ XCTAssertEqualObjects(layer.textFontNames, constantExpression,
+ @"textFontNames should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"{'Text Font', 'Tnof Txet'}"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.textFontNames = functionExpression;
+
+ mbgl::style::IntervalStops<std::vector<std::string>> intervalStops = {{
+ { -INFINITY, { "Text Font", "Tnof Txet" } },
+ { 18, { "Text Font", "Tnof Txet" } },
+ }};
propertyValue = mbgl::style::CameraFunction<std::vector<std::string>> { intervalStops };
XCTAssertEqual(rawLayer->getTextFont(), propertyValue,
- @"Setting textFontNames to a camera function should update text-font.");
- XCTAssertEqualObjects(layer.textFontNames, functionStyleValue,
- @"textFontNames should round-trip camera functions.");
+ @"Setting textFontNames to a camera expression should update text-font.");
+ XCTAssertEqualObjects(layer.textFontNames, functionExpression,
+ @"textFontNames should round-trip camera expressions.");
layer.textFontNames = nil;
XCTAssertTrue(rawLayer->getTextFont().isUndefined(),
@"Unsetting textFontNames should return text-font to the default value.");
- XCTAssertEqualObjects(layer.textFontNames, defaultStyleValue,
+ XCTAssertEqualObjects(layer.textFontNames, defaultExpression,
@"textFontNames should return the default value after being unset.");
-
- functionStyleValue = [MGLStyleValue<NSArray<NSString *> *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.textFontNames = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSArray<NSString *> *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.textFontNames = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
}
// text-size
{
XCTAssertTrue(rawLayer->getTextSize().isUndefined(),
@"text-size should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.textFontSize;
+ NSExpression *defaultExpression = layer.textFontSize;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.textFontSize = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.textFontSize = constantExpression;
mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getTextSize(), propertyValue,
- @"Setting textFontSize to a constant value should update text-size.");
- XCTAssertEqualObjects(layer.textFontSize, constantStyleValue,
- @"textFontSize should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.textFontSize = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting textFontSize to a constant value expression should update text-size.");
+ XCTAssertEqualObjects(layer.textFontSize, constantExpression,
+ @"textFontSize should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.textFontSize = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getTextSize(), propertyValue,
- @"Setting textFontSize to a camera function should update text-size.");
- XCTAssertEqualObjects(layer.textFontSize, functionStyleValue,
- @"textFontSize should round-trip camera functions.");
+ @"Setting textFontSize to a camera expression should update text-size.");
+ XCTAssertEqualObjects(layer.textFontSize, functionExpression,
+ @"textFontSize should round-trip camera expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
- layer.textFontSize = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.textFontSize = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getTextSize(), propertyValue,
- @"Setting textFontSize to a source function should update text-size.");
- XCTAssertEqualObjects(layer.textFontSize, functionStyleValue,
- @"textFontSize should round-trip source functions.");
+ @"Setting textFontSize to a data expression should update text-size.");
+ XCTAssertEqualObjects(layer.textFontSize, functionExpression,
+ @"textFontSize should round-trip data expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
- layer.textFontSize = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.textFontSize = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 };
@@ -1093,15 +1203,15 @@
propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getTextSize(), propertyValue,
- @"Setting textFontSize to a composite function should update text-size.");
- XCTAssertEqualObjects(layer.textFontSize, functionStyleValue,
- @"textFontSize should round-trip composite functions.");
+ @"Setting textFontSize to a camera-data expression should update text-size.");
+ XCTAssertEqualObjects(layer.textFontSize, functionExpression,
+ @"textFontSize should round-trip camera-data expressions.");
layer.textFontSize = nil;
XCTAssertTrue(rawLayer->getTextSize().isUndefined(),
@"Unsetting textFontSize should return text-size to the default value.");
- XCTAssertEqualObjects(layer.textFontSize, defaultStyleValue,
+ XCTAssertEqualObjects(layer.textFontSize, defaultExpression,
@"textFontSize should return the default value after being unset.");
}
@@ -1109,72 +1219,81 @@
{
XCTAssertTrue(rawLayer->getTextIgnorePlacement().isUndefined(),
@"text-ignore-placement should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.textIgnoresPlacement;
+ NSExpression *defaultExpression = layer.textIgnoresPlacement;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@YES];
- layer.textIgnoresPlacement = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"true"];
+ layer.textIgnoresPlacement = constantExpression;
mbgl::style::PropertyValue<bool> propertyValue = { true };
XCTAssertEqual(rawLayer->getTextIgnorePlacement(), propertyValue,
- @"Setting textIgnoresPlacement to a constant value should update text-ignore-placement.");
- XCTAssertEqualObjects(layer.textIgnoresPlacement, constantStyleValue,
- @"textIgnoresPlacement should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.textIgnoresPlacement = functionStyleValue;
-
- mbgl::style::IntervalStops<bool> intervalStops = { {{18, true}} };
+ @"Setting textIgnoresPlacement to a constant value expression should update text-ignore-placement.");
+ XCTAssertEqualObjects(layer.textIgnoresPlacement, constantExpression,
+ @"textIgnoresPlacement should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"true"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.textIgnoresPlacement = functionExpression;
+
+ mbgl::style::IntervalStops<bool> intervalStops = {{
+ { -INFINITY, true },
+ { 18, true },
+ }};
propertyValue = mbgl::style::CameraFunction<bool> { intervalStops };
XCTAssertEqual(rawLayer->getTextIgnorePlacement(), propertyValue,
- @"Setting textIgnoresPlacement to a camera function should update text-ignore-placement.");
- XCTAssertEqualObjects(layer.textIgnoresPlacement, functionStyleValue,
- @"textIgnoresPlacement should round-trip camera functions.");
+ @"Setting textIgnoresPlacement to a camera expression should update text-ignore-placement.");
+ XCTAssertEqualObjects(layer.textIgnoresPlacement, functionExpression,
+ @"textIgnoresPlacement should round-trip camera expressions.");
layer.textIgnoresPlacement = nil;
XCTAssertTrue(rawLayer->getTextIgnorePlacement().isUndefined(),
@"Unsetting textIgnoresPlacement should return text-ignore-placement to the default value.");
- XCTAssertEqualObjects(layer.textIgnoresPlacement, defaultStyleValue,
+ XCTAssertEqualObjects(layer.textIgnoresPlacement, defaultExpression,
@"textIgnoresPlacement should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.textIgnoresPlacement = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.textIgnoresPlacement = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.textIgnoresPlacement = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.textIgnoresPlacement = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// text-justify
{
XCTAssertTrue(rawLayer->getTextJustify().isUndefined(),
@"text-justify should be unset initially.");
- MGLStyleValue<NSValue *> *defaultStyleValue = layer.textJustification;
+ NSExpression *defaultExpression = layer.textJustification;
- MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLTextJustification:MGLTextJustificationRight]];
- layer.textJustification = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'right'"];
+ layer.textJustification = constantExpression;
mbgl::style::DataDrivenPropertyValue<mbgl::style::TextJustifyType> propertyValue = { mbgl::style::TextJustifyType::Right };
XCTAssertEqual(rawLayer->getTextJustify(), propertyValue,
- @"Setting textJustification to a constant value should update text-justify.");
- XCTAssertEqualObjects(layer.textJustification, constantStyleValue,
- @"textJustification should round-trip constant values.");
-
- MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.textJustification = functionStyleValue;
-
- mbgl::style::IntervalStops<mbgl::style::TextJustifyType> intervalStops = { {{18, mbgl::style::TextJustifyType::Right}} };
+ @"Setting textJustification to a constant value expression should update text-justify.");
+ XCTAssertEqualObjects(layer.textJustification, constantExpression,
+ @"textJustification should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"'right'"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.textJustification = functionExpression;
+
+ mbgl::style::IntervalStops<mbgl::style::TextJustifyType> intervalStops = {{
+ { -INFINITY, mbgl::style::TextJustifyType::Right },
+ { 18, mbgl::style::TextJustifyType::Right },
+ }};
propertyValue = mbgl::style::CameraFunction<mbgl::style::TextJustifyType> { intervalStops };
XCTAssertEqual(rawLayer->getTextJustify(), propertyValue,
- @"Setting textJustification to a camera function should update text-justify.");
- XCTAssertEqualObjects(layer.textJustification, functionStyleValue,
- @"textJustification should round-trip camera functions.");
+ @"Setting textJustification to a camera expression should update text-justify.");
+ XCTAssertEqualObjects(layer.textJustification, functionExpression,
+ @"textJustification should round-trip camera expressions.");
layer.textJustification = nil;
XCTAssertTrue(rawLayer->getTextJustify().isUndefined(),
@"Unsetting textJustification should return text-justify to the default value.");
- XCTAssertEqualObjects(layer.textJustification, defaultStyleValue,
+ XCTAssertEqualObjects(layer.textJustification, defaultExpression,
@"textJustification should return the default value after being unset.");
}
@@ -1182,40 +1301,44 @@
{
XCTAssertTrue(rawLayer->getTextLetterSpacing().isUndefined(),
@"text-letter-spacing should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.textLetterSpacing;
+ NSExpression *defaultExpression = layer.textLetterSpacing;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.textLetterSpacing = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.textLetterSpacing = constantExpression;
mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getTextLetterSpacing(), propertyValue,
- @"Setting textLetterSpacing to a constant value should update text-letter-spacing.");
- XCTAssertEqualObjects(layer.textLetterSpacing, constantStyleValue,
- @"textLetterSpacing should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.textLetterSpacing = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting textLetterSpacing to a constant value expression should update text-letter-spacing.");
+ XCTAssertEqualObjects(layer.textLetterSpacing, constantExpression,
+ @"textLetterSpacing should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.textLetterSpacing = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getTextLetterSpacing(), propertyValue,
- @"Setting textLetterSpacing to a camera function should update text-letter-spacing.");
- XCTAssertEqualObjects(layer.textLetterSpacing, functionStyleValue,
- @"textLetterSpacing should round-trip camera functions.");
+ @"Setting textLetterSpacing to a camera expression should update text-letter-spacing.");
+ XCTAssertEqualObjects(layer.textLetterSpacing, functionExpression,
+ @"textLetterSpacing should round-trip camera expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
- layer.textLetterSpacing = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.textLetterSpacing = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getTextLetterSpacing(), propertyValue,
- @"Setting textLetterSpacing to a source function should update text-letter-spacing.");
- XCTAssertEqualObjects(layer.textLetterSpacing, functionStyleValue,
- @"textLetterSpacing should round-trip source functions.");
+ @"Setting textLetterSpacing to a data expression should update text-letter-spacing.");
+ XCTAssertEqualObjects(layer.textLetterSpacing, functionExpression,
+ @"textLetterSpacing should round-trip data expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
- layer.textLetterSpacing = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.textLetterSpacing = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 };
@@ -1223,15 +1346,15 @@
propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getTextLetterSpacing(), propertyValue,
- @"Setting textLetterSpacing to a composite function should update text-letter-spacing.");
- XCTAssertEqualObjects(layer.textLetterSpacing, functionStyleValue,
- @"textLetterSpacing should round-trip composite functions.");
+ @"Setting textLetterSpacing to a camera-data expression should update text-letter-spacing.");
+ XCTAssertEqualObjects(layer.textLetterSpacing, functionExpression,
+ @"textLetterSpacing should round-trip camera-data expressions.");
layer.textLetterSpacing = nil;
XCTAssertTrue(rawLayer->getTextLetterSpacing().isUndefined(),
@"Unsetting textLetterSpacing should return text-letter-spacing to the default value.");
- XCTAssertEqualObjects(layer.textLetterSpacing, defaultStyleValue,
+ XCTAssertEqualObjects(layer.textLetterSpacing, defaultExpression,
@"textLetterSpacing should return the default value after being unset.");
}
@@ -1239,85 +1362,94 @@
{
XCTAssertTrue(rawLayer->getTextLineHeight().isUndefined(),
@"text-line-height should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.textLineHeight;
+ NSExpression *defaultExpression = layer.textLineHeight;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.textLineHeight = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.textLineHeight = constantExpression;
mbgl::style::PropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getTextLineHeight(), propertyValue,
- @"Setting textLineHeight to a constant value should update text-line-height.");
- XCTAssertEqualObjects(layer.textLineHeight, constantStyleValue,
- @"textLineHeight should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.textLineHeight = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting textLineHeight to a constant value expression should update text-line-height.");
+ XCTAssertEqualObjects(layer.textLineHeight, constantExpression,
+ @"textLineHeight should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.textLineHeight = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getTextLineHeight(), propertyValue,
- @"Setting textLineHeight to a camera function should update text-line-height.");
- XCTAssertEqualObjects(layer.textLineHeight, functionStyleValue,
- @"textLineHeight should round-trip camera functions.");
+ @"Setting textLineHeight to a camera expression should update text-line-height.");
+ XCTAssertEqualObjects(layer.textLineHeight, functionExpression,
+ @"textLineHeight should round-trip camera expressions.");
layer.textLineHeight = nil;
XCTAssertTrue(rawLayer->getTextLineHeight().isUndefined(),
@"Unsetting textLineHeight should return text-line-height to the default value.");
- XCTAssertEqualObjects(layer.textLineHeight, defaultStyleValue,
+ XCTAssertEqualObjects(layer.textLineHeight, defaultExpression,
@"textLineHeight should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.textLineHeight = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.textLineHeight = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.textLineHeight = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.textLineHeight = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// text-offset
{
XCTAssertTrue(rawLayer->getTextOffset().isUndefined(),
@"text-offset should be unset initially.");
- MGLStyleValue<NSValue *> *defaultStyleValue = layer.textOffset;
+ NSExpression *defaultExpression = layer.textOffset;
- MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@",
#if TARGET_OS_IPHONE
[NSValue valueWithCGVector:CGVectorMake(1, 1)]
#else
[NSValue valueWithMGLVector:CGVectorMake(1, -1)]
#endif
];
- layer.textOffset = constantStyleValue;
+ layer.textOffset = constantExpression;
mbgl::style::DataDrivenPropertyValue<std::array<float, 2>> propertyValue = { { 1, 1 } };
XCTAssertEqual(rawLayer->getTextOffset(), propertyValue,
- @"Setting textOffset to a constant value should update text-offset.");
- XCTAssertEqualObjects(layer.textOffset, constantStyleValue,
- @"textOffset should round-trip constant values.");
-
- MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.textOffset = functionStyleValue;
-
- mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = { {{18, { 1, 1 }}} };
+ @"Setting textOffset to a constant value expression should update text-offset.");
+ XCTAssertEqualObjects(layer.textOffset, constantExpression,
+ @"textOffset should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"{1, 1}"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.textOffset = functionExpression;
+
+ mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = {{
+ { -INFINITY, { 1, 1 } },
+ { 18, { 1, 1 } },
+ }};
propertyValue = mbgl::style::CameraFunction<std::array<float, 2>> { intervalStops };
XCTAssertEqual(rawLayer->getTextOffset(), propertyValue,
- @"Setting textOffset to a camera function should update text-offset.");
- XCTAssertEqualObjects(layer.textOffset, functionStyleValue,
- @"textOffset should round-trip camera functions.");
+ @"Setting textOffset to a camera expression should update text-offset.");
+ XCTAssertEqualObjects(layer.textOffset, functionExpression,
+ @"textOffset should round-trip camera expressions.");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
- layer.textOffset = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.textOffset = functionExpression;
mbgl::style::ExponentialStops<std::array<float, 2>> exponentialStops = { {{18, { 1, 1 }}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<std::array<float, 2>> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getTextOffset(), propertyValue,
- @"Setting textOffset to a source function should update text-offset.");
- XCTAssertEqualObjects(layer.textOffset, functionStyleValue,
- @"textOffset should round-trip source functions.");
+ @"Setting textOffset to a data expression should update text-offset.");
+ XCTAssertEqualObjects(layer.textOffset, functionExpression,
+ @"textOffset should round-trip data expressions.");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
- layer.textOffset = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.textOffset = functionExpression;
std::map<float, std::array<float, 2>> innerStops { {18, { 1, 1 }} };
mbgl::style::CompositeExponentialStops<std::array<float, 2>> compositeStops { { {10.0, innerStops} }, 1.0 };
@@ -1325,15 +1457,15 @@
propertyValue = mbgl::style::CompositeFunction<std::array<float, 2>> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getTextOffset(), propertyValue,
- @"Setting textOffset to a composite function should update text-offset.");
- XCTAssertEqualObjects(layer.textOffset, functionStyleValue,
- @"textOffset should round-trip composite functions.");
+ @"Setting textOffset to a camera-data expression should update text-offset.");
+ XCTAssertEqualObjects(layer.textOffset, functionExpression,
+ @"textOffset should round-trip camera-data expressions.");
layer.textOffset = nil;
XCTAssertTrue(rawLayer->getTextOffset().isUndefined(),
@"Unsetting textOffset should return text-offset to the default value.");
- XCTAssertEqualObjects(layer.textOffset, defaultStyleValue,
+ XCTAssertEqualObjects(layer.textOffset, defaultExpression,
@"textOffset should return the default value after being unset.");
}
@@ -1341,157 +1473,176 @@
{
XCTAssertTrue(rawLayer->getTextOptional().isUndefined(),
@"text-optional should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.textOptional;
+ NSExpression *defaultExpression = layer.textOptional;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@YES];
- layer.textOptional = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"true"];
+ layer.textOptional = constantExpression;
mbgl::style::PropertyValue<bool> propertyValue = { true };
XCTAssertEqual(rawLayer->getTextOptional(), propertyValue,
- @"Setting textOptional to a constant value should update text-optional.");
- XCTAssertEqualObjects(layer.textOptional, constantStyleValue,
- @"textOptional should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.textOptional = functionStyleValue;
-
- mbgl::style::IntervalStops<bool> intervalStops = { {{18, true}} };
+ @"Setting textOptional to a constant value expression should update text-optional.");
+ XCTAssertEqualObjects(layer.textOptional, constantExpression,
+ @"textOptional should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"true"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.textOptional = functionExpression;
+
+ mbgl::style::IntervalStops<bool> intervalStops = {{
+ { -INFINITY, true },
+ { 18, true },
+ }};
propertyValue = mbgl::style::CameraFunction<bool> { intervalStops };
XCTAssertEqual(rawLayer->getTextOptional(), propertyValue,
- @"Setting textOptional to a camera function should update text-optional.");
- XCTAssertEqualObjects(layer.textOptional, functionStyleValue,
- @"textOptional should round-trip camera functions.");
+ @"Setting textOptional to a camera expression should update text-optional.");
+ XCTAssertEqualObjects(layer.textOptional, functionExpression,
+ @"textOptional should round-trip camera expressions.");
layer.textOptional = nil;
XCTAssertTrue(rawLayer->getTextOptional().isUndefined(),
@"Unsetting textOptional should return text-optional to the default value.");
- XCTAssertEqualObjects(layer.textOptional, defaultStyleValue,
+ XCTAssertEqualObjects(layer.textOptional, defaultExpression,
@"textOptional should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.textOptional = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.textOptional = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.textOptional = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.textOptional = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// text-padding
{
XCTAssertTrue(rawLayer->getTextPadding().isUndefined(),
@"text-padding should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.textPadding;
+ NSExpression *defaultExpression = layer.textPadding;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.textPadding = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.textPadding = constantExpression;
mbgl::style::PropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getTextPadding(), propertyValue,
- @"Setting textPadding to a constant value should update text-padding.");
- XCTAssertEqualObjects(layer.textPadding, constantStyleValue,
- @"textPadding should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.textPadding = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting textPadding to a constant value expression should update text-padding.");
+ XCTAssertEqualObjects(layer.textPadding, constantExpression,
+ @"textPadding should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.textPadding = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getTextPadding(), propertyValue,
- @"Setting textPadding to a camera function should update text-padding.");
- XCTAssertEqualObjects(layer.textPadding, functionStyleValue,
- @"textPadding should round-trip camera functions.");
+ @"Setting textPadding to a camera expression should update text-padding.");
+ XCTAssertEqualObjects(layer.textPadding, functionExpression,
+ @"textPadding should round-trip camera expressions.");
layer.textPadding = nil;
XCTAssertTrue(rawLayer->getTextPadding().isUndefined(),
@"Unsetting textPadding should return text-padding to the default value.");
- XCTAssertEqualObjects(layer.textPadding, defaultStyleValue,
+ XCTAssertEqualObjects(layer.textPadding, defaultExpression,
@"textPadding should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.textPadding = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.textPadding = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.textPadding = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.textPadding = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// text-pitch-alignment
{
XCTAssertTrue(rawLayer->getTextPitchAlignment().isUndefined(),
@"text-pitch-alignment should be unset initially.");
- MGLStyleValue<NSValue *> *defaultStyleValue = layer.textPitchAlignment;
+ NSExpression *defaultExpression = layer.textPitchAlignment;
- MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLTextPitchAlignment:MGLTextPitchAlignmentAuto]];
- layer.textPitchAlignment = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'auto'"];
+ layer.textPitchAlignment = constantExpression;
mbgl::style::PropertyValue<mbgl::style::AlignmentType> propertyValue = { mbgl::style::AlignmentType::Auto };
XCTAssertEqual(rawLayer->getTextPitchAlignment(), propertyValue,
- @"Setting textPitchAlignment to a constant value should update text-pitch-alignment.");
- XCTAssertEqualObjects(layer.textPitchAlignment, constantStyleValue,
- @"textPitchAlignment should round-trip constant values.");
-
- MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.textPitchAlignment = functionStyleValue;
-
- mbgl::style::IntervalStops<mbgl::style::AlignmentType> intervalStops = { {{18, mbgl::style::AlignmentType::Auto}} };
+ @"Setting textPitchAlignment to a constant value expression should update text-pitch-alignment.");
+ XCTAssertEqualObjects(layer.textPitchAlignment, constantExpression,
+ @"textPitchAlignment should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"'auto'"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.textPitchAlignment = functionExpression;
+
+ mbgl::style::IntervalStops<mbgl::style::AlignmentType> intervalStops = {{
+ { -INFINITY, mbgl::style::AlignmentType::Auto },
+ { 18, mbgl::style::AlignmentType::Auto },
+ }};
propertyValue = mbgl::style::CameraFunction<mbgl::style::AlignmentType> { intervalStops };
XCTAssertEqual(rawLayer->getTextPitchAlignment(), propertyValue,
- @"Setting textPitchAlignment to a camera function should update text-pitch-alignment.");
- XCTAssertEqualObjects(layer.textPitchAlignment, functionStyleValue,
- @"textPitchAlignment should round-trip camera functions.");
+ @"Setting textPitchAlignment to a camera expression should update text-pitch-alignment.");
+ XCTAssertEqualObjects(layer.textPitchAlignment, functionExpression,
+ @"textPitchAlignment should round-trip camera expressions.");
layer.textPitchAlignment = nil;
XCTAssertTrue(rawLayer->getTextPitchAlignment().isUndefined(),
@"Unsetting textPitchAlignment should return text-pitch-alignment to the default value.");
- XCTAssertEqualObjects(layer.textPitchAlignment, defaultStyleValue,
+ XCTAssertEqualObjects(layer.textPitchAlignment, defaultExpression,
@"textPitchAlignment should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.textPitchAlignment = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.textPitchAlignment = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.textPitchAlignment = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.textPitchAlignment = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// text-rotate
{
XCTAssertTrue(rawLayer->getTextRotate().isUndefined(),
@"text-rotate should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.textRotation;
+ NSExpression *defaultExpression = layer.textRotation;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.textRotation = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.textRotation = constantExpression;
mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getTextRotate(), propertyValue,
- @"Setting textRotation to a constant value should update text-rotate.");
- XCTAssertEqualObjects(layer.textRotation, constantStyleValue,
- @"textRotation should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.textRotation = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting textRotation to a constant value expression should update text-rotate.");
+ XCTAssertEqualObjects(layer.textRotation, constantExpression,
+ @"textRotation should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.textRotation = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getTextRotate(), propertyValue,
- @"Setting textRotation to a camera function should update text-rotate.");
- XCTAssertEqualObjects(layer.textRotation, functionStyleValue,
- @"textRotation should round-trip camera functions.");
+ @"Setting textRotation to a camera expression should update text-rotate.");
+ XCTAssertEqualObjects(layer.textRotation, functionExpression,
+ @"textRotation should round-trip camera expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
- layer.textRotation = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.textRotation = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getTextRotate(), propertyValue,
- @"Setting textRotation to a source function should update text-rotate.");
- XCTAssertEqualObjects(layer.textRotation, functionStyleValue,
- @"textRotation should round-trip source functions.");
+ @"Setting textRotation to a data expression should update text-rotate.");
+ XCTAssertEqualObjects(layer.textRotation, functionExpression,
+ @"textRotation should round-trip data expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
- layer.textRotation = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.textRotation = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 };
@@ -1499,15 +1650,15 @@
propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getTextRotate(), propertyValue,
- @"Setting textRotation to a composite function should update text-rotate.");
- XCTAssertEqualObjects(layer.textRotation, functionStyleValue,
- @"textRotation should round-trip composite functions.");
+ @"Setting textRotation to a camera-data expression should update text-rotate.");
+ XCTAssertEqualObjects(layer.textRotation, functionExpression,
+ @"textRotation should round-trip camera-data expressions.");
layer.textRotation = nil;
XCTAssertTrue(rawLayer->getTextRotate().isUndefined(),
@"Unsetting textRotation should return text-rotate to the default value.");
- XCTAssertEqualObjects(layer.textRotation, defaultStyleValue,
+ XCTAssertEqualObjects(layer.textRotation, defaultExpression,
@"textRotation should return the default value after being unset.");
}
@@ -1515,72 +1666,81 @@
{
XCTAssertTrue(rawLayer->getTextRotationAlignment().isUndefined(),
@"text-rotation-alignment should be unset initially.");
- MGLStyleValue<NSValue *> *defaultStyleValue = layer.textRotationAlignment;
+ NSExpression *defaultExpression = layer.textRotationAlignment;
- MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLTextRotationAlignment:MGLTextRotationAlignmentAuto]];
- layer.textRotationAlignment = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'auto'"];
+ layer.textRotationAlignment = constantExpression;
mbgl::style::PropertyValue<mbgl::style::AlignmentType> propertyValue = { mbgl::style::AlignmentType::Auto };
XCTAssertEqual(rawLayer->getTextRotationAlignment(), propertyValue,
- @"Setting textRotationAlignment to a constant value should update text-rotation-alignment.");
- XCTAssertEqualObjects(layer.textRotationAlignment, constantStyleValue,
- @"textRotationAlignment should round-trip constant values.");
-
- MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.textRotationAlignment = functionStyleValue;
-
- mbgl::style::IntervalStops<mbgl::style::AlignmentType> intervalStops = { {{18, mbgl::style::AlignmentType::Auto}} };
+ @"Setting textRotationAlignment to a constant value expression should update text-rotation-alignment.");
+ XCTAssertEqualObjects(layer.textRotationAlignment, constantExpression,
+ @"textRotationAlignment should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"'auto'"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.textRotationAlignment = functionExpression;
+
+ mbgl::style::IntervalStops<mbgl::style::AlignmentType> intervalStops = {{
+ { -INFINITY, mbgl::style::AlignmentType::Auto },
+ { 18, mbgl::style::AlignmentType::Auto },
+ }};
propertyValue = mbgl::style::CameraFunction<mbgl::style::AlignmentType> { intervalStops };
XCTAssertEqual(rawLayer->getTextRotationAlignment(), propertyValue,
- @"Setting textRotationAlignment to a camera function should update text-rotation-alignment.");
- XCTAssertEqualObjects(layer.textRotationAlignment, functionStyleValue,
- @"textRotationAlignment should round-trip camera functions.");
+ @"Setting textRotationAlignment to a camera expression should update text-rotation-alignment.");
+ XCTAssertEqualObjects(layer.textRotationAlignment, functionExpression,
+ @"textRotationAlignment should round-trip camera expressions.");
layer.textRotationAlignment = nil;
XCTAssertTrue(rawLayer->getTextRotationAlignment().isUndefined(),
@"Unsetting textRotationAlignment should return text-rotation-alignment to the default value.");
- XCTAssertEqualObjects(layer.textRotationAlignment, defaultStyleValue,
+ XCTAssertEqualObjects(layer.textRotationAlignment, defaultExpression,
@"textRotationAlignment should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.textRotationAlignment = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.textRotationAlignment = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.textRotationAlignment = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.textRotationAlignment = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// text-transform
{
XCTAssertTrue(rawLayer->getTextTransform().isUndefined(),
@"text-transform should be unset initially.");
- MGLStyleValue<NSValue *> *defaultStyleValue = layer.textTransform;
+ NSExpression *defaultExpression = layer.textTransform;
- MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLTextTransform:MGLTextTransformLowercase]];
- layer.textTransform = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'lowercase'"];
+ layer.textTransform = constantExpression;
mbgl::style::DataDrivenPropertyValue<mbgl::style::TextTransformType> propertyValue = { mbgl::style::TextTransformType::Lowercase };
XCTAssertEqual(rawLayer->getTextTransform(), propertyValue,
- @"Setting textTransform to a constant value should update text-transform.");
- XCTAssertEqualObjects(layer.textTransform, constantStyleValue,
- @"textTransform should round-trip constant values.");
-
- MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.textTransform = functionStyleValue;
-
- mbgl::style::IntervalStops<mbgl::style::TextTransformType> intervalStops = { {{18, mbgl::style::TextTransformType::Lowercase}} };
+ @"Setting textTransform to a constant value expression should update text-transform.");
+ XCTAssertEqualObjects(layer.textTransform, constantExpression,
+ @"textTransform should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"'lowercase'"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.textTransform = functionExpression;
+
+ mbgl::style::IntervalStops<mbgl::style::TextTransformType> intervalStops = {{
+ { -INFINITY, mbgl::style::TextTransformType::Lowercase },
+ { 18, mbgl::style::TextTransformType::Lowercase },
+ }};
propertyValue = mbgl::style::CameraFunction<mbgl::style::TextTransformType> { intervalStops };
XCTAssertEqual(rawLayer->getTextTransform(), propertyValue,
- @"Setting textTransform to a camera function should update text-transform.");
- XCTAssertEqualObjects(layer.textTransform, functionStyleValue,
- @"textTransform should round-trip camera functions.");
+ @"Setting textTransform to a camera expression should update text-transform.");
+ XCTAssertEqualObjects(layer.textTransform, functionExpression,
+ @"textTransform should round-trip camera expressions.");
layer.textTransform = nil;
XCTAssertTrue(rawLayer->getTextTransform().isUndefined(),
@"Unsetting textTransform should return text-transform to the default value.");
- XCTAssertEqualObjects(layer.textTransform, defaultStyleValue,
+ XCTAssertEqualObjects(layer.textTransform, defaultExpression,
@"textTransform should return the default value after being unset.");
}
@@ -1588,40 +1748,44 @@
{
XCTAssertTrue(rawLayer->getIconColor().isUndefined(),
@"icon-color should be unset initially.");
- MGLStyleValue<MGLColor *> *defaultStyleValue = layer.iconColor;
+ NSExpression *defaultExpression = layer.iconColor;
- MGLStyleValue<MGLColor *> *constantStyleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]];
- layer.iconColor = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
+ layer.iconColor = constantExpression;
mbgl::style::DataDrivenPropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } };
XCTAssertEqual(rawLayer->getIconColor(), propertyValue,
- @"Setting iconColor to a constant value should update icon-color.");
- XCTAssertEqualObjects(layer.iconColor, constantStyleValue,
- @"iconColor should round-trip constant values.");
-
- MGLStyleValue<MGLColor *> * functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.iconColor = functionStyleValue;
-
- mbgl::style::IntervalStops<mbgl::Color> intervalStops = { {{18, { 1, 0, 0, 1 }}} };
+ @"Setting iconColor to a constant value expression should update icon-color.");
+ XCTAssertEqualObjects(layer.iconColor, constantExpression,
+ @"iconColor should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.iconColor = functionExpression;
+
+ mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{
+ { -INFINITY, { 1, 0, 0, 1 } },
+ { 18, { 1, 0, 0, 1 } },
+ }};
propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops };
XCTAssertEqual(rawLayer->getIconColor(), propertyValue,
- @"Setting iconColor to a camera function should update icon-color.");
- XCTAssertEqualObjects(layer.iconColor, functionStyleValue,
- @"iconColor should round-trip camera functions.");
+ @"Setting iconColor to a camera expression should update icon-color.");
+ XCTAssertEqualObjects(layer.iconColor, functionExpression,
+ @"iconColor should round-trip camera expressions.");
- functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
- layer.iconColor = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.iconColor = functionExpression;
mbgl::style::ExponentialStops<mbgl::Color> exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<mbgl::Color> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getIconColor(), propertyValue,
- @"Setting iconColor to a source function should update icon-color.");
- XCTAssertEqualObjects(layer.iconColor, functionStyleValue,
- @"iconColor should round-trip source functions.");
+ @"Setting iconColor to a data expression should update icon-color.");
+ XCTAssertEqualObjects(layer.iconColor, functionExpression,
+ @"iconColor should round-trip data expressions.");
- functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
- layer.iconColor = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.iconColor = functionExpression;
std::map<float, mbgl::Color> innerStops { {18, { 1, 0, 0, 1 }} };
mbgl::style::CompositeExponentialStops<mbgl::Color> compositeStops { { {10.0, innerStops} }, 1.0 };
@@ -1629,15 +1793,15 @@
propertyValue = mbgl::style::CompositeFunction<mbgl::Color> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getIconColor(), propertyValue,
- @"Setting iconColor to a composite function should update icon-color.");
- XCTAssertEqualObjects(layer.iconColor, functionStyleValue,
- @"iconColor should round-trip composite functions.");
+ @"Setting iconColor to a camera-data expression should update icon-color.");
+ XCTAssertEqualObjects(layer.iconColor, functionExpression,
+ @"iconColor should round-trip camera-data expressions.");
layer.iconColor = nil;
XCTAssertTrue(rawLayer->getIconColor().isUndefined(),
@"Unsetting iconColor should return icon-color to the default value.");
- XCTAssertEqualObjects(layer.iconColor, defaultStyleValue,
+ XCTAssertEqualObjects(layer.iconColor, defaultExpression,
@"iconColor should return the default value after being unset.");
// Transition property test
layer.iconColorTransition = transitionTest;
@@ -1654,40 +1818,44 @@
{
XCTAssertTrue(rawLayer->getIconHaloBlur().isUndefined(),
@"icon-halo-blur should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.iconHaloBlur;
+ NSExpression *defaultExpression = layer.iconHaloBlur;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.iconHaloBlur = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.iconHaloBlur = constantExpression;
mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getIconHaloBlur(), propertyValue,
- @"Setting iconHaloBlur to a constant value should update icon-halo-blur.");
- XCTAssertEqualObjects(layer.iconHaloBlur, constantStyleValue,
- @"iconHaloBlur should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.iconHaloBlur = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting iconHaloBlur to a constant value expression should update icon-halo-blur.");
+ XCTAssertEqualObjects(layer.iconHaloBlur, constantExpression,
+ @"iconHaloBlur should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.iconHaloBlur = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getIconHaloBlur(), propertyValue,
- @"Setting iconHaloBlur to a camera function should update icon-halo-blur.");
- XCTAssertEqualObjects(layer.iconHaloBlur, functionStyleValue,
- @"iconHaloBlur should round-trip camera functions.");
+ @"Setting iconHaloBlur to a camera expression should update icon-halo-blur.");
+ XCTAssertEqualObjects(layer.iconHaloBlur, functionExpression,
+ @"iconHaloBlur should round-trip camera expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
- layer.iconHaloBlur = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.iconHaloBlur = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getIconHaloBlur(), propertyValue,
- @"Setting iconHaloBlur to a source function should update icon-halo-blur.");
- XCTAssertEqualObjects(layer.iconHaloBlur, functionStyleValue,
- @"iconHaloBlur should round-trip source functions.");
+ @"Setting iconHaloBlur to a data expression should update icon-halo-blur.");
+ XCTAssertEqualObjects(layer.iconHaloBlur, functionExpression,
+ @"iconHaloBlur should round-trip data expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
- layer.iconHaloBlur = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.iconHaloBlur = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 };
@@ -1695,15 +1863,15 @@
propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getIconHaloBlur(), propertyValue,
- @"Setting iconHaloBlur to a composite function should update icon-halo-blur.");
- XCTAssertEqualObjects(layer.iconHaloBlur, functionStyleValue,
- @"iconHaloBlur should round-trip composite functions.");
+ @"Setting iconHaloBlur to a camera-data expression should update icon-halo-blur.");
+ XCTAssertEqualObjects(layer.iconHaloBlur, functionExpression,
+ @"iconHaloBlur should round-trip camera-data expressions.");
layer.iconHaloBlur = nil;
XCTAssertTrue(rawLayer->getIconHaloBlur().isUndefined(),
@"Unsetting iconHaloBlur should return icon-halo-blur to the default value.");
- XCTAssertEqualObjects(layer.iconHaloBlur, defaultStyleValue,
+ XCTAssertEqualObjects(layer.iconHaloBlur, defaultExpression,
@"iconHaloBlur should return the default value after being unset.");
// Transition property test
layer.iconHaloBlurTransition = transitionTest;
@@ -1720,40 +1888,44 @@
{
XCTAssertTrue(rawLayer->getIconHaloColor().isUndefined(),
@"icon-halo-color should be unset initially.");
- MGLStyleValue<MGLColor *> *defaultStyleValue = layer.iconHaloColor;
+ NSExpression *defaultExpression = layer.iconHaloColor;
- MGLStyleValue<MGLColor *> *constantStyleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]];
- layer.iconHaloColor = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
+ layer.iconHaloColor = constantExpression;
mbgl::style::DataDrivenPropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } };
XCTAssertEqual(rawLayer->getIconHaloColor(), propertyValue,
- @"Setting iconHaloColor to a constant value should update icon-halo-color.");
- XCTAssertEqualObjects(layer.iconHaloColor, constantStyleValue,
- @"iconHaloColor should round-trip constant values.");
-
- MGLStyleValue<MGLColor *> * functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.iconHaloColor = functionStyleValue;
-
- mbgl::style::IntervalStops<mbgl::Color> intervalStops = { {{18, { 1, 0, 0, 1 }}} };
+ @"Setting iconHaloColor to a constant value expression should update icon-halo-color.");
+ XCTAssertEqualObjects(layer.iconHaloColor, constantExpression,
+ @"iconHaloColor should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.iconHaloColor = functionExpression;
+
+ mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{
+ { -INFINITY, { 1, 0, 0, 1 } },
+ { 18, { 1, 0, 0, 1 } },
+ }};
propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops };
XCTAssertEqual(rawLayer->getIconHaloColor(), propertyValue,
- @"Setting iconHaloColor to a camera function should update icon-halo-color.");
- XCTAssertEqualObjects(layer.iconHaloColor, functionStyleValue,
- @"iconHaloColor should round-trip camera functions.");
+ @"Setting iconHaloColor to a camera expression should update icon-halo-color.");
+ XCTAssertEqualObjects(layer.iconHaloColor, functionExpression,
+ @"iconHaloColor should round-trip camera expressions.");
- functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
- layer.iconHaloColor = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.iconHaloColor = functionExpression;
mbgl::style::ExponentialStops<mbgl::Color> exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<mbgl::Color> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getIconHaloColor(), propertyValue,
- @"Setting iconHaloColor to a source function should update icon-halo-color.");
- XCTAssertEqualObjects(layer.iconHaloColor, functionStyleValue,
- @"iconHaloColor should round-trip source functions.");
+ @"Setting iconHaloColor to a data expression should update icon-halo-color.");
+ XCTAssertEqualObjects(layer.iconHaloColor, functionExpression,
+ @"iconHaloColor should round-trip data expressions.");
- functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
- layer.iconHaloColor = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.iconHaloColor = functionExpression;
std::map<float, mbgl::Color> innerStops { {18, { 1, 0, 0, 1 }} };
mbgl::style::CompositeExponentialStops<mbgl::Color> compositeStops { { {10.0, innerStops} }, 1.0 };
@@ -1761,15 +1933,15 @@
propertyValue = mbgl::style::CompositeFunction<mbgl::Color> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getIconHaloColor(), propertyValue,
- @"Setting iconHaloColor to a composite function should update icon-halo-color.");
- XCTAssertEqualObjects(layer.iconHaloColor, functionStyleValue,
- @"iconHaloColor should round-trip composite functions.");
+ @"Setting iconHaloColor to a camera-data expression should update icon-halo-color.");
+ XCTAssertEqualObjects(layer.iconHaloColor, functionExpression,
+ @"iconHaloColor should round-trip camera-data expressions.");
layer.iconHaloColor = nil;
XCTAssertTrue(rawLayer->getIconHaloColor().isUndefined(),
@"Unsetting iconHaloColor should return icon-halo-color to the default value.");
- XCTAssertEqualObjects(layer.iconHaloColor, defaultStyleValue,
+ XCTAssertEqualObjects(layer.iconHaloColor, defaultExpression,
@"iconHaloColor should return the default value after being unset.");
// Transition property test
layer.iconHaloColorTransition = transitionTest;
@@ -1786,40 +1958,44 @@
{
XCTAssertTrue(rawLayer->getIconHaloWidth().isUndefined(),
@"icon-halo-width should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.iconHaloWidth;
+ NSExpression *defaultExpression = layer.iconHaloWidth;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.iconHaloWidth = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.iconHaloWidth = constantExpression;
mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getIconHaloWidth(), propertyValue,
- @"Setting iconHaloWidth to a constant value should update icon-halo-width.");
- XCTAssertEqualObjects(layer.iconHaloWidth, constantStyleValue,
- @"iconHaloWidth should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.iconHaloWidth = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting iconHaloWidth to a constant value expression should update icon-halo-width.");
+ XCTAssertEqualObjects(layer.iconHaloWidth, constantExpression,
+ @"iconHaloWidth should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.iconHaloWidth = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getIconHaloWidth(), propertyValue,
- @"Setting iconHaloWidth to a camera function should update icon-halo-width.");
- XCTAssertEqualObjects(layer.iconHaloWidth, functionStyleValue,
- @"iconHaloWidth should round-trip camera functions.");
+ @"Setting iconHaloWidth to a camera expression should update icon-halo-width.");
+ XCTAssertEqualObjects(layer.iconHaloWidth, functionExpression,
+ @"iconHaloWidth should round-trip camera expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
- layer.iconHaloWidth = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.iconHaloWidth = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getIconHaloWidth(), propertyValue,
- @"Setting iconHaloWidth to a source function should update icon-halo-width.");
- XCTAssertEqualObjects(layer.iconHaloWidth, functionStyleValue,
- @"iconHaloWidth should round-trip source functions.");
+ @"Setting iconHaloWidth to a data expression should update icon-halo-width.");
+ XCTAssertEqualObjects(layer.iconHaloWidth, functionExpression,
+ @"iconHaloWidth should round-trip data expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
- layer.iconHaloWidth = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.iconHaloWidth = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 };
@@ -1827,15 +2003,15 @@
propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getIconHaloWidth(), propertyValue,
- @"Setting iconHaloWidth to a composite function should update icon-halo-width.");
- XCTAssertEqualObjects(layer.iconHaloWidth, functionStyleValue,
- @"iconHaloWidth should round-trip composite functions.");
+ @"Setting iconHaloWidth to a camera-data expression should update icon-halo-width.");
+ XCTAssertEqualObjects(layer.iconHaloWidth, functionExpression,
+ @"iconHaloWidth should round-trip camera-data expressions.");
layer.iconHaloWidth = nil;
XCTAssertTrue(rawLayer->getIconHaloWidth().isUndefined(),
@"Unsetting iconHaloWidth should return icon-halo-width to the default value.");
- XCTAssertEqualObjects(layer.iconHaloWidth, defaultStyleValue,
+ XCTAssertEqualObjects(layer.iconHaloWidth, defaultExpression,
@"iconHaloWidth should return the default value after being unset.");
// Transition property test
layer.iconHaloWidthTransition = transitionTest;
@@ -1852,40 +2028,44 @@
{
XCTAssertTrue(rawLayer->getIconOpacity().isUndefined(),
@"icon-opacity should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.iconOpacity;
+ NSExpression *defaultExpression = layer.iconOpacity;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.iconOpacity = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.iconOpacity = constantExpression;
mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getIconOpacity(), propertyValue,
- @"Setting iconOpacity to a constant value should update icon-opacity.");
- XCTAssertEqualObjects(layer.iconOpacity, constantStyleValue,
- @"iconOpacity should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.iconOpacity = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting iconOpacity to a constant value expression should update icon-opacity.");
+ XCTAssertEqualObjects(layer.iconOpacity, constantExpression,
+ @"iconOpacity should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.iconOpacity = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getIconOpacity(), propertyValue,
- @"Setting iconOpacity to a camera function should update icon-opacity.");
- XCTAssertEqualObjects(layer.iconOpacity, functionStyleValue,
- @"iconOpacity should round-trip camera functions.");
+ @"Setting iconOpacity to a camera expression should update icon-opacity.");
+ XCTAssertEqualObjects(layer.iconOpacity, functionExpression,
+ @"iconOpacity should round-trip camera expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
- layer.iconOpacity = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.iconOpacity = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getIconOpacity(), propertyValue,
- @"Setting iconOpacity to a source function should update icon-opacity.");
- XCTAssertEqualObjects(layer.iconOpacity, functionStyleValue,
- @"iconOpacity should round-trip source functions.");
+ @"Setting iconOpacity to a data expression should update icon-opacity.");
+ XCTAssertEqualObjects(layer.iconOpacity, functionExpression,
+ @"iconOpacity should round-trip data expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
- layer.iconOpacity = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.iconOpacity = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 };
@@ -1893,15 +2073,15 @@
propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getIconOpacity(), propertyValue,
- @"Setting iconOpacity to a composite function should update icon-opacity.");
- XCTAssertEqualObjects(layer.iconOpacity, functionStyleValue,
- @"iconOpacity should round-trip composite functions.");
+ @"Setting iconOpacity to a camera-data expression should update icon-opacity.");
+ XCTAssertEqualObjects(layer.iconOpacity, functionExpression,
+ @"iconOpacity should round-trip camera-data expressions.");
layer.iconOpacity = nil;
XCTAssertTrue(rawLayer->getIconOpacity().isUndefined(),
@"Unsetting iconOpacity should return icon-opacity to the default value.");
- XCTAssertEqualObjects(layer.iconOpacity, defaultStyleValue,
+ XCTAssertEqualObjects(layer.iconOpacity, defaultExpression,
@"iconOpacity should return the default value after being unset.");
// Transition property test
layer.iconOpacityTransition = transitionTest;
@@ -1918,124 +2098,138 @@
{
XCTAssertTrue(rawLayer->getIconTranslate().isUndefined(),
@"icon-translate should be unset initially.");
- MGLStyleValue<NSValue *> *defaultStyleValue = layer.iconTranslation;
+ NSExpression *defaultExpression = layer.iconTranslation;
- MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@",
#if TARGET_OS_IPHONE
[NSValue valueWithCGVector:CGVectorMake(1, 1)]
#else
[NSValue valueWithMGLVector:CGVectorMake(1, -1)]
#endif
];
- layer.iconTranslation = constantStyleValue;
+ layer.iconTranslation = constantExpression;
mbgl::style::PropertyValue<std::array<float, 2>> propertyValue = { { 1, 1 } };
XCTAssertEqual(rawLayer->getIconTranslate(), propertyValue,
- @"Setting iconTranslation to a constant value should update icon-translate.");
- XCTAssertEqualObjects(layer.iconTranslation, constantStyleValue,
- @"iconTranslation should round-trip constant values.");
-
- MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.iconTranslation = functionStyleValue;
-
- mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = { {{18, { 1, 1 }}} };
+ @"Setting iconTranslation to a constant value expression should update icon-translate.");
+ XCTAssertEqualObjects(layer.iconTranslation, constantExpression,
+ @"iconTranslation should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"{1, 1}"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.iconTranslation = functionExpression;
+
+ mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = {{
+ { -INFINITY, { 1, 1 } },
+ { 18, { 1, 1 } },
+ }};
propertyValue = mbgl::style::CameraFunction<std::array<float, 2>> { intervalStops };
XCTAssertEqual(rawLayer->getIconTranslate(), propertyValue,
- @"Setting iconTranslation to a camera function should update icon-translate.");
- XCTAssertEqualObjects(layer.iconTranslation, functionStyleValue,
- @"iconTranslation should round-trip camera functions.");
+ @"Setting iconTranslation to a camera expression should update icon-translate.");
+ XCTAssertEqualObjects(layer.iconTranslation, functionExpression,
+ @"iconTranslation should round-trip camera expressions.");
layer.iconTranslation = nil;
XCTAssertTrue(rawLayer->getIconTranslate().isUndefined(),
@"Unsetting iconTranslation should return icon-translate to the default value.");
- XCTAssertEqualObjects(layer.iconTranslation, defaultStyleValue,
+ XCTAssertEqualObjects(layer.iconTranslation, defaultExpression,
@"iconTranslation should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.iconTranslation = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.iconTranslation = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.iconTranslation = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.iconTranslation = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// icon-translate-anchor
{
XCTAssertTrue(rawLayer->getIconTranslateAnchor().isUndefined(),
@"icon-translate-anchor should be unset initially.");
- MGLStyleValue<NSValue *> *defaultStyleValue = layer.iconTranslationAnchor;
+ NSExpression *defaultExpression = layer.iconTranslationAnchor;
- MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLIconTranslationAnchor:MGLIconTranslationAnchorViewport]];
- layer.iconTranslationAnchor = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'viewport'"];
+ layer.iconTranslationAnchor = constantExpression;
mbgl::style::PropertyValue<mbgl::style::TranslateAnchorType> propertyValue = { mbgl::style::TranslateAnchorType::Viewport };
XCTAssertEqual(rawLayer->getIconTranslateAnchor(), propertyValue,
- @"Setting iconTranslationAnchor to a constant value should update icon-translate-anchor.");
- XCTAssertEqualObjects(layer.iconTranslationAnchor, constantStyleValue,
- @"iconTranslationAnchor should round-trip constant values.");
-
- MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.iconTranslationAnchor = functionStyleValue;
-
- mbgl::style::IntervalStops<mbgl::style::TranslateAnchorType> intervalStops = { {{18, mbgl::style::TranslateAnchorType::Viewport}} };
+ @"Setting iconTranslationAnchor to a constant value expression should update icon-translate-anchor.");
+ XCTAssertEqualObjects(layer.iconTranslationAnchor, constantExpression,
+ @"iconTranslationAnchor should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"'viewport'"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.iconTranslationAnchor = functionExpression;
+
+ mbgl::style::IntervalStops<mbgl::style::TranslateAnchorType> intervalStops = {{
+ { -INFINITY, mbgl::style::TranslateAnchorType::Viewport },
+ { 18, mbgl::style::TranslateAnchorType::Viewport },
+ }};
propertyValue = mbgl::style::CameraFunction<mbgl::style::TranslateAnchorType> { intervalStops };
XCTAssertEqual(rawLayer->getIconTranslateAnchor(), propertyValue,
- @"Setting iconTranslationAnchor to a camera function should update icon-translate-anchor.");
- XCTAssertEqualObjects(layer.iconTranslationAnchor, functionStyleValue,
- @"iconTranslationAnchor should round-trip camera functions.");
+ @"Setting iconTranslationAnchor to a camera expression should update icon-translate-anchor.");
+ XCTAssertEqualObjects(layer.iconTranslationAnchor, functionExpression,
+ @"iconTranslationAnchor should round-trip camera expressions.");
layer.iconTranslationAnchor = nil;
XCTAssertTrue(rawLayer->getIconTranslateAnchor().isUndefined(),
@"Unsetting iconTranslationAnchor should return icon-translate-anchor to the default value.");
- XCTAssertEqualObjects(layer.iconTranslationAnchor, defaultStyleValue,
+ XCTAssertEqualObjects(layer.iconTranslationAnchor, defaultExpression,
@"iconTranslationAnchor should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.iconTranslationAnchor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.iconTranslationAnchor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.iconTranslationAnchor = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.iconTranslationAnchor = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// text-color
{
XCTAssertTrue(rawLayer->getTextColor().isUndefined(),
@"text-color should be unset initially.");
- MGLStyleValue<MGLColor *> *defaultStyleValue = layer.textColor;
+ NSExpression *defaultExpression = layer.textColor;
- MGLStyleValue<MGLColor *> *constantStyleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]];
- layer.textColor = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
+ layer.textColor = constantExpression;
mbgl::style::DataDrivenPropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } };
XCTAssertEqual(rawLayer->getTextColor(), propertyValue,
- @"Setting textColor to a constant value should update text-color.");
- XCTAssertEqualObjects(layer.textColor, constantStyleValue,
- @"textColor should round-trip constant values.");
-
- MGLStyleValue<MGLColor *> * functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.textColor = functionStyleValue;
-
- mbgl::style::IntervalStops<mbgl::Color> intervalStops = { {{18, { 1, 0, 0, 1 }}} };
+ @"Setting textColor to a constant value expression should update text-color.");
+ XCTAssertEqualObjects(layer.textColor, constantExpression,
+ @"textColor should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.textColor = functionExpression;
+
+ mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{
+ { -INFINITY, { 1, 0, 0, 1 } },
+ { 18, { 1, 0, 0, 1 } },
+ }};
propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops };
XCTAssertEqual(rawLayer->getTextColor(), propertyValue,
- @"Setting textColor to a camera function should update text-color.");
- XCTAssertEqualObjects(layer.textColor, functionStyleValue,
- @"textColor should round-trip camera functions.");
+ @"Setting textColor to a camera expression should update text-color.");
+ XCTAssertEqualObjects(layer.textColor, functionExpression,
+ @"textColor should round-trip camera expressions.");
- functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
- layer.textColor = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.textColor = functionExpression;
mbgl::style::ExponentialStops<mbgl::Color> exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<mbgl::Color> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getTextColor(), propertyValue,
- @"Setting textColor to a source function should update text-color.");
- XCTAssertEqualObjects(layer.textColor, functionStyleValue,
- @"textColor should round-trip source functions.");
+ @"Setting textColor to a data expression should update text-color.");
+ XCTAssertEqualObjects(layer.textColor, functionExpression,
+ @"textColor should round-trip data expressions.");
- functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
- layer.textColor = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.textColor = functionExpression;
std::map<float, mbgl::Color> innerStops { {18, { 1, 0, 0, 1 }} };
mbgl::style::CompositeExponentialStops<mbgl::Color> compositeStops { { {10.0, innerStops} }, 1.0 };
@@ -2043,15 +2237,15 @@
propertyValue = mbgl::style::CompositeFunction<mbgl::Color> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getTextColor(), propertyValue,
- @"Setting textColor to a composite function should update text-color.");
- XCTAssertEqualObjects(layer.textColor, functionStyleValue,
- @"textColor should round-trip composite functions.");
+ @"Setting textColor to a camera-data expression should update text-color.");
+ XCTAssertEqualObjects(layer.textColor, functionExpression,
+ @"textColor should round-trip camera-data expressions.");
layer.textColor = nil;
XCTAssertTrue(rawLayer->getTextColor().isUndefined(),
@"Unsetting textColor should return text-color to the default value.");
- XCTAssertEqualObjects(layer.textColor, defaultStyleValue,
+ XCTAssertEqualObjects(layer.textColor, defaultExpression,
@"textColor should return the default value after being unset.");
// Transition property test
layer.textColorTransition = transitionTest;
@@ -2068,40 +2262,44 @@
{
XCTAssertTrue(rawLayer->getTextHaloBlur().isUndefined(),
@"text-halo-blur should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.textHaloBlur;
+ NSExpression *defaultExpression = layer.textHaloBlur;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.textHaloBlur = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.textHaloBlur = constantExpression;
mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getTextHaloBlur(), propertyValue,
- @"Setting textHaloBlur to a constant value should update text-halo-blur.");
- XCTAssertEqualObjects(layer.textHaloBlur, constantStyleValue,
- @"textHaloBlur should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.textHaloBlur = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting textHaloBlur to a constant value expression should update text-halo-blur.");
+ XCTAssertEqualObjects(layer.textHaloBlur, constantExpression,
+ @"textHaloBlur should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.textHaloBlur = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getTextHaloBlur(), propertyValue,
- @"Setting textHaloBlur to a camera function should update text-halo-blur.");
- XCTAssertEqualObjects(layer.textHaloBlur, functionStyleValue,
- @"textHaloBlur should round-trip camera functions.");
+ @"Setting textHaloBlur to a camera expression should update text-halo-blur.");
+ XCTAssertEqualObjects(layer.textHaloBlur, functionExpression,
+ @"textHaloBlur should round-trip camera expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
- layer.textHaloBlur = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.textHaloBlur = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getTextHaloBlur(), propertyValue,
- @"Setting textHaloBlur to a source function should update text-halo-blur.");
- XCTAssertEqualObjects(layer.textHaloBlur, functionStyleValue,
- @"textHaloBlur should round-trip source functions.");
+ @"Setting textHaloBlur to a data expression should update text-halo-blur.");
+ XCTAssertEqualObjects(layer.textHaloBlur, functionExpression,
+ @"textHaloBlur should round-trip data expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
- layer.textHaloBlur = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.textHaloBlur = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 };
@@ -2109,15 +2307,15 @@
propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getTextHaloBlur(), propertyValue,
- @"Setting textHaloBlur to a composite function should update text-halo-blur.");
- XCTAssertEqualObjects(layer.textHaloBlur, functionStyleValue,
- @"textHaloBlur should round-trip composite functions.");
+ @"Setting textHaloBlur to a camera-data expression should update text-halo-blur.");
+ XCTAssertEqualObjects(layer.textHaloBlur, functionExpression,
+ @"textHaloBlur should round-trip camera-data expressions.");
layer.textHaloBlur = nil;
XCTAssertTrue(rawLayer->getTextHaloBlur().isUndefined(),
@"Unsetting textHaloBlur should return text-halo-blur to the default value.");
- XCTAssertEqualObjects(layer.textHaloBlur, defaultStyleValue,
+ XCTAssertEqualObjects(layer.textHaloBlur, defaultExpression,
@"textHaloBlur should return the default value after being unset.");
// Transition property test
layer.textHaloBlurTransition = transitionTest;
@@ -2134,40 +2332,44 @@
{
XCTAssertTrue(rawLayer->getTextHaloColor().isUndefined(),
@"text-halo-color should be unset initially.");
- MGLStyleValue<MGLColor *> *defaultStyleValue = layer.textHaloColor;
+ NSExpression *defaultExpression = layer.textHaloColor;
- MGLStyleValue<MGLColor *> *constantStyleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]];
- layer.textHaloColor = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
+ layer.textHaloColor = constantExpression;
mbgl::style::DataDrivenPropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } };
XCTAssertEqual(rawLayer->getTextHaloColor(), propertyValue,
- @"Setting textHaloColor to a constant value should update text-halo-color.");
- XCTAssertEqualObjects(layer.textHaloColor, constantStyleValue,
- @"textHaloColor should round-trip constant values.");
-
- MGLStyleValue<MGLColor *> * functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.textHaloColor = functionStyleValue;
-
- mbgl::style::IntervalStops<mbgl::Color> intervalStops = { {{18, { 1, 0, 0, 1 }}} };
+ @"Setting textHaloColor to a constant value expression should update text-halo-color.");
+ XCTAssertEqualObjects(layer.textHaloColor, constantExpression,
+ @"textHaloColor should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.textHaloColor = functionExpression;
+
+ mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{
+ { -INFINITY, { 1, 0, 0, 1 } },
+ { 18, { 1, 0, 0, 1 } },
+ }};
propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops };
XCTAssertEqual(rawLayer->getTextHaloColor(), propertyValue,
- @"Setting textHaloColor to a camera function should update text-halo-color.");
- XCTAssertEqualObjects(layer.textHaloColor, functionStyleValue,
- @"textHaloColor should round-trip camera functions.");
+ @"Setting textHaloColor to a camera expression should update text-halo-color.");
+ XCTAssertEqualObjects(layer.textHaloColor, functionExpression,
+ @"textHaloColor should round-trip camera expressions.");
- functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
- layer.textHaloColor = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.textHaloColor = functionExpression;
mbgl::style::ExponentialStops<mbgl::Color> exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<mbgl::Color> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getTextHaloColor(), propertyValue,
- @"Setting textHaloColor to a source function should update text-halo-color.");
- XCTAssertEqualObjects(layer.textHaloColor, functionStyleValue,
- @"textHaloColor should round-trip source functions.");
+ @"Setting textHaloColor to a data expression should update text-halo-color.");
+ XCTAssertEqualObjects(layer.textHaloColor, functionExpression,
+ @"textHaloColor should round-trip data expressions.");
- functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
- layer.textHaloColor = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.textHaloColor = functionExpression;
std::map<float, mbgl::Color> innerStops { {18, { 1, 0, 0, 1 }} };
mbgl::style::CompositeExponentialStops<mbgl::Color> compositeStops { { {10.0, innerStops} }, 1.0 };
@@ -2175,15 +2377,15 @@
propertyValue = mbgl::style::CompositeFunction<mbgl::Color> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getTextHaloColor(), propertyValue,
- @"Setting textHaloColor to a composite function should update text-halo-color.");
- XCTAssertEqualObjects(layer.textHaloColor, functionStyleValue,
- @"textHaloColor should round-trip composite functions.");
+ @"Setting textHaloColor to a camera-data expression should update text-halo-color.");
+ XCTAssertEqualObjects(layer.textHaloColor, functionExpression,
+ @"textHaloColor should round-trip camera-data expressions.");
layer.textHaloColor = nil;
XCTAssertTrue(rawLayer->getTextHaloColor().isUndefined(),
@"Unsetting textHaloColor should return text-halo-color to the default value.");
- XCTAssertEqualObjects(layer.textHaloColor, defaultStyleValue,
+ XCTAssertEqualObjects(layer.textHaloColor, defaultExpression,
@"textHaloColor should return the default value after being unset.");
// Transition property test
layer.textHaloColorTransition = transitionTest;
@@ -2200,40 +2402,44 @@
{
XCTAssertTrue(rawLayer->getTextHaloWidth().isUndefined(),
@"text-halo-width should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.textHaloWidth;
+ NSExpression *defaultExpression = layer.textHaloWidth;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.textHaloWidth = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.textHaloWidth = constantExpression;
mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getTextHaloWidth(), propertyValue,
- @"Setting textHaloWidth to a constant value should update text-halo-width.");
- XCTAssertEqualObjects(layer.textHaloWidth, constantStyleValue,
- @"textHaloWidth should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.textHaloWidth = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting textHaloWidth to a constant value expression should update text-halo-width.");
+ XCTAssertEqualObjects(layer.textHaloWidth, constantExpression,
+ @"textHaloWidth should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.textHaloWidth = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getTextHaloWidth(), propertyValue,
- @"Setting textHaloWidth to a camera function should update text-halo-width.");
- XCTAssertEqualObjects(layer.textHaloWidth, functionStyleValue,
- @"textHaloWidth should round-trip camera functions.");
+ @"Setting textHaloWidth to a camera expression should update text-halo-width.");
+ XCTAssertEqualObjects(layer.textHaloWidth, functionExpression,
+ @"textHaloWidth should round-trip camera expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
- layer.textHaloWidth = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.textHaloWidth = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getTextHaloWidth(), propertyValue,
- @"Setting textHaloWidth to a source function should update text-halo-width.");
- XCTAssertEqualObjects(layer.textHaloWidth, functionStyleValue,
- @"textHaloWidth should round-trip source functions.");
+ @"Setting textHaloWidth to a data expression should update text-halo-width.");
+ XCTAssertEqualObjects(layer.textHaloWidth, functionExpression,
+ @"textHaloWidth should round-trip data expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
- layer.textHaloWidth = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.textHaloWidth = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 };
@@ -2241,15 +2447,15 @@
propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getTextHaloWidth(), propertyValue,
- @"Setting textHaloWidth to a composite function should update text-halo-width.");
- XCTAssertEqualObjects(layer.textHaloWidth, functionStyleValue,
- @"textHaloWidth should round-trip composite functions.");
+ @"Setting textHaloWidth to a camera-data expression should update text-halo-width.");
+ XCTAssertEqualObjects(layer.textHaloWidth, functionExpression,
+ @"textHaloWidth should round-trip camera-data expressions.");
layer.textHaloWidth = nil;
XCTAssertTrue(rawLayer->getTextHaloWidth().isUndefined(),
@"Unsetting textHaloWidth should return text-halo-width to the default value.");
- XCTAssertEqualObjects(layer.textHaloWidth, defaultStyleValue,
+ XCTAssertEqualObjects(layer.textHaloWidth, defaultExpression,
@"textHaloWidth should return the default value after being unset.");
// Transition property test
layer.textHaloWidthTransition = transitionTest;
@@ -2266,40 +2472,44 @@
{
XCTAssertTrue(rawLayer->getTextOpacity().isUndefined(),
@"text-opacity should be unset initially.");
- MGLStyleValue<NSNumber *> *defaultStyleValue = layer.textOpacity;
+ NSExpression *defaultExpression = layer.textOpacity;
- MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
- layer.textOpacity = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ layer.textOpacity = constantExpression;
mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getTextOpacity(), propertyValue,
- @"Setting textOpacity to a constant value should update text-opacity.");
- XCTAssertEqualObjects(layer.textOpacity, constantStyleValue,
- @"textOpacity should round-trip constant values.");
-
- MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.textOpacity = functionStyleValue;
-
- mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ @"Setting textOpacity to a constant value expression should update text-opacity.");
+ XCTAssertEqualObjects(layer.textOpacity, constantExpression,
+ @"textOpacity should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"0xff"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.textOpacity = functionExpression;
+
+ mbgl::style::IntervalStops<float> intervalStops = {{
+ { -INFINITY, 0xff },
+ { 18, 0xff },
+ }};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getTextOpacity(), propertyValue,
- @"Setting textOpacity to a camera function should update text-opacity.");
- XCTAssertEqualObjects(layer.textOpacity, functionStyleValue,
- @"textOpacity should round-trip camera functions.");
+ @"Setting textOpacity to a camera expression should update text-opacity.");
+ XCTAssertEqualObjects(layer.textOpacity, functionExpression,
+ @"textOpacity should round-trip camera expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
- layer.textOpacity = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ layer.textOpacity = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getTextOpacity(), propertyValue,
- @"Setting textOpacity to a source function should update text-opacity.");
- XCTAssertEqualObjects(layer.textOpacity, functionStyleValue,
- @"textOpacity should round-trip source functions.");
+ @"Setting textOpacity to a data expression should update text-opacity.");
+ XCTAssertEqualObjects(layer.textOpacity, functionExpression,
+ @"textOpacity should round-trip data expressions.");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
- layer.textOpacity = functionStyleValue;
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ layer.textOpacity = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 };
@@ -2307,15 +2517,15 @@
propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getTextOpacity(), propertyValue,
- @"Setting textOpacity to a composite function should update text-opacity.");
- XCTAssertEqualObjects(layer.textOpacity, functionStyleValue,
- @"textOpacity should round-trip composite functions.");
+ @"Setting textOpacity to a camera-data expression should update text-opacity.");
+ XCTAssertEqualObjects(layer.textOpacity, functionExpression,
+ @"textOpacity should round-trip camera-data expressions.");
layer.textOpacity = nil;
XCTAssertTrue(rawLayer->getTextOpacity().isUndefined(),
@"Unsetting textOpacity should return text-opacity to the default value.");
- XCTAssertEqualObjects(layer.textOpacity, defaultStyleValue,
+ XCTAssertEqualObjects(layer.textOpacity, defaultExpression,
@"textOpacity should return the default value after being unset.");
// Transition property test
layer.textOpacityTransition = transitionTest;
@@ -2332,84 +2542,94 @@
{
XCTAssertTrue(rawLayer->getTextTranslate().isUndefined(),
@"text-translate should be unset initially.");
- MGLStyleValue<NSValue *> *defaultStyleValue = layer.textTranslation;
+ NSExpression *defaultExpression = layer.textTranslation;
- MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@",
#if TARGET_OS_IPHONE
[NSValue valueWithCGVector:CGVectorMake(1, 1)]
#else
[NSValue valueWithMGLVector:CGVectorMake(1, -1)]
#endif
];
- layer.textTranslation = constantStyleValue;
+ layer.textTranslation = constantExpression;
mbgl::style::PropertyValue<std::array<float, 2>> propertyValue = { { 1, 1 } };
XCTAssertEqual(rawLayer->getTextTranslate(), propertyValue,
- @"Setting textTranslation to a constant value should update text-translate.");
- XCTAssertEqualObjects(layer.textTranslation, constantStyleValue,
- @"textTranslation should round-trip constant values.");
-
- MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.textTranslation = functionStyleValue;
-
- mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = { {{18, { 1, 1 }}} };
+ @"Setting textTranslation to a constant value expression should update text-translate.");
+ XCTAssertEqualObjects(layer.textTranslation, constantExpression,
+ @"textTranslation should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"{1, 1}"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.textTranslation = functionExpression;
+
+ mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = {{
+ { -INFINITY, { 1, 1 } },
+ { 18, { 1, 1 } },
+ }};
propertyValue = mbgl::style::CameraFunction<std::array<float, 2>> { intervalStops };
XCTAssertEqual(rawLayer->getTextTranslate(), propertyValue,
- @"Setting textTranslation to a camera function should update text-translate.");
- XCTAssertEqualObjects(layer.textTranslation, functionStyleValue,
- @"textTranslation should round-trip camera functions.");
+ @"Setting textTranslation to a camera expression should update text-translate.");
+ XCTAssertEqualObjects(layer.textTranslation, functionExpression,
+ @"textTranslation should round-trip camera expressions.");
layer.textTranslation = nil;
XCTAssertTrue(rawLayer->getTextTranslate().isUndefined(),
@"Unsetting textTranslation should return text-translate to the default value.");
- XCTAssertEqualObjects(layer.textTranslation, defaultStyleValue,
+ XCTAssertEqualObjects(layer.textTranslation, defaultExpression,
@"textTranslation should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.textTranslation = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.textTranslation = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.textTranslation = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.textTranslation = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
// text-translate-anchor
{
XCTAssertTrue(rawLayer->getTextTranslateAnchor().isUndefined(),
@"text-translate-anchor should be unset initially.");
- MGLStyleValue<NSValue *> *defaultStyleValue = layer.textTranslationAnchor;
+ NSExpression *defaultExpression = layer.textTranslationAnchor;
- MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLTextTranslationAnchor:MGLTextTranslationAnchorViewport]];
- layer.textTranslationAnchor = constantStyleValue;
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'viewport'"];
+ layer.textTranslationAnchor = constantExpression;
mbgl::style::PropertyValue<mbgl::style::TranslateAnchorType> propertyValue = { mbgl::style::TranslateAnchorType::Viewport };
XCTAssertEqual(rawLayer->getTextTranslateAnchor(), propertyValue,
- @"Setting textTranslationAnchor to a constant value should update text-translate-anchor.");
- XCTAssertEqualObjects(layer.textTranslationAnchor, constantStyleValue,
- @"textTranslationAnchor should round-trip constant values.");
-
- MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
- layer.textTranslationAnchor = functionStyleValue;
-
- mbgl::style::IntervalStops<mbgl::style::TranslateAnchorType> intervalStops = { {{18, mbgl::style::TranslateAnchorType::Viewport}} };
+ @"Setting textTranslationAnchor to a constant value expression should update text-translate-anchor.");
+ XCTAssertEqualObjects(layer.textTranslationAnchor, constantExpression,
+ @"textTranslationAnchor should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"'viewport'"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.textTranslationAnchor = functionExpression;
+
+ mbgl::style::IntervalStops<mbgl::style::TranslateAnchorType> intervalStops = {{
+ { -INFINITY, mbgl::style::TranslateAnchorType::Viewport },
+ { 18, mbgl::style::TranslateAnchorType::Viewport },
+ }};
propertyValue = mbgl::style::CameraFunction<mbgl::style::TranslateAnchorType> { intervalStops };
XCTAssertEqual(rawLayer->getTextTranslateAnchor(), propertyValue,
- @"Setting textTranslationAnchor to a camera function should update text-translate-anchor.");
- XCTAssertEqualObjects(layer.textTranslationAnchor, functionStyleValue,
- @"textTranslationAnchor should round-trip camera functions.");
+ @"Setting textTranslationAnchor to a camera expression should update text-translate-anchor.");
+ XCTAssertEqualObjects(layer.textTranslationAnchor, functionExpression,
+ @"textTranslationAnchor should round-trip camera expressions.");
layer.textTranslationAnchor = nil;
XCTAssertTrue(rawLayer->getTextTranslateAnchor().isUndefined(),
@"Unsetting textTranslationAnchor should return text-translate-anchor to the default value.");
- XCTAssertEqualObjects(layer.textTranslationAnchor, defaultStyleValue,
+ XCTAssertEqualObjects(layer.textTranslationAnchor, defaultExpression,
@"textTranslationAnchor should return the default value after being unset.");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.textTranslationAnchor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.textTranslationAnchor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.textTranslationAnchor = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.textTranslationAnchor = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
}
diff --git a/platform/darwin/test/test-Bridging-Header.h b/platform/darwin/test/test-Bridging-Header.h
index 5d23e9d6c5..1b2cb5d6d0 100644
--- a/platform/darwin/test/test-Bridging-Header.h
+++ b/platform/darwin/test/test-Bridging-Header.h
@@ -1,4 +1,4 @@
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//
-#import "MGLStyleValueTests.h"
+
diff --git a/platform/default/asset_file_source.cpp b/platform/default/asset_file_source.cpp
index 54dbb8d0f6..3063bf88a0 100644
--- a/platform/default/asset_file_source.cpp
+++ b/platform/default/asset_file_source.cpp
@@ -10,6 +10,12 @@
#include <sys/types.h>
#include <sys/stat.h>
+namespace {
+
+const std::string assetProtocol = "asset://";
+
+} // namespace
+
namespace mbgl {
class AssetFileSource::Impl {
@@ -19,18 +25,17 @@ public:
}
void request(const std::string& url, ActorRef<FileSourceRequest> req) {
- std::string path;
+ Response response;
- if (url.size() <= 8 || url[8] == '/') {
- // This is an empty or absolute path.
- path = mbgl::util::percentDecode(url.substr(8));
- } else {
- // This is a relative path. Prefix with the application root.
- path = root + "/" + mbgl::util::percentDecode(url.substr(8));
+ if (!acceptsURL(url)) {
+ response.error = std::make_unique<Response::Error>(Response::Error::Reason::Other,
+ "Invalid asset URL");
+ req.invoke(&FileSourceRequest::setResponse, response);
+ return;
}
- Response response;
-
+ // Cut off the protocol and prefix with path.
+ const auto path = root + "/" + mbgl::util::percentDecode(url.substr(assetProtocol.size()));
struct stat buf;
int result = stat(path.c_str(), &buf);
@@ -69,4 +74,8 @@ std::unique_ptr<AsyncRequest> AssetFileSource::request(const Resource& resource,
return std::move(req);
}
+bool AssetFileSource::acceptsURL(const std::string& url) {
+ return std::equal(assetProtocol.begin(), assetProtocol.end(), url.begin());
+}
+
} // namespace mbgl
diff --git a/platform/default/default_file_source.cpp b/platform/default/default_file_source.cpp
index 608b782ab9..cb602995a4 100644
--- a/platform/default/default_file_source.cpp
+++ b/platform/default/default_file_source.cpp
@@ -14,16 +14,6 @@
#include <cassert>
-namespace {
-
-const std::string assetProtocol = "asset://";
-
-bool isAssetURL(const std::string& url) {
- return std::equal(assetProtocol.begin(), assetProtocol.end(), url.begin());
-}
-
-} // namespace
-
namespace mbgl {
class DefaultFileSource::Impl {
@@ -118,7 +108,7 @@ public:
ref.invoke(&FileSourceRequest::setResponse, res);
};
- if (isAssetURL(resource.url)) {
+ if (AssetFileSource::acceptsURL(resource.url)) {
//Asset request
tasks[req] = assetFileSource->request(resource, callback);
} else if (LocalFileSource::acceptsURL(resource.url)) {
diff --git a/platform/default/headless_backend_osmesa.cpp b/platform/default/headless_backend_osmesa.cpp
index 5042f5ed10..0da1caf9af 100644
--- a/platform/default/headless_backend_osmesa.cpp
+++ b/platform/default/headless_backend_osmesa.cpp
@@ -7,45 +7,41 @@
namespace mbgl {
-struct OSMesaImpl : public HeadlessBackend::Impl {
- OSMesaImpl(OSMesaContext glContext_) : glContext(glContext_) {
+class OSMesaBackendImpl : public HeadlessBackend::Impl {
+public:
+ OSMesaBackendImpl() {
+#if OSMESA_MAJOR_VERSION * 100 + OSMESA_MINOR_VERSION >= 305
+ glContext = OSMesaCreateContextExt(OSMESA_RGBA, 16, 0, 0, nullptr);
+#else
+ glContext = OSMesaCreateContext(OSMESA_RGBA, nullptr);
+#endif
+ if (glContext == nullptr) {
+ throw std::runtime_error("Error creating GL context object.");
+ }
}
- ~OSMesaImpl() {
+ ~OSMesaBackendImpl() final {
OSMesaDestroyContext(glContext);
}
+ gl::ProcAddress getExtensionFunctionPointer(const char* name) final {
+ return OSMesaGetProcAddress(name);
+ }
+
void activateContext() final {
if (!OSMesaMakeCurrent(glContext, &fakeBuffer, GL_UNSIGNED_BYTE, 1, 1)) {
throw std::runtime_error("Switching OpenGL context failed.\n");
}
}
+private:
OSMesaContext glContext = nullptr;
GLubyte fakeBuffer = 0;
};
-gl::ProcAddress HeadlessBackend::initializeExtension(const char* name) {
- return OSMesaGetProcAddress(name);
-}
-
-bool HeadlessBackend::hasDisplay() {
- return true;
-};
-
-void HeadlessBackend::createContext() {
- assert(!hasContext());
-
-#if OSMESA_MAJOR_VERSION * 100 + OSMESA_MINOR_VERSION >= 305
- OSMesaContext glContext = OSMesaCreateContextExt(OSMESA_RGBA, 16, 0, 0, nullptr);
-#else
- OSMesaContext glContext = OSMesaCreateContext(OSMESA_RGBA, nullptr);
-#endif
- if (glContext == nullptr) {
- throw std::runtime_error("Error creating GL context object.");
- }
-
- impl.reset(new OSMesaImpl(glContext));
+void HeadlessBackend::createImpl() {
+ assert(!impl);
+ impl = std::make_unique<OSMesaBackendImpl>();
}
} // namespace mbgl
diff --git a/platform/default/local_file_source.cpp b/platform/default/local_file_source.cpp
index 21a291d8d4..0635e86d80 100644
--- a/platform/default/local_file_source.cpp
+++ b/platform/default/local_file_source.cpp
@@ -9,12 +9,14 @@
#include <sys/types.h>
#include <sys/stat.h>
-#include <unistd.h>
+
+#if defined(_WINDOWS) && !defined(S_ISDIR)
+#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#endif
namespace {
-const char* protocol = "file://";
-const std::size_t protocolLength = 7;
+const std::string fileProtocol = "file://";
} // namespace
@@ -25,11 +27,17 @@ public:
Impl(ActorRef<Impl>) {}
void request(const std::string& url, ActorRef<FileSourceRequest> req) {
- // Cut off the protocol
- std::string path = mbgl::util::percentDecode(url.substr(protocolLength));
-
Response response;
+ if (!acceptsURL(url)) {
+ response.error = std::make_unique<Response::Error>(Response::Error::Reason::Other,
+ "Invalid file URL");
+ req.invoke(&FileSourceRequest::setResponse, response);
+ return;
+ }
+
+ // Cut off the protocol and prefix with path.
+ const auto path = mbgl::util::percentDecode(url.substr(fileProtocol.size()));
struct stat buf;
int result = stat(path.c_str(), &buf);
@@ -67,7 +75,7 @@ std::unique_ptr<AsyncRequest> LocalFileSource::request(const Resource& resource,
}
bool LocalFileSource::acceptsURL(const std::string& url) {
- return url.compare(0, protocolLength, protocol) == 0;
+ return std::equal(fileProtocol.begin(), fileProtocol.end(), url.begin());
}
} // namespace mbgl
diff --git a/platform/default/mbgl/gl/headless_backend.cpp b/platform/default/mbgl/gl/headless_backend.cpp
index edf637a560..ba08aecab7 100644
--- a/platform/default/mbgl/gl/headless_backend.cpp
+++ b/platform/default/mbgl/gl/headless_backend.cpp
@@ -1,5 +1,4 @@
#include <mbgl/gl/headless_backend.hpp>
-#include <mbgl/gl/headless_display.hpp>
#include <mbgl/gl/context.hpp>
#include <mbgl/renderer/backend_scope.hpp>
@@ -32,22 +31,24 @@ HeadlessBackend::~HeadlessBackend() {
context.reset();
}
+gl::ProcAddress HeadlessBackend::getExtensionFunctionPointer(const char* name) {
+ assert(impl);
+ return impl->getExtensionFunctionPointer(name);
+}
+
void HeadlessBackend::activate() {
active = true;
- if (!hasContext()) {
- if (!hasDisplay()) {
- throw std::runtime_error("Display is not set");
- }
- createContext();
+ if (!impl) {
+ createImpl();
}
- assert(hasContext());
+ assert(impl);
impl->activateContext();
}
void HeadlessBackend::deactivate() {
- assert(hasContext());
+ assert(impl);
impl->deactivateContext();
active = false;
}
diff --git a/platform/default/mbgl/gl/headless_backend.hpp b/platform/default/mbgl/gl/headless_backend.hpp
index 66f861e213..7757037533 100644
--- a/platform/default/mbgl/gl/headless_backend.hpp
+++ b/platform/default/mbgl/gl/headless_backend.hpp
@@ -7,8 +7,6 @@
namespace mbgl {
-class HeadlessDisplay;
-
class HeadlessBackend : public RendererBackend {
public:
HeadlessBackend(Size = { 256, 256 });
@@ -21,25 +19,24 @@ public:
void setSize(Size);
PremultipliedImage readStillImage();
- struct Impl {
+ class Impl {
+ public:
virtual ~Impl() = default;
+ virtual gl::ProcAddress getExtensionFunctionPointer(const char*) = 0;
virtual void activateContext() = 0;
virtual void deactivateContext() {}
};
private:
// Implementation specific functions
- gl::ProcAddress initializeExtension(const char*) override;
+ gl::ProcAddress getExtensionFunctionPointer(const char*) override;
void activate() override;
void deactivate() override;
- bool hasContext() const { return bool(impl); }
- bool hasDisplay();
-
- void createContext();
+ void createImpl();
- std::shared_ptr<HeadlessDisplay> display;
+private:
std::unique_ptr<Impl> impl;
Size size;
diff --git a/platform/default/mbgl/gl/headless_display.cpp b/platform/default/mbgl/gl/headless_display.cpp
deleted file mode 100644
index 6247046c29..0000000000
--- a/platform/default/mbgl/gl/headless_display.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-#include <mbgl/gl/headless_display.hpp>
-
-namespace mbgl {
-
-class HeadlessDisplay::Impl {};
-
-HeadlessDisplay::HeadlessDisplay() {
- // no-op
-}
-
-HeadlessDisplay::~HeadlessDisplay() {
- // no-op
-}
-
-} // namespace mbgl
diff --git a/platform/default/mbgl/gl/headless_display.hpp b/platform/default/mbgl/gl/headless_display.hpp
deleted file mode 100644
index 8c294655e5..0000000000
--- a/platform/default/mbgl/gl/headless_display.hpp
+++ /dev/null
@@ -1,34 +0,0 @@
-#pragma once
-
-#include <memory>
-
-namespace mbgl {
-
-class HeadlessDisplay {
-public:
- static std::shared_ptr<HeadlessDisplay> create() {
- static std::weak_ptr<HeadlessDisplay> instance;
-
- auto shared = instance.lock();
-
- if (!shared) {
- instance = shared = std::shared_ptr<HeadlessDisplay>(new HeadlessDisplay());
- }
-
- return shared;
- }
-
-
- ~HeadlessDisplay();
-
- template <typename DisplayAttribute>
- DisplayAttribute attribute() const;
-
-private:
- HeadlessDisplay();
-
- class Impl;
- std::unique_ptr<Impl> impl;
-};
-
-} // namespace mbgl
diff --git a/platform/default/mbgl/gl/headless_frontend.cpp b/platform/default/mbgl/gl/headless_frontend.cpp
index 2cbb624bd0..4263d2b148 100644
--- a/platform/default/mbgl/gl/headless_frontend.cpp
+++ b/platform/default/mbgl/gl/headless_frontend.cpp
@@ -7,11 +7,11 @@
namespace mbgl {
-HeadlessFrontend::HeadlessFrontend(float pixelRatio_, FileSource& fileSource, Scheduler& scheduler, const optional<std::string> programCacheDir)
- : HeadlessFrontend({ 256, 256 }, pixelRatio_, fileSource, scheduler, programCacheDir) {
+HeadlessFrontend::HeadlessFrontend(float pixelRatio_, FileSource& fileSource, Scheduler& scheduler, const optional<std::string> programCacheDir, GLContextMode mode, const optional<std::string> localFontFamily)
+ : HeadlessFrontend({ 256, 256 }, pixelRatio_, fileSource, scheduler, programCacheDir, mode, localFontFamily) {
}
-HeadlessFrontend::HeadlessFrontend(Size size_, float pixelRatio_, FileSource& fileSource, Scheduler& scheduler, const optional<std::string> programCacheDir)
+HeadlessFrontend::HeadlessFrontend(Size size_, float pixelRatio_, FileSource& fileSource, Scheduler& scheduler, const optional<std::string> programCacheDir, GLContextMode mode, const optional<std::string> localFontFamily)
: size(size_),
pixelRatio(pixelRatio_),
backend({ static_cast<uint32_t>(size.width * pixelRatio),
@@ -22,7 +22,7 @@ HeadlessFrontend::HeadlessFrontend(Size size_, float pixelRatio_, FileSource& fi
renderer->render(*updateParameters);
}
}),
- renderer(std::make_unique<Renderer>(backend, pixelRatio, fileSource, scheduler, GLContextMode::Unique, programCacheDir)) {
+ renderer(std::make_unique<Renderer>(backend, pixelRatio, fileSource, scheduler, mode, programCacheDir, localFontFamily)) {
}
HeadlessFrontend::~HeadlessFrontend() = default;
diff --git a/platform/default/mbgl/gl/headless_frontend.hpp b/platform/default/mbgl/gl/headless_frontend.hpp
index 4d1116904e..8ae617d37b 100644
--- a/platform/default/mbgl/gl/headless_frontend.hpp
+++ b/platform/default/mbgl/gl/headless_frontend.hpp
@@ -1,5 +1,6 @@
#pragma once
+#include <mbgl/renderer/mode.hpp>
#include <mbgl/renderer/renderer_frontend.hpp>
#include <mbgl/gl/headless_backend.hpp>
#include <mbgl/util/async_task.hpp>
@@ -18,8 +19,8 @@ class TransformState;
class HeadlessFrontend : public RendererFrontend {
public:
- HeadlessFrontend(float pixelRatio_, FileSource&, Scheduler&, const optional<std::string> programCacheDir = {});
- HeadlessFrontend(Size, float pixelRatio_, FileSource&, Scheduler&, const optional<std::string> programCacheDir = {});
+ HeadlessFrontend(float pixelRatio_, FileSource&, Scheduler&, const optional<std::string> programCacheDir = {}, GLContextMode mode = GLContextMode::Unique, const optional<std::string> localFontFamily = {});
+ HeadlessFrontend(Size, float pixelRatio_, FileSource&, Scheduler&, const optional<std::string> programCacheDir = {}, GLContextMode mode = GLContextMode::Unique, const optional<std::string> localFontFamily = {});
~HeadlessFrontend() override;
void reset() override;
diff --git a/platform/default/mbgl/map/map_snapshotter.cpp b/platform/default/mbgl/map/map_snapshotter.cpp
index 7b4ec5913b..9341c23cfd 100644
--- a/platform/default/mbgl/map/map_snapshotter.cpp
+++ b/platform/default/mbgl/map/map_snapshotter.cpp
@@ -50,7 +50,7 @@ MapSnapshotter::Impl::Impl(FileSource& fileSource,
const optional<LatLngBounds> region,
const optional<std::string> programCacheDir)
: frontend(size, pixelRatio, fileSource, scheduler, programCacheDir)
- , map(frontend, MapObserver::nullObserver(), size, pixelRatio, fileSource, scheduler, MapMode::Still) {
+ , map(frontend, MapObserver::nullObserver(), size, pixelRatio, fileSource, scheduler, MapMode::Static) {
map.getStyle().loadURL(styleURL);
diff --git a/platform/default/mbgl/storage/offline.cpp b/platform/default/mbgl/storage/offline.cpp
index 9ec789f725..7670790be9 100644
--- a/platform/default/mbgl/storage/offline.cpp
+++ b/platform/default/mbgl/storage/offline.cpp
@@ -24,7 +24,7 @@ OfflineTilePyramidRegionDefinition::OfflineTilePyramidRegionDefinition(
}
}
-std::vector<CanonicalTileID> OfflineTilePyramidRegionDefinition::tileCover(SourceType type, uint16_t tileSize, const Range<uint8_t>& zoomRange) const {
+std::vector<CanonicalTileID> OfflineTilePyramidRegionDefinition::tileCover(style::SourceType type, uint16_t tileSize, const Range<uint8_t>& zoomRange) const {
const Range<uint8_t> clampedZoomRange = coveringZoomRange(type, tileSize, zoomRange);
std::vector<CanonicalTileID> result;
@@ -38,7 +38,7 @@ std::vector<CanonicalTileID> OfflineTilePyramidRegionDefinition::tileCover(Sourc
return result;
}
-uint64_t OfflineTilePyramidRegionDefinition::tileCount(SourceType type, uint16_t tileSize, const Range<uint8_t>& zoomRange) const {
+uint64_t OfflineTilePyramidRegionDefinition::tileCount(style::SourceType type, uint16_t tileSize, const Range<uint8_t>& zoomRange) const {
const Range<uint8_t> clampedZoomRange = coveringZoomRange(type, tileSize, zoomRange);
unsigned long result = 0;;
@@ -49,7 +49,7 @@ uint64_t OfflineTilePyramidRegionDefinition::tileCount(SourceType type, uint16_t
return result;
}
-Range<uint8_t> OfflineTilePyramidRegionDefinition::coveringZoomRange(SourceType type, uint16_t tileSize, const Range<uint8_t>& zoomRange) const {
+Range<uint8_t> OfflineTilePyramidRegionDefinition::coveringZoomRange(style::SourceType type, uint16_t tileSize, const Range<uint8_t>& zoomRange) const {
double minZ = std::max<double>(util::coveringZoomLevel(minZoom, type, tileSize), zoomRange.min);
double maxZ = std::min<double>(util::coveringZoomLevel(maxZoom, type, tileSize), zoomRange.max);
diff --git a/platform/default/mbgl/storage/offline_download.cpp b/platform/default/mbgl/storage/offline_download.cpp
index ff61114888..ba504c1f9b 100644
--- a/platform/default/mbgl/storage/offline_download.cpp
+++ b/platform/default/mbgl/storage/offline_download.cpp
@@ -7,6 +7,7 @@
#include <mbgl/style/parser.hpp>
#include <mbgl/style/sources/vector_source.hpp>
#include <mbgl/style/sources/raster_source.hpp>
+#include <mbgl/style/sources/raster_dem_source.hpp>
#include <mbgl/style/sources/geojson_source.hpp>
#include <mbgl/style/sources/image_source.hpp>
#include <mbgl/style/conversion/json.hpp>
@@ -110,6 +111,12 @@ OfflineRegionStatus OfflineDownload::getStatus() const {
handleTiledSource(rasterSource.getURLOrTileset(), rasterSource.getTileSize());
break;
}
+
+ case SourceType::RasterDEM: {
+ const auto& rasterDEMSource = *source->as<RasterDEMSource>();
+ handleTiledSource(rasterDEMSource.getURLOrTileset(), rasterDEMSource.getTileSize());
+ break;
+ }
case SourceType::GeoJSON: {
const auto& geojsonSource = *source->as<GeoJSONSource>();
@@ -129,6 +136,7 @@ OfflineRegionStatus OfflineDownload::getStatus() const {
case SourceType::Video:
case SourceType::Annotations:
+ case SourceType::CustomVector:
break;
}
}
@@ -194,6 +202,12 @@ void OfflineDownload::activateDownload() {
handleTiledSource(rasterSource.getURLOrTileset(), rasterSource.getTileSize());
break;
}
+
+ case SourceType::RasterDEM: {
+ const auto& rasterDEMSource = *source->as<RasterDEMSource>();
+ handleTiledSource(rasterDEMSource.getURLOrTileset(), rasterDEMSource.getTileSize());
+ break;
+ }
case SourceType::GeoJSON: {
const auto& geojsonSource = *source->as<GeoJSONSource>();
@@ -214,6 +228,7 @@ void OfflineDownload::activateDownload() {
case SourceType::Video:
case SourceType::Annotations:
+ case SourceType::CustomVector:
break;
}
}
diff --git a/platform/default/mbgl/storage/offline_download.hpp b/platform/default/mbgl/storage/offline_download.hpp
index c978ded931..437f221c11 100644
--- a/platform/default/mbgl/storage/offline_download.hpp
+++ b/platform/default/mbgl/storage/offline_download.hpp
@@ -60,7 +60,7 @@ private:
std::deque<Resource> resourcesRemaining;
void queueResource(Resource);
- void queueTiles(SourceType, uint16_t tileSize, const Tileset&);
+ void queueTiles(style::SourceType, uint16_t tileSize, const Tileset&);
};
} // namespace mbgl
diff --git a/platform/default/run_loop.cpp b/platform/default/run_loop.cpp
index 5bccd21d56..868ee72114 100644
--- a/platform/default/run_loop.cpp
+++ b/platform/default/run_loop.cpp
@@ -129,11 +129,8 @@ LOOP_HANDLE RunLoop::getLoopHandle() {
return Get()->impl->loop;
}
-void RunLoop::push(std::shared_ptr<WorkTask> task) {
- withMutex([&] {
- queue.push(std::move(task));
- impl->async->send();
- });
+void RunLoop::wake() {
+ impl->async->send();
}
void RunLoop::run() {
diff --git a/platform/glfw/glfw_view.cpp b/platform/glfw/glfw_view.cpp
index d591f897e0..3988419265 100644
--- a/platform/glfw/glfw_view.cpp
+++ b/platform/glfw/glfw_view.cpp
@@ -288,6 +288,7 @@ void GLFWView::onKey(GLFWwindow *window, int key, int /*scancode*/, int action,
case GLFW_KEY_8: view->addRandomShapeAnnotations(10); break;
case GLFW_KEY_9: view->addRandomShapeAnnotations(100); break;
case GLFW_KEY_0: view->addRandomShapeAnnotations(1000); break;
+ case GLFW_KEY_M: view->addAnimatedAnnotation(); break;
}
}
}
@@ -379,12 +380,36 @@ void GLFWView::addRandomShapeAnnotations(int count) {
}
}
+void GLFWView::addAnimatedAnnotation() {
+ const double started = glfwGetTime();
+ animatedAnnotationIDs.push_back(map->addAnnotation(mbgl::SymbolAnnotation { { 0, 0 } , "default_marker" }));
+ animatedAnnotationAddedTimes.push_back(started);
+}
+
+void GLFWView::updateAnimatedAnnotations() {
+ const double time = glfwGetTime();
+ for (size_t i = 0; i < animatedAnnotationIDs.size(); i++) {
+ auto dt = time - animatedAnnotationAddedTimes[i];
+
+ const double period = 10;
+ const double x = dt / period * 360 - 180;
+ const double y = std::sin(dt/ period * M_PI * 2.0) * 80;
+ map->updateAnnotation(animatedAnnotationIDs[i], mbgl::SymbolAnnotation { {x, y }, "default_marker" });
+ }
+}
+
void GLFWView::clearAnnotations() {
for (const auto& id : annotationIDs) {
map->removeAnnotation(id);
}
annotationIDs.clear();
+
+ for (const auto& id : animatedAnnotationIDs) {
+ map->removeAnnotation(id);
+ }
+
+ animatedAnnotationIDs.clear();
}
void GLFWView::popAnnotation() {
@@ -506,6 +531,8 @@ void GLFWView::run() {
if (animateRouteCallback)
animateRouteCallback(map);
+ updateAnimatedAnnotations();
+
activate();
rendererFrontend->render();
@@ -540,7 +567,7 @@ mbgl::Size GLFWView::getFramebufferSize() const {
return { static_cast<uint32_t>(fbWidth), static_cast<uint32_t>(fbHeight) };
}
-mbgl::gl::ProcAddress GLFWView::initializeExtension(const char* name) {
+mbgl::gl::ProcAddress GLFWView::getExtensionFunctionPointer(const char* name) {
return glfwGetProcAddress(name);
}
diff --git a/platform/glfw/glfw_view.hpp b/platform/glfw/glfw_view.hpp
index d3257f6b9a..d5acf697f7 100644
--- a/platform/glfw/glfw_view.hpp
+++ b/platform/glfw/glfw_view.hpp
@@ -52,7 +52,7 @@ public:
protected:
// mbgl::Backend implementation
- mbgl::gl::ProcAddress initializeExtension(const char*) override;
+ mbgl::gl::ProcAddress getExtensionFunctionPointer(const char*) override;
void activate() override;
void deactivate() override;
@@ -78,6 +78,8 @@ private:
void addRandomLineAnnotations(int count);
void addRandomShapeAnnotations(int count);
void addRandomCustomPointAnnotations(int count);
+ void addAnimatedAnnotation();
+ void updateAnimatedAnnotations();
void clearAnnotations();
void popAnnotation();
@@ -85,6 +87,9 @@ private:
mbgl::AnnotationIDs annotationIDs;
std::vector<std::string> spriteIDs;
+ mbgl::AnnotationIDs animatedAnnotationIDs;
+ std::vector<double> animatedAnnotationAddedTimes;
+
private:
void toggle3DExtrusions(bool visible);
diff --git a/platform/glfw/main.cpp b/platform/glfw/main.cpp
index dadd88c705..c4e0ecf692 100644
--- a/platform/glfw/main.cpp
+++ b/platform/glfw/main.cpp
@@ -10,10 +10,11 @@
#include <mbgl/style/style.hpp>
#include <mbgl/renderer/renderer.hpp>
+#include <args/args.hxx>
+
#include <csignal>
-#include <getopt.h>
#include <fstream>
-#include <sstream>
+#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <array>
@@ -34,64 +35,47 @@ void quit_handler(int) {
}
int main(int argc, char *argv[]) {
+ args::ArgumentParser argumentParser("Mapbox GL GLFW example");
+ args::HelpFlag helpFlag(argumentParser, "help", "Display this help menu", {'h', "help"});
+
+ args::Flag fullscreenFlag(argumentParser, "fullscreen", "Toggle fullscreen", {'f', "fullscreen"});
+ args::Flag benchmarkFlag(argumentParser, "benchmark", "Toggle benchmark", {'b', "benchmark"});
+ args::Flag offlineFlag(argumentParser, "offline", "Toggle offline", {'o', "offline"});
+
+ args::ValueFlag<std::string> styleValue(argumentParser, "URL", "Map stylesheet", {'s', "style"});
+ args::ValueFlag<double> lonValue(argumentParser, "degrees", "Longitude", {'x', "lon"});
+ args::ValueFlag<double> latValue(argumentParser, "degrees", "Latitude", {'y', "lat"});
+ args::ValueFlag<double> zoomValue(argumentParser, "number", "Zoom level", {'z', "zoom"});
+ args::ValueFlag<double> bearingValue(argumentParser, "degrees", "Bearing", {'b', "bearing"});
+ args::ValueFlag<double> pitchValue(argumentParser, "degrees", "Pitch", {'p', "pitch"});
+
+ try {
+ argumentParser.ParseCLI(argc, argv);
+ } catch (args::Help) {
+ std::cout << argumentParser;
+ exit(0);
+ } catch (args::ParseError e) {
+ std::cerr << e.what() << std::endl;
+ std::cerr << argumentParser;
+ exit(1);
+ } catch (args::ValidationError e) {
+ std::cerr << e.what() << std::endl;
+ std::cerr << argumentParser;
+ exit(2);
+ }
+
// Load settings
mbgl::Settings_JSON settings;
-
- bool fullscreen = false;
- bool benchmark = false;
- std::string style;
-
- const struct option long_options[] = {
- {"fullscreen", no_argument, nullptr, 'f'},
- {"benchmark", no_argument, nullptr, 'b'},
- {"offline", no_argument, nullptr, 'o'},
- {"style", required_argument, nullptr, 's'},
- {"lon", required_argument, nullptr, 'x'},
- {"lat", required_argument, nullptr, 'y'},
- {"zoom", required_argument, nullptr, 'z'},
- {"bearing", required_argument, nullptr, 'r'},
- {"pitch", required_argument, nullptr, 'p'},
- {nullptr, 0, nullptr, 0}
- };
-
- while (true) {
- int option_index = 0;
- int opt = getopt_long(argc, argv, "fbs:", long_options, &option_index);
- if (opt == -1) break;
- switch (opt)
- {
- case 'f':
- fullscreen = true;
- break;
- case 'b':
- benchmark = true;
- break;
- case 'o':
- settings.online = false;
- break;
- case 's':
- style = std::string(optarg);
- break;
- case 'x':
- settings.longitude = atof(optarg);
- break;
- case 'y':
- settings.latitude = atof(optarg);
- break;
- case 'z':
- settings.zoom = atof(optarg);
- break;
- case 'r':
- settings.bearing = atof(optarg);
- break;
- case 'p':
- settings.pitch = atof(optarg);
- break;
- default:
- break;
- }
-
- }
+ settings.online = !offlineFlag;
+ if (lonValue) settings.longitude = args::get(lonValue);
+ if (latValue) settings.latitude = args::get(latValue);
+ if (zoomValue) settings.zoom = args::get(zoomValue);
+ if (bearingValue) settings.bearing = args::get(bearingValue);
+ if (pitchValue) settings.pitch = args::get(pitchValue);
+
+ const bool fullscreen = fullscreenFlag ? args::get(fullscreenFlag) : false;
+ const bool benchmark = benchmarkFlag ? args::get(benchmarkFlag) : false;
+ std::string style = styleValue ? args::get(styleValue) : "";
// sigint handling
struct sigaction sigIntHandler;
diff --git a/platform/glfw/settings_json.hpp b/platform/glfw/settings_json.hpp
index 49ea00e3e1..c89accb8af 100644
--- a/platform/glfw/settings_json.hpp
+++ b/platform/glfw/settings_json.hpp
@@ -17,6 +17,9 @@ public:
double zoom = 0;
double bearing = 0;
double pitch = 0;
+ bool axonometric = false;
+ double xSkew = 0.0;
+ double ySkew = 1.0;
EnumType debug = 0;
bool online = true;
diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md
index 7d23a3fbaf..471dd949c1 100644
--- a/platform/ios/CHANGELOG.md
+++ b/platform/ios/CHANGELOG.md
@@ -2,6 +2,37 @@
Mapbox welcomes participation and contributions from everyone. Please read [CONTRIBUTING.md](../../CONTRIBUTING.md) to get started.
+## master
+
+### Packaging
+
+* Removed support for 32-bit simulators. ([#10962](https://github.com/mapbox/mapbox-gl-native/pull/10962))
+
+### Styles and rendering
+
+* Added support for a new layer type: `MGLHeatmapStyleLayer`, a powerful way to visualize point data distributions using heatmaps, fully customizable through runtime styling. [#11046](https://github.com/mapbox/mapbox-gl-native/pull/11046)
+* The layout and paint properties on subclasses of `MGLStyleLayer` are now of type `NSExpression` instead of `MGLStyleValue`. A new “Predicates and Expressions” guide provides an overview of the supported operators. ([#10726](https://github.com/mapbox/mapbox-gl-native/pull/10726))
+* Added an `MGLComputedShapeSource` class that allows applications to supply vector data to a style layer on a per-tile basis. ([#9983](https://github.com/mapbox/mapbox-gl-native/pull/9983))
+* A style can now display smooth hillshading and customize its appearance at runtime using the `MGLHillshadeStyleLayer` class. Hillshading is based on a rasterized digital elevation model supplied by the `MGLRasterDEMSource` class. ([#10642](https://github.com/mapbox/mapbox-gl-native/pull/10642))
+* Properties such as `MGLSymbolStyleLayer.iconAllowsOverlap` and `MGLSymbolStyleLayer.iconIgnoresPlacement` now account for symbols in other sources. ([#10436](https://github.com/mapbox/mapbox-gl-native/pull/10436))
+* Improved the reliability of collision detection between symbols near the edges of tiles, as well as between symbols when the map is tilted. It is no longer necessary to enable `MGLSymbolStyleLayer.symbolAvoidsEdges` to prevent symbols in adjacent tiles from overlapping with each other. ([#10436](https://github.com/mapbox/mapbox-gl-native/pull/10436))
+* Symbols can fade in and out as the map pans, rotates, or tilts. ([#10436](https://github.com/mapbox/mapbox-gl-native/pull/10436))
+* Fixed an issue preventing a dynamically-added `MGLRasterStyleLayer` from drawing until the map pans. ([#10270](https://github.com/mapbox/mapbox-gl-native/pull/10270))
+* Fixed an issue preventing `MGLImageSource`s from drawing on the map when the map is zoomed in and tilted. ([#10677](https://github.com/mapbox/mapbox-gl-native/pull/10677))
+
+### Snapshots
+
+* Fixed a memory leak that occurred when creating a map snapshot. ([#10585](https://github.com/mapbox/mapbox-gl-native/pull/10585))
+
+### Annotations
+
+* Improved performance of `MGLAnnotationView`-backed annotations that have `scalesWithViewingDistance` enabled. ([#10951](https://github.com/mapbox/mapbox-gl-native/pull/10951))
+
+### Other changes
+
+* Added a Hebrew localization. ([#10967](https://github.com/mapbox/mapbox-gl-native/pull/10967))
+* Long-pressing the attribution button causes the SDK’s version number to be displayed in the action sheet that appears. ([#10650](https://github.com/mapbox/mapbox-gl-native/pull/10650))
+
## 3.7.5 - February 16, 2018
* Fixed an issue where requesting location services permission would trigger an unrecoverable loop. ([#11229](https://github.com/mapbox/mapbox-gl-native/pull/11229))
diff --git a/platform/ios/DEVELOPING.md b/platform/ios/DEVELOPING.md
index 40d45e2e56..10a94c73f3 100644
--- a/platform/ios/DEVELOPING.md
+++ b/platform/ios/DEVELOPING.md
@@ -6,7 +6,7 @@ This document explains how to build the Mapbox Maps SDK for iOS from source. It
The Mapbox Maps SDK for iOS and iosapp demo application require iOS 8.0 or above.
-The Mapbox Maps SDK for iOS requires Xcode 8.0 or above.
+The Mapbox Maps SDK for iOS requires Xcode 9.1 or above to compile from source.
## Building the SDK
@@ -30,7 +30,7 @@ Before building, use the scheme picker button in the toolbar to change the schem
* **static** builds the SDK as a static library and separate resource bundle.
* **dynamic+static** is a combination of the **dynamic** and **static** schemes.
-If you don’t have an Apple Developer account, change the destination to a simulator such as “iPhone 6s” before you run and build the app.
+If you don’t have an Apple Developer account, change the destination to a simulator such as “iPhone 6s” before you build and run the app.
### Packaging builds
@@ -52,12 +52,12 @@ You can customize the build output by passing the following arguments into the `
* `BUILDTYPE=Release` will optimize for distribution. Defaults to `Debug`.
* `BUILD_DEVICE=false` builds only for the iOS Simulator.
* `FORMAT=dynamic` builds only a dynamic framework. `FORMAT=static` builds only a static framework, for legacy compatibility.
-* `SYMBOLS=NO` strips the build output of any debug symbols, yielding much smaller binaries. Defaults to `YES`.
+* `SYMBOLS=NO` strips the build output of any debug symbols, yielding smaller binaries. Defaults to `YES`.
An example command that creates a dynamic framework suitable for eventual App Store distribution:
```bash
-make iframework BUILDTYPE=Release SYMBOLS=NO
+make iframework BUILDTYPE=Release
```
The products of these build commands can be found in the `build/ios/pkg` folder at the base of the repository.
@@ -168,5 +168,5 @@ The included applications use Mapbox vector tiles, which require a Mapbox accoun
- Use two fingers to rotate
- Double-tap to zoom in one level
- Two-finger single-tap to zoom out one level
-- Double-tap, long-pressing the second, then pan up and down to "quick zoom" (iPhone only, meant for one-handed use)
+- Double-tap, long-pressing the second, then pan up and down to "quick zoom" (meant for one-handed use)
- Use the debug menu to add test annotations, reset position, and cycle through the debug options.
diff --git a/platform/ios/INSTALL.md b/platform/ios/INSTALL.md
index b0d7be83d5..3aef7db6f4 100644
--- a/platform/ios/INSTALL.md
+++ b/platform/ios/INSTALL.md
@@ -10,9 +10,12 @@ The Mapbox Maps SDK for iOS is intended to run on iOS 8.0 and above on the follo
* iPad 2 and above (3, 4, Mini, Air, Mini 2, Air 2, Pro)
* iPod touch 5th generation and above
-Note that debugging in 32-bit simulators (such as the iPhone 5 or iPad 2) is only partially supported. Support for these simulators will be removed entirely in a future release of this SDK.
+Note that 32-bit simulators (such as the iPhone 5 or iPad 2) are not supported.
-The Mapbox Maps SDK for iOS requires Xcode 8.0 or higher. To use this SDK with Xcode 7.3.1, download and use a symbols build from the [releases](https://github.com/mapbox/mapbox-gl-native/releases) page.
+The Mapbox Maps SDK for iOS requires:
+
+* Xcode 9.1 or higher to compile from source
+* Xcode 8.0 or higher to integrate the compiled framework into an application
### Building the SDK
@@ -24,9 +27,8 @@ The Mapbox Maps SDK for iOS requires Xcode 8.0 or higher. To use this SDK with X
[sudo] gem install jazzy
```
-1. Run `make ipackage`. The packaging script will produce a `build/ios/pkg/` folder containing:
+1. Run `make iframework BUILDTYPE=Release`. The packaging script will produce a `build/ios/pkg/` folder containing:
- a `dynamic` folder containing a dynamically-linked fat framework with debug symbols for devices and the iOS Simulator
- - a `static` folder containing a statically-linked framework with debug symbols for devices and the iOS Simulator
- a `documentation` folder with HTML API documentation
- an example `Settings.bundle` containing an optional Mapbox Telemetry opt-out setting
@@ -60,7 +62,7 @@ A nightly build of the dynamic framework, based on the master branch, is availab
You can alternatively install the SDK as a static framework:
-1. Build from source manually, per above.
+1. Build from source using the `make ipackage` command.
1. Drag the Mapbox.bundle and Mapbox.framework from the `build/ios/pkg/static/` directory into the Project navigator. In the sheet that appears, make sure “Copy items if needed” is checked, then click Finish. Open the project editor and select your application target to verify that the following changes occurred automatically:
diff --git a/platform/ios/Integration Test Harness/AppDelegate.h b/platform/ios/Integration Test Harness/AppDelegate.h
new file mode 100644
index 0000000000..2a9bac67c9
--- /dev/null
+++ b/platform/ios/Integration Test Harness/AppDelegate.h
@@ -0,0 +1,8 @@
+#import <UIKit/UIKit.h>
+
+@interface AppDelegate : UIResponder <UIApplicationDelegate>
+
+@property (strong, nonatomic) UIWindow *window;
+
+@end
+
diff --git a/platform/ios/Integration Test Harness/AppDelegate.m b/platform/ios/Integration Test Harness/AppDelegate.m
new file mode 100644
index 0000000000..4483c5f98a
--- /dev/null
+++ b/platform/ios/Integration Test Harness/AppDelegate.m
@@ -0,0 +1,14 @@
+#import "AppDelegate.h"
+
+@interface AppDelegate ()
+
+@end
+
+@implementation AppDelegate
+
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+
+ return YES;
+}
+
+@end
diff --git a/platform/ios/Integration Test Harness/Assets.xcassets/AppIcon.appiconset/Contents.json b/platform/ios/Integration Test Harness/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000000..1d060ed288
--- /dev/null
+++ b/platform/ios/Integration Test Harness/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,93 @@
+{
+ "images" : [
+ {
+ "idiom" : "iphone",
+ "size" : "20x20",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "20x20",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "29x29",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "29x29",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "40x40",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "40x40",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "60x60",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "60x60",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "20x20",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "20x20",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "29x29",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "29x29",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "40x40",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "40x40",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "76x76",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "76x76",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "83.5x83.5",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+} \ No newline at end of file
diff --git a/platform/ios/Integration Test Harness/Base.lproj/LaunchScreen.storyboard b/platform/ios/Integration Test Harness/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 0000000000..f83f6fd581
--- /dev/null
+++ b/platform/ios/Integration Test Harness/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" systemVersion="17A277" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
+ <dependencies>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
+ <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+ </dependencies>
+ <scenes>
+ <!--View Controller-->
+ <scene sceneID="EHf-IW-A2E">
+ <objects>
+ <viewController id="01J-lp-oVM" sceneMemberID="viewController">
+ <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
+ <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+ <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
+ </view>
+ </viewController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="53" y="375"/>
+ </scene>
+ </scenes>
+</document>
diff --git a/platform/ios/Integration Test Harness/Info.plist b/platform/ios/Integration Test Harness/Info.plist
new file mode 100644
index 0000000000..4222ac2dd3
--- /dev/null
+++ b/platform/ios/Integration Test Harness/Info.plist
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>$(DEVELOPMENT_LANGUAGE)</string>
+ <key>CFBundleExecutable</key>
+ <string>$(EXECUTABLE_NAME)</string>
+ <key>CFBundleIdentifier</key>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>$(PRODUCT_NAME)</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+ <key>LSRequiresIPhoneOS</key>
+ <true/>
+ <key>UILaunchStoryboardName</key>
+ <string>LaunchScreen</string>
+ <key>UIRequiredDeviceCapabilities</key>
+ <array>
+ <string>armv7</string>
+ </array>
+ <key>UISupportedInterfaceOrientations</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+ <key>UISupportedInterfaceOrientations~ipad</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationPortraitUpsideDown</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+</dict>
+</plist>
diff --git a/platform/ios/Integration Test Harness/main.m b/platform/ios/Integration Test Harness/main.m
new file mode 100644
index 0000000000..81e84cbb78
--- /dev/null
+++ b/platform/ios/Integration Test Harness/main.m
@@ -0,0 +1,8 @@
+#import <UIKit/UIKit.h>
+#import "AppDelegate.h"
+
+int main(int argc, char * argv[]) {
+ @autoreleasepool {
+ return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
+ }
+}
diff --git a/platform/ios/Integration Tests/Info.plist b/platform/ios/Integration Tests/Info.plist
new file mode 100644
index 0000000000..6c40a6cd0c
--- /dev/null
+++ b/platform/ios/Integration Tests/Info.plist
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>$(DEVELOPMENT_LANGUAGE)</string>
+ <key>CFBundleExecutable</key>
+ <string>$(EXECUTABLE_NAME)</string>
+ <key>CFBundleIdentifier</key>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>$(PRODUCT_NAME)</string>
+ <key>CFBundlePackageType</key>
+ <string>BNDL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+</dict>
+</plist>
diff --git a/platform/ios/Integration Tests/MBGLIntegrationTests.m b/platform/ios/Integration Tests/MBGLIntegrationTests.m
new file mode 100644
index 0000000000..db6cc13930
--- /dev/null
+++ b/platform/ios/Integration Tests/MBGLIntegrationTests.m
@@ -0,0 +1,77 @@
+#import <XCTest/XCTest.h>
+
+@import Mapbox;
+
+@interface MBGLIntegrationTests : XCTestCase <MGLMapViewDelegate>
+
+@property (nonatomic) MGLMapView *mapView;
+@property (nonatomic) MGLStyle *style;
+
+@end
+
+@implementation MBGLIntegrationTests {
+ XCTestExpectation *_styleLoadingExpectation;
+}
+
+- (void)setUp {
+ [super setUp];
+
+ [MGLAccountManager setAccessToken:@"pk.feedcafedeadbeefbadebede"];
+ NSURL *styleURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"one-liner" withExtension:@"json"];
+ self.mapView = [[MGLMapView alloc] initWithFrame:UIScreen.mainScreen.bounds styleURL:styleURL];
+ self.mapView.delegate = self;
+ if (!self.mapView.style) {
+ _styleLoadingExpectation = [self expectationWithDescription:@"Map view should finish loading style."];
+ [self waitForExpectationsWithTimeout:1 handler:nil];
+ }
+
+ UIView *superView = [[UIView alloc] initWithFrame:UIScreen.mainScreen.bounds];
+ [superView addSubview:self.mapView];
+ UIWindow *window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
+ [window addSubview:superView];
+ [window makeKeyAndVisible];
+}
+
+- (void)mapView:(MGLMapView *)mapView didFinishLoadingStyle:(MGLStyle *)style {
+ XCTAssertNotNil(mapView.style);
+ XCTAssertEqual(mapView.style, style);
+
+ [_styleLoadingExpectation fulfill];
+}
+
+- (void)tearDown {
+ _styleLoadingExpectation = nil;
+ self.mapView = nil;
+
+ [super tearDown];
+}
+
+- (MGLStyle *)style {
+ return self.mapView.style;
+}
+
+- (void)testAddingRemovingOpenGLLayer {
+ XCTAssertNotNil(self.style);
+
+ void(^addRemoveGLLayer)(void) = ^{
+ MGLOpenGLStyleLayer *layer = [[MGLOpenGLStyleLayer alloc] initWithIdentifier:@"gl-layer"];
+ [self.style insertLayer:layer atIndex:0];
+ layer = nil;
+
+ [[NSRunLoop currentRunLoop] runUntilDate:[NSDate date]];
+
+ id retrievedLayer = [self.style layerWithIdentifier:@"gl-layer"];
+ XCTAssertNotNil(retrievedLayer);
+ [self.style removeLayer:retrievedLayer];
+ };
+
+ addRemoveGLLayer();
+ addRemoveGLLayer();
+ addRemoveGLLayer();
+}
+
+//- (void)testOpenGLLayerDoesNotLeakWhenCreatedAndDestroyedWithoutAddingToStyle {
+// XCTFail(@"Not yet implemented");
+//}
+
+@end
diff --git a/platform/ios/README.md b/platform/ios/README.md
index 40e27c2d3f..3ca5b84dde 100644
--- a/platform/ios/README.md
+++ b/platform/ios/README.md
@@ -7,6 +7,7 @@ A library based on [Mapbox GL Native](../../README.md) for embedding interactive
This repository is for day-to-day development of the SDK. Building the SDK yourself requires [a number of dependencies and steps](../../INSTALL.md) that are unnecessary for developing production applications. For production applications, please consider installing an official, prebuilt release instead; see the [Mapbox iOS SDK website](https://www.mapbox.com/ios-sdk/) for installation instructions.
* [Integrate the Mapbox Maps SDK for iOS into your application](https://www.mapbox.com/install/ios/).
+* [Learn about custom builds](INSTALL.md)
* [Contribute to the Mapbox Maps SDK for iOS](DEVELOPING.md)
![](docs/img/screenshot.png)
diff --git a/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Contents.json b/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Contents.json
index f4e2027f0f..ccfb66f047 100644
--- a/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Contents.json
+++ b/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -99,8 +99,9 @@
"scale" : "2x"
},
{
- "idiom" : "ios-marketing",
"size" : "1024x1024",
+ "idiom" : "ios-marketing",
+ "filename" : "Icon-1024.png",
"scale" : "1x"
}
],
diff --git a/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-1024.png b/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-1024.png
new file mode 100644
index 0000000000..d1cb5c50f7
--- /dev/null
+++ b/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-1024.png
Binary files differ
diff --git a/platform/ios/app/Info.plist b/platform/ios/app/Info.plist
index 0bf66cb8da..ea70ecd629 100644
--- a/platform/ios/app/Info.plist
+++ b/platform/ios/app/Info.plist
@@ -27,7 +27,7 @@
<key>MGLIdeographicFontFamilyName</key>
<string>PingFang TC</string>
<key>NSHumanReadableCopyright</key>
- <string>© 2014–2017 Mapbox</string>
+ <string>© 2014–2018 Mapbox</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>The map will display your location. The map may also use your location when it isn’t visible in order to improve OpenStreetMap and Mapbox products.</string>
<key>NSLocationWhenInUseUsageDescription</key>
diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m
index 1046644f8c..282dc901d6 100644
--- a/platform/ios/app/MBXViewController.m
+++ b/platform/ios/app/MBXViewController.m
@@ -75,6 +75,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsRuntimeStylingRows) {
MBXSettingsRuntimeStylingImageSource,
MBXSettingsRuntimeStylingRouteLine,
MBXSettingsRuntimeStylingDDSPolygon,
+ MBXSettingsRuntimeStylingCustomLatLonGrid,
};
typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
@@ -111,7 +112,8 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
@interface MBXViewController () <UITableViewDelegate,
UITableViewDataSource,
- MGLMapViewDelegate>
+ MGLMapViewDelegate,
+ MGLComputedShapeSourceDataSource>
@property (nonatomic) IBOutlet MGLMapView *mapView;
@@ -353,6 +355,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
@"Style Image Source",
@"Add Route Line",
@"Dynamically Style Polygon",
+ @"Add Custom Lat/Lon Grid",
]];
break;
case MBXSettingsMiscellaneous:
@@ -529,6 +532,9 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
case MBXSettingsRuntimeStylingDDSPolygon:
[self stylePolygonWithDDS];
break;
+ case MBXSettingsRuntimeStylingCustomLatLonGrid:
+ [self addLatLonGrid];
+ break;
default:
NSAssert(NO, @"All runtime styling setting rows should be implemented");
break;
@@ -891,8 +897,8 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
MGLFillExtrusionStyleLayer* layer = [[MGLFillExtrusionStyleLayer alloc] initWithIdentifier:@"extrudedBuildings" source:source];
layer.sourceLayerIdentifier = @"building";
layer.predicate = [NSPredicate predicateWithFormat:@"extrude == 'true' AND height > 0"];
- layer.fillExtrusionBase = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"min_height" options:nil];
- layer.fillExtrusionHeight = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"height" options:nil];
+ layer.fillExtrusionBase = [NSExpression expressionForKeyPath:@"min_height"];
+ layer.fillExtrusionHeight = [NSExpression expressionForKeyPath:@"height"];
// Set the fill color to that of the existing building footprint layer, if it exists.
MGLFillStyleLayer* buildingLayer = (MGLFillStyleLayer*)[self.mapView.style layerWithIdentifier:@"building"];
@@ -900,10 +906,10 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
if (buildingLayer.fillColor) {
layer.fillExtrusionColor = buildingLayer.fillColor;
} else {
- layer.fillExtrusionColor = [MGLStyleValue valueWithRawValue:[UIColor whiteColor]];
+ layer.fillExtrusionColor = [NSExpression expressionForConstantValue:[UIColor whiteColor]];
}
- layer.fillExtrusionOpacity = [MGLStyleValue<NSNumber *> valueWithRawValue:@0.75];
+ layer.fillExtrusionOpacity = [NSExpression expressionForConstantValue:@0.75];
}
MGLStyleLayer* labelLayer = [self.mapView.style layerWithIdentifier:@"waterway-label"];
@@ -918,48 +924,45 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
- (void)styleWaterLayer
{
MGLFillStyleLayer *waterLayer = (MGLFillStyleLayer *)[self.mapView.style layerWithIdentifier:@"water"];
- NSDictionary *waterColorStops = @{@6.0f: [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor yellowColor]],
- @8.0f: [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor blueColor]],
- @10.0f: [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor redColor]],
- @12.0f: [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor greenColor]],
- @14.0f: [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor blueColor]]};
- MGLStyleValue *waterColorFunction = [MGLStyleValue<UIColor *> valueWithInterpolationMode:MGLInterpolationModeExponential
- cameraStops:waterColorStops
- options: nil];
- waterLayer.fillColor = waterColorFunction;
-
- NSDictionary *fillAntialiasedStops = @{@11: [MGLStyleValue<NSNumber *> valueWithRawValue:@YES],
- @12: [MGLStyleValue<NSNumber *> valueWithRawValue:@NO],
- @13: [MGLStyleValue<NSNumber *> valueWithRawValue:@YES],
- @14: [MGLStyleValue<NSNumber *> valueWithRawValue:@NO],
- @15: [MGLStyleValue<NSNumber *> valueWithRawValue:@YES]};
- MGLStyleValue *fillAntialiasedFunction = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval
- cameraStops:fillAntialiasedStops
- options:nil];
- waterLayer.fillAntialiased = fillAntialiasedFunction;
+ NSDictionary *waterColorStops = @{@6.0f: [UIColor yellowColor],
+ @8.0f: [UIColor blueColor],
+ @10.0f: [UIColor redColor],
+ @12.0f: [UIColor greenColor],
+ @14.0f: [UIColor blueColor]};
+ waterLayer.fillColor = [NSExpression expressionWithFormat:
+ @"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)",
+ waterColorStops];
+
+ NSDictionary *fillAntialiasedStops = @{@11: @YES,
+ @12: @NO,
+ @13: @YES,
+ @14: @NO,
+ @15: @YES};
+ waterLayer.fillAntialiased = [NSExpression expressionWithFormat:
+ @"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', false, %@)",
+ fillAntialiasedStops];
}
- (void)styleRoadLayer
{
MGLLineStyleLayer *roadLayer = (MGLLineStyleLayer *)[self.mapView.style layerWithIdentifier:@"road-primary"];
- roadLayer.lineColor = [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor blackColor]];
-
- NSDictionary *lineWidthStops = @{@5: [MGLStyleValue<NSNumber *> valueWithRawValue:@5],
- @10: [MGLStyleValue<NSNumber *> valueWithRawValue:@15],
- @15: [MGLStyleValue<NSNumber *> valueWithRawValue:@30]};
- MGLStyleValue *lineWidthFunction = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential
- cameraStops:lineWidthStops
- options:nil];
- roadLayer.lineWidth = lineWidthFunction;
- roadLayer.lineGapWidth = lineWidthFunction;
-
- NSDictionary *roadLineColorStops = @{@10: [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor purpleColor]],
- @13: [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor yellowColor]],
- @16: [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor cyanColor]]};
- MGLStyleValue *roadLineColor = [MGLStyleValue<UIColor *> valueWithInterpolationMode:MGLInterpolationModeExponential
- cameraStops:roadLineColorStops
- options: nil];
- roadLayer.lineColor = roadLineColor;
+ roadLayer.lineColor = [NSExpression expressionForConstantValue:[UIColor blackColor]];
+
+ NSDictionary *lineWidthStops = @{@5: @5,
+ @10: @15,
+ @15: @30};
+ NSExpression *lineWidthExpression = [NSExpression expressionWithFormat:
+ @"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)",
+ lineWidthStops];
+ roadLayer.lineWidth = lineWidthExpression;
+ roadLayer.lineGapWidth = lineWidthExpression;
+
+ NSDictionary *roadLineColorStops = @{@10: [UIColor purpleColor],
+ @13: [UIColor yellowColor],
+ @16: [UIColor cyanColor]};
+ roadLayer.lineColor = [NSExpression expressionWithFormat:
+ @"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)",
+ roadLineColorStops];
roadLayer.visible = YES;
roadLayer.maximumZoomLevel = 15;
@@ -973,11 +976,11 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
[self.mapView.style addSource:rasterSource];
MGLRasterStyleLayer *rasterLayer = [[MGLRasterStyleLayer alloc] initWithIdentifier:@"my-raster-layer" source:rasterSource];
- MGLStyleValue *opacityFunction = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential
- cameraStops:@{@20.0f: [MGLStyleValue<NSNumber *> valueWithRawValue:@1.0f],
- @5.0f: [MGLStyleValue<NSNumber *> valueWithRawValue:@0.0f]}
- options:nil];
- rasterLayer.rasterOpacity = opacityFunction;
+ NSDictionary *opacityStops = @{@20.0f: @1.0f,
+ @5.0f: @0.0f};
+ rasterLayer.rasterOpacity = [NSExpression expressionWithFormat:
+ @"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)",
+ opacityStops];
[self.mapView.style addLayer:rasterLayer];
}
@@ -989,7 +992,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
[self.mapView.style addSource:source];
MGLFillStyleLayer *fillLayer = [[MGLFillStyleLayer alloc] initWithIdentifier:@"test" source:source];
- fillLayer.fillColor = [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor purpleColor]];
+ fillLayer.fillColor = [NSExpression expressionForConstantValue:[UIColor purpleColor]];
[self.mapView.style addLayer:fillLayer];
}
@@ -997,7 +1000,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
- (void)styleSymbolLayer
{
MGLSymbolStyleLayer *stateLayer = (MGLSymbolStyleLayer *)[self.mapView.style layerWithIdentifier:@"state-label-lg"];
- stateLayer.textColor = [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor redColor]];
+ stateLayer.textColor = [NSExpression expressionForConstantValue:[UIColor redColor]];
}
- (void)styleBuildingLayer
@@ -1005,13 +1008,13 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
MGLTransition transition = { 5, 1 };
self.mapView.style.transition = transition;
MGLFillStyleLayer *buildingLayer = (MGLFillStyleLayer *)[self.mapView.style layerWithIdentifier:@"building"];
- buildingLayer.fillColor = [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor purpleColor]];
+ buildingLayer.fillColor = [NSExpression expressionForConstantValue:[UIColor purpleColor]];
}
- (void)styleFerryLayer
{
MGLLineStyleLayer *ferryLineLayer = (MGLLineStyleLayer *)[self.mapView.style layerWithIdentifier:@"ferry"];
- ferryLineLayer.lineColor = [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor redColor]];
+ ferryLineLayer.lineColor = [NSExpression expressionForConstantValue:[UIColor redColor]];
}
- (void)removeParkLayer
@@ -1035,8 +1038,8 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
statesLayer.predicate = [NSPredicate predicateWithFormat:@"name == 'Texas'"];
// paint properties
- statesLayer.fillColor = [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor redColor]];
- statesLayer.fillOpacity = [MGLStyleValue<NSNumber *> valueWithRawValue:@0.25];
+ statesLayer.fillColor = [NSExpression expressionForConstantValue:[UIColor redColor]];
+ statesLayer.fillOpacity = [NSExpression expressionForConstantValue:@0.25];
});
}
@@ -1055,9 +1058,9 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
countiesLayer.predicate = [NSPredicate predicateWithFormat:@"NAME10 == 'Washington'"];
// paint properties
- countiesLayer.lineColor = [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor redColor]];
- countiesLayer.lineOpacity = [MGLStyleValue<NSNumber *> valueWithRawValue:@0.75];
- countiesLayer.lineWidth = [MGLStyleValue<NSNumber *> valueWithRawValue:@5];
+ countiesLayer.lineColor = [NSExpression expressionForConstantValue:[UIColor redColor]];
+ countiesLayer.lineOpacity = [NSExpression expressionForConstantValue:@0.75];
+ countiesLayer.lineWidth = [NSExpression expressionForConstantValue:@5];
});
}
@@ -1076,8 +1079,8 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
regionsLayer.predicate = [NSPredicate predicateWithFormat:@"HRRNUM >= %@ AND HRRNUM < 300", @(200)];
// paint properties
- regionsLayer.fillColor = [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor blueColor]];
- regionsLayer.fillOpacity = [MGLStyleValue<NSNumber *> valueWithRawValue:@0.5];
+ regionsLayer.fillColor = [NSExpression expressionForConstantValue:[UIColor blueColor]];
+ regionsLayer.fillOpacity = [NSExpression expressionForConstantValue:@0.5];
});
}
@@ -1107,8 +1110,8 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
[self.mapView.style addSource:source];
MGLFillStyleLayer *fillLayer = [[MGLFillStyleLayer alloc] initWithIdentifier:queryLayerID source:source];
- fillLayer.fillColor = [MGLConstantStyleValue<UIColor *> valueWithRawValue:[UIColor blueColor]];
- fillLayer.fillOpacity = [MGLConstantStyleValue<NSNumber *> valueWithRawValue:@0.5];
+ fillLayer.fillColor = [NSExpression expressionForConstantValue:[UIColor blueColor]];
+ fillLayer.fillOpacity = [NSExpression expressionForConstantValue:@0.5];
[self.mapView.style addLayer:fillLayer];
});
}
@@ -1161,8 +1164,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
MGLFillStyleLayer *layer = [[MGLFillStyleLayer alloc] initWithIdentifier:@"leaf-fill-layer" source:source];
layer.predicate = [NSPredicate predicateWithFormat:@"color = 'red'"];
- MGLStyleValue *fillColor = [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor redColor]];
- layer.fillColor = fillColor;
+ layer.fillColor = [NSExpression expressionForConstantValue:[UIColor redColor]];
[self.mapView.style addLayer:layer];
NSString *geoJSON = @"{\"type\": \"Feature\", \"properties\": {\"color\": \"green\"}, \"geometry\": { \"type\": \"Point\", \"coordinates\": [ -114.06847000122069, 51.050459433092655 ] }}";
@@ -1173,7 +1175,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
[self.mapView.style addSource:pointSource];
MGLCircleStyleLayer *circleLayer = [[MGLCircleStyleLayer alloc] initWithIdentifier:@"leaf-circle-layer" source:pointSource];
- circleLayer.circleColor = [MGLStyleValue valueWithRawValue:[UIColor greenColor]];
+ circleLayer.circleColor = [NSExpression expressionForConstantValue:[UIColor greenColor]];
circleLayer.predicate = [NSPredicate predicateWithFormat:@"color = 'green'"];
[self.mapView.style addLayer:circleLayer];
@@ -1190,7 +1192,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
[self.mapView.style addSource:plainShapeSource];
MGLFillStyleLayer *plainFillLayer = [[MGLFillStyleLayer alloc] initWithIdentifier:@"leaf-plain-fill-layer" source:plainShapeSource];
- plainFillLayer.fillColor = [MGLStyleValue valueWithRawValue:[UIColor yellowColor]];
+ plainFillLayer.fillColor = [NSExpression expressionForConstantValue:[UIColor yellowColor]];
[self.mapView.style addLayer:plainFillLayer];
}
@@ -1267,8 +1269,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
[self.mapView.style addSource:source];
MGLFillStyleLayer *layer = [[MGLFillStyleLayer alloc] initWithIdentifier:@"mutable-data-layer-features-id" source:source];
- MGLStyleValue *fillColor = [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor redColor]];
- layer.fillColor = fillColor;
+ layer.fillColor = [NSExpression expressionForConstantValue:[UIColor redColor]];
[self.mapView.style addLayer:layer];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
@@ -1301,16 +1302,14 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
[self.mapView.style addSource:vectorSource];
MGLBackgroundStyleLayer *backgroundLayer = [[MGLBackgroundStyleLayer alloc] initWithIdentifier:@"style-vector-background-layer-id"];
- backgroundLayer.backgroundColor = [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor blackColor]];
+ backgroundLayer.backgroundColor = [NSExpression expressionForConstantValue:[UIColor blackColor]];
[self.mapView.style addLayer:backgroundLayer];
MGLLineStyleLayer *lineLayer = [[MGLLineStyleLayer alloc] initWithIdentifier:@"style-vector-line-layer-id" source:vectorSource];
lineLayer.sourceLayerIdentifier = @"contour";
- NSUInteger lineJoinValue = MGLLineJoinRound;
- lineLayer.lineJoin = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue value:&lineJoinValue withObjCType:@encode(MGLLineJoin)]];
- NSUInteger lineCapValue = MGLLineCapRound;
- lineLayer.lineCap = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue value:&lineCapValue withObjCType:@encode(MGLLineCap)]];
- lineLayer.lineColor = [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor greenColor]];
+ lineLayer.lineJoin = [NSExpression expressionForConstantValue:@"round"];
+ lineLayer.lineCap = [NSExpression expressionForConstantValue:@"round"];
+ lineLayer.lineColor = [NSExpression expressionForConstantValue:[UIColor greenColor]];
[self.mapView.style addLayer:lineLayer];
}
@@ -1388,19 +1387,19 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
[self.mapView.style addSource:routeSource];
MGLLineStyleLayer *baseRouteLayer = [[MGLLineStyleLayer alloc] initWithIdentifier:@"style-base-route-layer" source:routeSource];
- baseRouteLayer.lineColor = [MGLConstantStyleValue valueWithRawValue:[UIColor orangeColor]];
- baseRouteLayer.lineWidth = [MGLConstantStyleValue valueWithRawValue:@20];
- baseRouteLayer.lineOpacity = [MGLConstantStyleValue valueWithRawValue:@0.5];
- baseRouteLayer.lineCap = [MGLConstantStyleValue valueWithRawValue:[NSValue valueWithMGLLineCap:MGLLineCapRound]];
- baseRouteLayer.lineJoin = [MGLConstantStyleValue valueWithRawValue:[NSValue valueWithMGLLineJoin:MGLLineJoinRound]];
+ baseRouteLayer.lineColor = [NSExpression expressionForConstantValue:[UIColor orangeColor]];
+ baseRouteLayer.lineWidth = [NSExpression expressionForConstantValue:@20];
+ baseRouteLayer.lineOpacity = [NSExpression expressionForConstantValue:@0.5];
+ baseRouteLayer.lineCap = [NSExpression expressionForConstantValue:@"round"];
+ baseRouteLayer.lineJoin = [NSExpression expressionForConstantValue:@"round"];
[self.mapView.style addLayer:baseRouteLayer];
MGLLineStyleLayer *routeLayer = [[MGLLineStyleLayer alloc] initWithIdentifier:@"style-route-layer" source:routeSource];
- routeLayer.lineColor = [MGLConstantStyleValue valueWithRawValue:[UIColor whiteColor]];
- routeLayer.lineWidth = [MGLConstantStyleValue valueWithRawValue:@15];
- routeLayer.lineOpacity = [MGLConstantStyleValue valueWithRawValue:@0.8];
- routeLayer.lineCap = [MGLConstantStyleValue valueWithRawValue:[NSValue valueWithMGLLineCap:MGLLineCapRound]];
- routeLayer.lineJoin = [MGLConstantStyleValue valueWithRawValue:[NSValue valueWithMGLLineJoin:MGLLineJoinRound]];
+ routeLayer.lineColor = [NSExpression expressionForConstantValue:[UIColor whiteColor]];
+ routeLayer.lineWidth = [NSExpression expressionForConstantValue:@15];
+ routeLayer.lineOpacity = [NSExpression expressionForConstantValue:@0.8];
+ routeLayer.lineCap = [NSExpression expressionForConstantValue:@"round"];
+ routeLayer.lineJoin = [NSExpression expressionForConstantValue:@"round"];
[self.mapView.style addLayer:routeLayer];
}
@@ -1430,22 +1429,28 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
// source, categorical function that sets any feature with a "fill" attribute value of true to red color and anything without to green
MGLFillStyleLayer *fillStyleLayer = [[MGLFillStyleLayer alloc] initWithIdentifier:@"fill-layer" source:shapeSource];
- NSDictionary *stops = @{@(YES): [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor greenColor]]};
- NSDictionary *fillColorOptions = @{MGLStyleFunctionOptionDefaultValue: [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor redColor]]};
- fillStyleLayer.fillColor = [MGLStyleValue<UIColor *> valueWithInterpolationMode:MGLInterpolationModeCategorical
- sourceStops:stops
- attributeName:@"fill"
- options:fillColorOptions];
+ fillStyleLayer.fillColor = [NSExpression expressionWithFormat:@"TERNARY(fill, %@, %@)", [UIColor greenColor], [UIColor redColor]];
// source, identity function that sets any feature with an "opacity" attribute to use that value and anything without to 1.0
- NSDictionary *fillOpacityOptions = @{MGLStyleFunctionOptionDefaultValue: [MGLStyleValue<NSNumber *> valueWithRawValue:@(1.0)]};
- fillStyleLayer.fillOpacity = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity
- sourceStops:nil
- attributeName:@"opacity"
- options:fillOpacityOptions];
+ fillStyleLayer.fillOpacity = [NSExpression expressionWithFormat:@"TERNARY(opacity != nil, opacity, 1.0)"];
[self.mapView.style addLayer:fillStyleLayer];
}
+- (void)addLatLonGrid
+{
+ MGLComputedShapeSource *source = [[MGLComputedShapeSource alloc] initWithIdentifier:@"latlon"
+ options:@{MGLShapeSourceOptionMaximumZoomLevel:@14}];
+ source.dataSource = self;
+ [self.mapView.style addSource:source];
+ MGLLineStyleLayer *lineLayer = [[MGLLineStyleLayer alloc] initWithIdentifier:@"latlonlines"
+ source:source];
+ [self.mapView.style addLayer:lineLayer];
+ MGLSymbolStyleLayer *labelLayer = [[MGLSymbolStyleLayer alloc] initWithIdentifier:@"latlonlabels"
+ source:source];
+ labelLayer.text = [NSExpression expressionForKeyPath:@"value"];
+ [self.mapView.style addLayer:labelLayer];
+}
+
- (NSString *)bestLanguageForUser
{
// https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview
@@ -1912,4 +1917,52 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
[self.hudLabel setTitle:hudString forState:UIControlStateNormal];
}
+#pragma mark - MGLComputedShapeSourceDataSource
+
+- (NSArray<id <MGLFeature>>*)featuresInCoordinateBounds:(MGLCoordinateBounds)bounds zoomLevel:(NSUInteger)zoom {
+ double gridSpacing;
+ if(zoom >= 13) {
+ gridSpacing = 0.01;
+ } else if(zoom >= 11) {
+ gridSpacing = 0.05;
+ } else if(zoom == 10) {
+ gridSpacing = .1;
+ } else if(zoom == 9) {
+ gridSpacing = 0.25;
+ } else if(zoom == 8) {
+ gridSpacing = 0.5;
+ } else if (zoom >= 6) {
+ gridSpacing = 1;
+ } else if(zoom == 5) {
+ gridSpacing = 2;
+ } else if(zoom >= 4) {
+ gridSpacing = 5;
+ } else if(zoom == 2) {
+ gridSpacing = 10;
+ } else {
+ gridSpacing = 20;
+ }
+
+ NSMutableArray <id <MGLFeature>> * features = [NSMutableArray array];
+ CLLocationCoordinate2D coords[2];
+
+ for (double y = ceil(bounds.ne.latitude / gridSpacing) * gridSpacing; y >= floor(bounds.sw.latitude / gridSpacing) * gridSpacing; y -= gridSpacing) {
+ coords[0] = CLLocationCoordinate2DMake(y, bounds.sw.longitude);
+ coords[1] = CLLocationCoordinate2DMake(y, bounds.ne.longitude);
+ MGLPolylineFeature *feature = [MGLPolylineFeature polylineWithCoordinates:coords count:2];
+ feature.attributes = @{@"value": @(y)};
+ [features addObject:feature];
+ }
+
+ for (double x = floor(bounds.sw.longitude / gridSpacing) * gridSpacing; x <= ceil(bounds.ne.longitude / gridSpacing) * gridSpacing; x += gridSpacing) {
+ coords[0] = CLLocationCoordinate2DMake(bounds.sw.latitude, x);
+ coords[1] = CLLocationCoordinate2DMake(bounds.ne.latitude, x);
+ MGLPolylineFeature *feature = [MGLPolylineFeature polylineWithCoordinates:coords count:2];
+ feature.attributes = @{@"value": @(x)};
+ [features addObject:feature];
+ }
+
+ return features;
+}
+
@end
diff --git a/platform/ios/app/he.lproj/Localizable.strings b/platform/ios/app/he.lproj/Localizable.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/platform/ios/app/he.lproj/Localizable.strings
diff --git a/platform/ios/benchmark/Info.plist b/platform/ios/benchmark/Info.plist
index 35d3658a1c..b35cb572ab 100644
--- a/platform/ios/benchmark/Info.plist
+++ b/platform/ios/benchmark/Info.plist
@@ -25,7 +25,7 @@
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSHumanReadableCopyright</key>
- <string>© 2015–2017 Mapbox</string>
+ <string>© 2015–2018 Mapbox</string>
<key>UIApplicationExitsOnSuspend</key>
<true/>
<key>UILaunchStoryboardName</key>
diff --git a/platform/ios/config.cmake b/platform/ios/config.cmake
index 55de8bc398..1caf372b25 100644
--- a/platform/ios/config.cmake
+++ b/platform/ios/config.cmake
@@ -48,8 +48,6 @@ macro(mbgl_platform_core)
PRIVATE platform/default/mbgl/gl/headless_backend.cpp
PRIVATE platform/default/mbgl/gl/headless_backend.hpp
PRIVATE platform/darwin/src/headless_backend_eagl.mm
- PRIVATE platform/default/mbgl/gl/headless_display.cpp
- PRIVATE platform/default/mbgl/gl/headless_display.hpp
# Snapshotting
PRIVATE platform/default/mbgl/map/map_snapshotter.cpp
@@ -66,17 +64,6 @@ macro(mbgl_platform_core)
target_add_mason_package(mbgl-core PUBLIC polylabel)
target_add_mason_package(mbgl-core PRIVATE icu)
- target_compile_options(mbgl-core
- PRIVATE -fvisibility=hidden
- )
-
- # TODO: Remove this by converting to ARC
- set_source_files_properties(
- platform/darwin/src/headless_backend_eagl.mm
- PROPERTIES
- COMPILE_FLAGS -fno-objc-arc
- )
-
target_include_directories(mbgl-core
PUBLIC platform/darwin
PUBLIC platform/default
@@ -106,10 +93,6 @@ macro(mbgl_filesource)
PRIVATE platform/default/sqlite3.cpp
)
- target_compile_options(mbgl-filesource
- PRIVATE -fvisibility=hidden
- )
-
target_link_libraries(mbgl-filesource
PUBLIC "-lsqlite3"
PUBLIC "-framework Foundation"
diff --git a/platform/ios/docs/guides/For Style Authors.md b/platform/ios/docs/guides/For Style Authors.md
index ebf4eb23d6..68d59fbc70 100644
--- a/platform/ios/docs/guides/For Style Authors.md
+++ b/platform/ios/docs/guides/For Style Authors.md
@@ -192,6 +192,8 @@ In style JSON | In the SDK
`circle` | `MGLCircleStyleLayer`
`fill` | `MGLFillStyleLayer`
`fill-extrusion` | `MGLFillExtrusionStyleLayer`
+`heatmap` | `MGLHeatmapStyleLayer`
+`hillshade` | `MGLHillshadeStyleLayer`
`line` | `MGLLineStyleLayer`
`raster` | `MGLRasterStyleLayer`
`symbol` | `MGLSymbolStyleLayer`
@@ -271,12 +273,16 @@ In style JSON | In Objective-C | In Swift
## Setting attribute values
Each property representing a layout or paint attribute is set to an
-`MGLStyleValue` object, which is either an `MGLConstantStyleValue` object (for
-constant values) or an `MGLStyleFunction` object (for style functions). The
-style value object is a container for the raw value or function parameters that
-you want the attribute to be set to.
+`NSExpression` object. `NSExpression` objects play the same role as
+[expressions in the Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions),
+but you create the former using a very different syntax. `NSExpression`’s format
+string syntax is reminiscent of a spreadsheet formula or an expression in a
+database query. See the
+“[Predicates and Expressions](Predicates and Expressions.md)” guide for an
+overview of the expression support in this SDK. This SDK no longer supports
+style functions; use expressions instead.
-### Constant style values
+### Constant values in expressions
In contrast to the JSON type that the style specification defines for each
layout or paint property, the style value object often contains a more specific
@@ -287,10 +293,10 @@ or set.
In style JSON | In Objective-C | In Swift
--------------|-----------------------|---------
Color | `UIColor` | `UIColor`
-Enum | `NSValue` (see `NSValue(MGLAdditions)`) | `NSValue` (see `NSValue(MGLAdditions)`)
+Enum | `NSString` | `String`
String | `NSString` | `String`
-Boolean | `NSNumber.boolValue` | `Bool`
-Number | `NSNumber.floatValue` | `Float`
+Boolean | `NSNumber.boolValue` | `NSNumber.boolValue`
+Number | `NSNumber.floatValue` | `NSNumber.floatValue`
Array (`-dasharray`) | `NSArray<NSNumber>` | `[Float]`
Array (`-font`) | `NSArray<NSString>` | `[String]`
Array (`-offset`, `-translate`) | `NSValue.CGVectorValue` | `NSValue.cgVectorValue`
@@ -302,38 +308,73 @@ in Swift
are specified in counterclockwise order, in contrast to the clockwise order
defined by the style specification.
-### Style functions
-
-A _style function_ allows you to vary the value of a layout or paint attribute
-based on the zoom level, data provided by content sources, or both. For more
-information about style functions, see “[Using Style Functions at Runtime](using-style-functions-at-runtime.html)”.
-
-Each kind of style function is represented by a distinct class, but you
-typically create style functions as you create any other style value, using
-class methods on `MGLStyleValue`:
-
-In style specification | SDK class | SDK factory method
----------------------------|-----------------------------|-------------------
-zoom function | `MGLCameraStyleFunction` | `+[MGLStyleValue valueWithInterpolationMode:cameraStops:options:]`
-property function | `MGLSourceStyleFunction` | `+[MGLStyleValue valueWithInterpolationMode:sourceStops:attributeName:options:]`
-zoom-and-property function | `MGLCompositeStyleFunction` | `+[MGLStyleValue valueWithInterpolationMode:compositeStops:attributeName:options:]`
-
-The documentation for each individual style layer property indicates the kinds
-of style functions that are enabled for that property.
-
-When you create a style function, you specify an _interpolation mode_ and a
-series of _stops_. Each stop determines the effective value displayed at a
-particular zoom level (for camera functions) or the effective value on features
-with a particular attribute value in the content source (for source functions).
-The interpolation mode tells the SDK how to calculate the effective value
-between any two stops:
-
-In style specification | In the SDK
------------------------------|-----------
-`exponential` | `MGLInterpolationModeExponential`
-`interval` | `MGLInterpolationModeInterval`
-`categorical` | `MGLInterpolationModeCategorical`
-`identity` | `MGLInterpolationModeIdentity`
+### Expression operators
+
+In style specification | Method, function, or predicate type | Format string syntax
+-----------------------|-------------------------------------|---------------------
+`array` | |
+`boolean` | |
+`literal` | `+[NSExpression expressionForConstantValue:]` | `%@` representing `NSArray` or `NSDictionary`
+`number` | |
+`string` | |
+`to-boolean` | `boolValue` |
+`to-color` | |
+`to-number` | `mgl_numberWithFallbackValues:` |
+`to-string` | `stringValue` |
+`typeof` | |
+`geometry-type` | |
+`id` | |
+`properties` | |
+`at` | |
+`get` | `+[NSExpression expressionForKeyPath:]` | Key path
+`has` | |
+`length` | `count:` | `count({1, 2, 2, 3, 4, 7, 9})`
+`!` | `NSNotPredicateType` | `NOT (p0 OR … OR pn)`
+`!=` | `NSNotEqualToPredicateOperatorType` | `key != value`
+`<` | `NSLessThanPredicateOperatorType` | `key < value`
+`<=` | `NSLessThanOrEqualToPredicateOperatorType` | `key <= value`
+`==` | `NSEqualToPredicateOperatorType` | `key == value`
+`>` | `NSGreaterThanPredicateOperatorType` | `key > value`
+`>=` | `NSGreaterThanOrEqualToPredicateOperatorType` | `key >= value`
+`all` | `NSAndPredicateType` | `p0 AND … AND pn`
+`any` | `NSOrPredicateType` | `p0 OR … OR pn`
+`case` | `+[NSExpression expressionForConditional:trueExpression:falseExpression:]` | `TERNARY(condition, trueExpression, falseExpression)`
+`coalesce` | |
+`match` | |
+`interpolate` | `mgl_interpolateWithCurveType:parameters:stops:` |
+`step` | `mgl_stepWithMinimum:stops:` |
+`let` | `mgl_expressionWithContext:` |
+`var` | `+[NSExpression expressionForVariable:]` | `$variable`
+`concat` | `stringByAppendingString:` |
+`downcase` | `lowercase:` | `lowercase('DOWNTOWN')`
+`upcase` | `uppercase:` | `uppercase('Elysian Fields')`
+
+`rgb` | `+[UIColor colorWithRed:green:blue:alpha:]` |
+`rgba` | `+[UIColor colorWithRed:green:blue:alpha:]` |
+`to-rgba` | |
+`-` | `from:subtract:` | `2 - 1`
+`*` | `multiply:by:` | `1 * 2`
+`/` | `divide:by:` | `1 / 2`
+`%` | `modulus:by:` |
+`^` | `raise:toPower:` | `2 ** 2`
+`+` | `add:to:` | `1 + 2`
+`acos` | |
+`asin` | |
+`atan` | |
+`cos` | |
+`e` | | `%@` representing `NSNumber` containing `M_E`
+`ln` | `ln:` | `ln(2)`
+`ln2` | | `%@` representing `NSNumber` containing `M_LN2`
+`log10` | `log:` | `log(1)`
+`log2` | |
+`max` | `max:` | `max({1, 2, 2, 3, 4, 7, 9})`
+`min` | `min:` | `min({1, 2, 2, 3, 4, 7, 9})`
+`pi` | | `%@` representing `NSNumber` containing `M_PI`
+`sin` | |
+`sqrt` | `sqrt:` | `sqrt(2)`
+`tan` | |
+`zoom` | | `$zoom`
+`heatmap-density` | | `$heatmapDensity`
## Filtering sources
@@ -358,5 +399,5 @@ In style JSON | In the format string
`["any", f0, …, fn]` | `p0 OR … OR pn`
`["none", f0, …, fn]` | `NOT (p0 OR … OR pn)`
-See the `MGLVectorStyleLayer.predicate` documentation for a full description of
-the supported operators and operand types.
+See the “[Predicates and Expressions](Predicates and Expressions.md)” guide for
+a full description of the supported operators and operand types.
diff --git a/platform/ios/docs/guides/Using Style Functions at Runtime.md b/platform/ios/docs/guides/Using Style Functions at Runtime.md
index c1fcaa00e9..0b4e842e0e 100644
--- a/platform/ios/docs/guides/Using Style Functions at Runtime.md
+++ b/platform/ios/docs/guides/Using Style Functions at Runtime.md
@@ -27,15 +27,15 @@ The documentation for each individual style layer property notes which style fun
## Stops
-Stops are key-value pairs that that determine a style value. With a `MGLCameraSourceFunction` stop, you can use a dictionary with a zoom level for a key and a `MGLStyleValue` for the value. For example, you can use a stops dictionary with zoom levels 0, 10, and 20 as keys, and yellow, orange, and red as the values. A `MGLSourceStyleFunction` uses the relevant attribute value as the key.
+Stops are dictionary keys that are associated with layer attribute values. With feature attribute values as stops, you can use a dictionary with a zoom level for a key and an expression or constant value for the value. For example, you can use a stop dictionary with the zoom levels 0, 10, and 20 as keys and the colors yellow, orange, and red as the values. Alternatively, attribute values can be the keys.
```swift
-let stops = [
- 0: MGLStyleValue<UIColor>(rawValue: .yellow),
- 2.5: MGLStyleValue(rawValue: .orange),
- 5: MGLStyleValue(rawValue: .red),
- 7.5: MGLStyleValue(rawValue: .blue),
- 10: MGLStyleValue(rawValue: .white),
+let stops: [Float: UIColor] = [
+ 0: .yellow,
+ 2.5: .orange,
+ 5: .red,
+ 7.5: .blue,
+ 10: .white,
]
```
@@ -57,20 +57,18 @@ let symbolLayer = MGLSymbolStyleLayer(identifier: "place-city-sm", source: symbo
let source = MGLShapeSource(identifier: "earthquakes", url: url, options: nil)
mapView.style?.addSource(source)
-let stops = [
- 0: MGLStyleValue<UIColor>(rawValue: .yellow),
- 2.5: MGLStyleValue(rawValue: .orange),
- 5: MGLStyleValue(rawValue: .red),
- 7.5: MGLStyleValue(rawValue: .blue),
- 10: MGLStyleValue(rawValue: .white),
+let stops: [Float: UIColor] = [
+ 0: .yellow,
+ 2.5: .orange,
+ 5: .red,
+ 7.5: .blue,
+ 10: .white,
]
let layer = MGLCircleStyleLayer(identifier: "circles", source: source)
-layer.circleColor = MGLStyleValue(interpolationMode: .exponential,
- sourceStops: stops,
- attributeName: "mag",
- options: [.defaultValue: MGLStyleValue<UIColor>(rawValue: .green)])
-layer.circleRadius = MGLStyleValue(rawValue: 10)
+layer.circleColor = NSExpression(format: "FUNCTION(mag, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)",
+ stops)
+layer.circleRadius = NSExpression(forConstantValue: 10)
mapView.style?.insertLayer(layer, below: symbolLayer)
```
@@ -89,14 +87,13 @@ The example below increases a layer’s `circleRadius` exponentially based on a
```swift
let stops = [
- 12: MGLStyleValue<NSNumber>(rawValue: 0.5),
- 14: MGLStyleValue(rawValue: 2),
- 18: MGLStyleValue(rawValue: 18),
+ 12: 0.5,
+ 14: 2,
+ 18: 18,
]
-layer.circleRadius = MGLStyleValue(interpolationMode: .exponential,
- cameraStops: stops,
- options: [.interpolationBase: 1.5])
+layer.circleRadius = NSExpression(format: "FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'exponential', 1.5, %@)",
+ stops)
```
### Interval
@@ -106,18 +103,16 @@ layer.circleRadius = MGLStyleValue(interpolationMode: .exponential,
When we use the stops dictionary given above with an interval interpolation mode, we create ranges where earthquakes with a magnitude of 0 to just less than 2.5 would be yellow, 2.5 to just less than 5 would be orange, and so on.
```swift
-let stops = [
- 0: MGLStyleValue<UIColor>(rawValue: .yellow),
- 2.5: MGLStyleValue(rawValue: .orange),
- 5: MGLStyleValue(rawValue: .red),
- 7.5: MGLStyleValue(rawValue: .blue),
- 10: MGLStyleValue(rawValue: .white),
+let stops: [Float: UIColor] = [
+ 0: .yellow,
+ 2.5: .orange,
+ 5: .red,
+ 7.5: .blue,
+ 10: .white,
]
-layer.circleColor = MGLStyleValue(interpolationMode: .interval,
- sourceStops: stops,
- attributeName: "mag",
- options: [.defaultValue: MGLStyleValue<UIColor>(rawValue: .green)])
+layer.circleColor = NSExpression(format: "FUNCTION(mag, 'mgl_stepWithMinimum:stops:', %@, %@)",
+ UIColor.green, stops)
```
![interval mode](img/data-driven-styling/interval.png)
@@ -129,16 +124,16 @@ At each stop, `MGLInterpolationModeCategorical` produces an output value equal t
There are three main types of events in the dataset: earthquakes, explosions, and quarry blasts. In this case, the color of the circle layer will be determined by the type of event, with a default value of blue to catch any events that do not fall into any of those categories.
```swift
-let categoricalStops = [
- "earthquake": MGLStyleValue<UIColor>(rawValue: .orange),
- "explosion": MGLStyleValue(rawValue: .red),
- "quarry blast": MGLStyleValue(rawValue: .yellow),
+let colors: [String: UIColor] = [
+ "earthquake": .orange,
+ "explosion": .red,
+ "quarry blast": .yellow,
]
+let defaultColor = UIColor.blue
-layer.circleColor = MGLStyleValue(interpolationMode: .categorical,
- sourceStops: categoricalStops,
- attributeName: "type",
- options: [.defaultValue: MGLStyleValue<UIColor>(rawValue: .blue)])
+layer.circleColor = NSExpression(
+ format: "TERNARY(FUNCTION(%@, 'valueForKeyPath:', type) != nil, FUNCTION(%@, 'valueForKeyPath:', type), %@)",
+ colors, colors, defaultColor)
```
![categorical mode](img/data-driven-styling/categorical1.png) ![categorical mode](img/data-driven-styling/categorical2.png)
@@ -148,10 +143,7 @@ layer.circleColor = MGLStyleValue(interpolationMode: .categorical,
`MGLInterpolationModeIdentity` uses the attribute’s value as the style value. For example, you can set the `circleRadius` to the earthquake’s magnitude. Since the attribute value itself will be used as the style value, `sourceStops` should be set to `nil`.
```swift
-layer.circleRadius = MGLStyleValue(interpolationMode: .identity,
- sourceStops: nil,
- attributeName: "mag",
- options: [.defaultValue: MGLStyleValue<NSNumber>(rawValue: 0)])
+layer.circleRadius = NSExpression(forKeyPath: "mag")
```
![identity mode](img/data-driven-styling/identity.png)
diff --git a/platform/ios/framework/Settings.bundle/he.lproj/Root.strings b/platform/ios/framework/Settings.bundle/he.lproj/Root.strings
new file mode 100644
index 0000000000..0fd5881e5f
--- /dev/null
+++ b/platform/ios/framework/Settings.bundle/he.lproj/Root.strings
@@ -0,0 +1,3 @@
+"TELEMETRY_GROUP_TITLE" = "הגדרות פרטיות";
+"TELEMETRY_SWITCH_TITLE" = "Mapbox Telemetry";
+"TELEMETRY_GROUP_FOOTER" = "הגדרה זו מתירה לאפליקציה לשתף באופן אנונימי את נתוני המיקום והשימוש עם Mapbox.";
diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj
index bbd4067534..07fae5945c 100644
--- a/platform/ios/ios.xcodeproj/project.pbxproj
+++ b/platform/ios/ios.xcodeproj/project.pbxproj
@@ -12,7 +12,24 @@
071BBB031EE76146001FB02A /* MGLImageSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 071BBAFC1EE75CD4001FB02A /* MGLImageSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
071BBB041EE76147001FB02A /* MGLImageSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 071BBAFC1EE75CD4001FB02A /* MGLImageSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
071BBB071EE77631001FB02A /* MGLImageSourceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 071BBB051EE7761A001FB02A /* MGLImageSourceTests.m */; };
+ 0778DD431F67556700A73B34 /* MGLComputedShapeSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 0778DD401F67555F00A73B34 /* MGLComputedShapeSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 0778DD441F67556C00A73B34 /* MGLComputedShapeSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0778DD411F67555F00A73B34 /* MGLComputedShapeSource.mm */; };
+ 07D8C6FB1F67560100381808 /* MGLComputedShapeSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0778DD411F67555F00A73B34 /* MGLComputedShapeSource.mm */; };
+ 07D8C6FC1F67560400381808 /* MGLAbstractShapeSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 07D947501F67487E00E37934 /* MGLAbstractShapeSource.mm */; };
+ 07D8C6FF1F67562C00381808 /* MGLComputedShapeSourceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 07D8C6FD1F67562800381808 /* MGLComputedShapeSourceTests.m */; };
+ 07D947521F67488800E37934 /* MGLAbstractShapeSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 07D9474F1F67487E00E37934 /* MGLAbstractShapeSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 07D947531F67488E00E37934 /* MGLAbstractShapeSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 07D9474E1F67487E00E37934 /* MGLAbstractShapeSource_Private.h */; };
+ 07D947541F67489200E37934 /* MGLAbstractShapeSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 07D947501F67487E00E37934 /* MGLAbstractShapeSource.mm */; };
+ 16376B0A1FFD9DAF0000563E /* MBGLIntegrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 16376B091FFD9DAF0000563E /* MBGLIntegrationTests.m */; };
+ 16376B331FFDB4B40000563E /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 16376B321FFDB4B40000563E /* AppDelegate.m */; };
+ 16376B3B1FFDB4B40000563E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 16376B3A1FFDB4B40000563E /* Assets.xcassets */; };
+ 16376B3E1FFDB4B40000563E /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 16376B3C1FFDB4B40000563E /* LaunchScreen.storyboard */; };
+ 16376B411FFDB4B40000563E /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 16376B401FFDB4B40000563E /* main.m */; };
+ 16376B471FFDB92B0000563E /* one-liner.json in Resources */ = {isa = PBXBuildFile; fileRef = DA35D0871E1A6309007DED41 /* one-liner.json */; };
16376B491FFEED010000563E /* MGLMapViewLayoutTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 16376B481FFEED010000563E /* MGLMapViewLayoutTests.m */; };
+ 165D0CE720005419009A3C66 /* Mapbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA8847D21CBAF91600AB86E3 /* Mapbox.framework */; };
+ 170C437C2029D96F00863DF0 /* MGLHeatmapColorTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 170C43782028D49800863DF0 /* MGLHeatmapColorTests.mm */; };
+ 170C437D2029D97900863DF0 /* MGLHeatmapStyleLayerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 170C43792028D49800863DF0 /* MGLHeatmapStyleLayerTests.mm */; };
1753ED421E53CE6F00A9FD90 /* MGLConversion.h in Headers */ = {isa = PBXBuildFile; fileRef = 1753ED411E53CE6F00A9FD90 /* MGLConversion.h */; };
1753ED431E53CE6F00A9FD90 /* MGLConversion.h in Headers */ = {isa = PBXBuildFile; fileRef = 1753ED411E53CE6F00A9FD90 /* MGLConversion.h */; };
1F06668A1EC64F8E001C16D7 /* MGLLight.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F0666881EC64F8E001C16D7 /* MGLLight.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -23,10 +40,6 @@
1F7454971ECD450D00021D39 /* MGLLight_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F7454941ECD450D00021D39 /* MGLLight_Private.h */; };
1F7454A91ED08AB400021D39 /* MGLLightTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1F7454A61ED08AB400021D39 /* MGLLightTest.mm */; };
1F95931D1E6DE2E900D5B294 /* MGLNSDateAdditionsTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1F95931C1E6DE2E900D5B294 /* MGLNSDateAdditionsTests.mm */; };
- 1FB7DAAF1F2A4DBD00410606 /* MGLVectorSource+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1FDD9D6D1F26936400252B09 /* MGLVectorSource+MGLAdditions.h */; };
- 1FB7DAB01F2A4DC200410606 /* MGLVectorSource+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1FDD9D6D1F26936400252B09 /* MGLVectorSource+MGLAdditions.h */; };
- 1FB7DAB11F2A4DC800410606 /* MGLVectorSource+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FDD9D6E1F26936400252B09 /* MGLVectorSource+MGLAdditions.m */; };
- 1FB7DAB21F2A4DC900410606 /* MGLVectorSource+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FDD9D6E1F26936400252B09 /* MGLVectorSource+MGLAdditions.m */; };
30E578171DAA85520050F07E /* UIImage+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 30E578111DAA7D690050F07E /* UIImage+MGLAdditions.h */; };
30E578181DAA85520050F07E /* UIImage+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 30E578111DAA7D690050F07E /* UIImage+MGLAdditions.h */; };
30E578191DAA855E0050F07E /* UIImage+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 30E578121DAA7D690050F07E /* UIImage+MGLAdditions.mm */; };
@@ -43,7 +56,7 @@
3510FFEB1D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3510FFE81D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.h */; };
3510FFEC1D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3510FFE91D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.mm */; };
3510FFED1D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3510FFE91D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.mm */; };
- 3510FFF01D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3510FFEE1D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h */; };
+ 3510FFF01D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3510FFEE1D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
3510FFF11D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3510FFEE1D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h */; };
3510FFF21D6D9D8C00F413B2 /* NSExpression+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3510FFEF1D6D9D8C00F413B2 /* NSExpression+MGLAdditions.mm */; };
3510FFF31D6D9D8C00F413B2 /* NSExpression+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3510FFEF1D6D9D8C00F413B2 /* NSExpression+MGLAdditions.mm */; };
@@ -121,7 +134,6 @@
357FE2DF1E02D2B20068B753 /* NSCoder+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 357FE2DC1E02D2B20068B753 /* NSCoder+MGLAdditions.mm */; };
357FE2E01E02D2B20068B753 /* NSCoder+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 357FE2DC1E02D2B20068B753 /* NSCoder+MGLAdditions.mm */; };
3598544D1E1D38AA00B29F84 /* MGLDistanceFormatterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3598544C1E1D38AA00B29F84 /* MGLDistanceFormatterTests.m */; };
- 3599A3E61DF708BC00E77FB2 /* MGLStyleValueTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3599A3E51DF708BC00E77FB2 /* MGLStyleValueTests.m */; };
359F57461D2FDDA6005217F1 /* MGLUserLocationAnnotationView_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 359F57451D2FDBD5005217F1 /* MGLUserLocationAnnotationView_Private.h */; };
35B82BF81D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 35B82BF61D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h */; };
35B82BF91D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 35B82BF61D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h */; };
@@ -216,14 +228,10 @@
55E2AD131E5B125400E8C587 /* MGLOfflineStorageTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 55E2AD121E5B125400E8C587 /* MGLOfflineStorageTests.mm */; };
632281DF1E6F855900D75A5D /* MBXEmbeddedMapViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 632281DE1E6F855900D75A5D /* MBXEmbeddedMapViewController.m */; };
6407D6701E0085FD00F6A9C3 /* MGLDocumentationExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6407D66F1E0085FD00F6A9C3 /* MGLDocumentationExampleTests.swift */; };
- 7E016D7E1D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 7E016D7C1D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.h */; };
- 7E016D7F1D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 7E016D7C1D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.h */; };
- 7E016D801D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 7E016D7D1D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.m */; };
- 7E016D811D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 7E016D7D1D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.m */; };
- 7E016D841D9E890300A29A21 /* MGLPolygon+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 7E016D821D9E890300A29A21 /* MGLPolygon+MGLAdditions.h */; };
- 7E016D851D9E890300A29A21 /* MGLPolygon+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 7E016D821D9E890300A29A21 /* MGLPolygon+MGLAdditions.h */; };
- 7E016D861D9E890300A29A21 /* MGLPolygon+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 7E016D831D9E890300A29A21 /* MGLPolygon+MGLAdditions.m */; };
- 7E016D871D9E890300A29A21 /* MGLPolygon+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 7E016D831D9E890300A29A21 /* MGLPolygon+MGLAdditions.m */; };
+ 8989B17C201A48EB0081CF59 /* MGLHeatmapStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 8989B17A201A48EA0081CF59 /* MGLHeatmapStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 8989B17D201A48EB0081CF59 /* MGLHeatmapStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 8989B17A201A48EA0081CF59 /* MGLHeatmapStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 8989B17E201A48EB0081CF59 /* MGLHeatmapStyleLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8989B17B201A48EA0081CF59 /* MGLHeatmapStyleLayer.mm */; };
+ 8989B17F201A48EB0081CF59 /* MGLHeatmapStyleLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8989B17B201A48EA0081CF59 /* MGLHeatmapStyleLayer.mm */; };
920A3E5D1E6F995200C16EFC /* MGLSourceQueryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 920A3E5C1E6F995200C16EFC /* MGLSourceQueryTests.m */; };
927FBCFC1F4DAA8300F8BF1F /* MBXSnapshotsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 927FBCFB1F4DAA8300F8BF1F /* MBXSnapshotsViewController.m */; };
927FBCFF1F4DB05500F8BF1F /* MGLMapSnapshotter.h in Headers */ = {isa = PBXBuildFile; fileRef = 927FBCFD1F4DB05500F8BF1F /* MGLMapSnapshotter.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -232,12 +240,19 @@
927FBD021F4DB05500F8BF1F /* MGLMapSnapshotter.mm in Sources */ = {isa = PBXBuildFile; fileRef = 927FBCFE1F4DB05500F8BF1F /* MGLMapSnapshotter.mm */; };
929EFFAB1F56DCD4003A77D5 /* MGLAnnotationView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4018B1C41CDC277F00F666AF /* MGLAnnotationView.mm */; };
92F2C3ED1F0E3C3A00268EC0 /* MGLRendererFrontend.h in Headers */ = {isa = PBXBuildFile; fileRef = 92F2C3EC1F0E3C3A00268EC0 /* MGLRendererFrontend.h */; };
+ 96036A01200565C700510F3D /* NSOrthography+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 960369FF200565C700510F3D /* NSOrthography+MGLAdditions.h */; };
+ 96036A02200565C700510F3D /* NSOrthography+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 960369FF200565C700510F3D /* NSOrthography+MGLAdditions.h */; };
+ 96036A03200565C700510F3D /* NSOrthography+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 96036A00200565C700510F3D /* NSOrthography+MGLAdditions.m */; };
+ 96036A04200565C700510F3D /* NSOrthography+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 96036A00200565C700510F3D /* NSOrthography+MGLAdditions.m */; };
+ 96036A0620059BBA00510F3D /* MGLNSOrthographyAdditionsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 96036A0520059BBA00510F3D /* MGLNSOrthographyAdditionsTests.m */; };
960D0C361ECF5AAF008E151F /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 960D0C351ECF5AAF008E151F /* Images.xcassets */; };
960D0C371ECF5AAF008E151F /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 960D0C351ECF5AAF008E151F /* Images.xcassets */; };
9620BB381E69FE1700705A1D /* MGLSDKUpdateChecker.h in Headers */ = {isa = PBXBuildFile; fileRef = 9620BB361E69FE1700705A1D /* MGLSDKUpdateChecker.h */; };
9620BB391E69FE1700705A1D /* MGLSDKUpdateChecker.h in Headers */ = {isa = PBXBuildFile; fileRef = 9620BB361E69FE1700705A1D /* MGLSDKUpdateChecker.h */; };
9620BB3A1E69FE1700705A1D /* MGLSDKUpdateChecker.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9620BB371E69FE1700705A1D /* MGLSDKUpdateChecker.mm */; };
9620BB3B1E69FE1700705A1D /* MGLSDKUpdateChecker.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9620BB371E69FE1700705A1D /* MGLSDKUpdateChecker.mm */; };
+ 9654C1261FFC1AB900DB6A19 /* MGLPolyline_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 9654C1251FFC1AB900DB6A19 /* MGLPolyline_Private.h */; };
+ 9654C1291FFC1CCD00DB6A19 /* MGLPolygon_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 9654C1271FFC1CC000DB6A19 /* MGLPolygon_Private.h */; };
966FCF4C1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 966FCF4A1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.h */; };
966FCF4E1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 966FCF4B1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.m */; };
966FCF4F1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 966FCF4B1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.m */; };
@@ -246,6 +261,47 @@
966FCF551F3C323500F2B6DE /* MGLUserLocationHeadingArrowLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 966FCF511F3C321000F2B6DE /* MGLUserLocationHeadingArrowLayer.m */; };
968F36B51E4D128D003A5522 /* MGLDistanceFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = 3557F7AE1E1D27D300CCA5E6 /* MGLDistanceFormatter.h */; settings = {ATTRIBUTES = (Public, ); }; };
96E027231E57C76E004B8E66 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 96E027251E57C76E004B8E66 /* Localizable.strings */; };
+ 96E516DC2000547000A02306 /* MGLPolyline_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 9654C1251FFC1AB900DB6A19 /* MGLPolyline_Private.h */; };
+ 96E516DD200054F200A02306 /* MGLPolygon_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 9654C1271FFC1CC000DB6A19 /* MGLPolygon_Private.h */; };
+ 96E516DE200054F700A02306 /* MGLGeometry_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848011CBAFA6200AB86E3 /* MGLGeometry_Private.h */; };
+ 96E516DF200054FB00A02306 /* MGLShape_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 40CF6DBA1DAC3C1800A4D18B /* MGLShape_Private.h */; };
+ 96E516E02000550C00A02306 /* MGLFeature_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DAD1656A1CF41981001FF4B9 /* MGLFeature_Private.h */; };
+ 96E516E12000551100A02306 /* MGLMultiPoint_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848041CBAFA6200AB86E3 /* MGLMultiPoint_Private.h */; };
+ 96E516E22000551900A02306 /* MGLPointCollection_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4049C2AB1DB6E05500B3F799 /* MGLPointCollection_Private.h */; };
+ 96E516E32000552A00A02306 /* MGLAccountManager_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8847FF1CBAFA6200AB86E3 /* MGLAccountManager_Private.h */; };
+ 96E516E42000560B00A02306 /* MGLAbstractShapeSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 07D9474E1F67487E00E37934 /* MGLAbstractShapeSource_Private.h */; };
+ 96E516E52000560B00A02306 /* MGLOfflinePack_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848061CBAFA6200AB86E3 /* MGLOfflinePack_Private.h */; };
+ 96E516E62000560B00A02306 /* MGLOfflineRegion_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848081CBAFA6200AB86E3 /* MGLOfflineRegion_Private.h */; };
+ 96E516E72000560B00A02306 /* MGLOfflineStorage_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848091CBAFA6200AB86E3 /* MGLOfflineStorage_Private.h */; };
+ 96E516E82000560B00A02306 /* MGLAnnotationContainerView_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 404326881D5B9B1A007111BD /* MGLAnnotationContainerView_Private.h */; };
+ 96E516E92000560B00A02306 /* MGLAnnotationImage_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848401CBAFB9800AB86E3 /* MGLAnnotationImage_Private.h */; };
+ 96E516EA2000560B00A02306 /* MGLAnnotationView_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4018B1C31CDC277F00F666AF /* MGLAnnotationView_Private.h */; };
+ 96E516EB2000560B00A02306 /* MGLUserLocation_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DA88484B1CBAFB9800AB86E3 /* MGLUserLocation_Private.h */; };
+ 96E516EC2000560B00A02306 /* MGLUserLocationAnnotationView_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 359F57451D2FDBD5005217F1 /* MGLUserLocationAnnotationView_Private.h */; };
+ 96E516ED200058A200A02306 /* MGLComputedShapeSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 0778DD401F67555F00A73B34 /* MGLComputedShapeSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 96E516EE2000590900A02306 /* MGLAbstractShapeSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 07D9474F1F67487E00E37934 /* MGLAbstractShapeSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 96E516EF2000594F00A02306 /* NSArray+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 400532FF1DB0862B0069F638 /* NSArray+MGLAdditions.h */; };
+ 96E516F02000595800A02306 /* NSBundle+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848121CBAFA6200AB86E3 /* NSBundle+MGLAdditions.h */; };
+ 96E516F12000596800A02306 /* NSString+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848171CBAFA6200AB86E3 /* NSString+MGLAdditions.h */; };
+ 96E516F22000596D00A02306 /* NSException+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848141CBAFA6200AB86E3 /* NSException+MGLAdditions.h */; };
+ 96E516F32000597100A02306 /* NSDictionary+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 408AA8551DAEDA0800022900 /* NSDictionary+MGLAdditions.h */; };
+ 96E516F42000597D00A02306 /* NSData+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 35305D461D22AA450007D005 /* NSData+MGLAdditions.h */; };
+ 96E516F5200059B100A02306 /* MGLNetworkConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = DD0902A41DB18F1B00C5BDCE /* MGLNetworkConfiguration.h */; };
+ 96E516F6200059EC00A02306 /* MGLRendererFrontend.h in Headers */ = {isa = PBXBuildFile; fileRef = 92F2C3EC1F0E3C3A00268EC0 /* MGLRendererFrontend.h */; };
+ 96E516F720005A2700A02306 /* MGLAnnotationContainerView.h in Headers */ = {isa = PBXBuildFile; fileRef = 40EDA1BD1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.h */; };
+ 96E516F820005A3000A02306 /* MGLCompactCalloutView.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848441CBAFB9800AB86E3 /* MGLCompactCalloutView.h */; };
+ 96E516F920005A3500A02306 /* MGLFaux3DUserLocationAnnotationView.h in Headers */ = {isa = PBXBuildFile; fileRef = DA88484D1CBAFB9800AB86E3 /* MGLFaux3DUserLocationAnnotationView.h */; };
+ 96E516FA20005A3D00A02306 /* MGLUserLocationHeadingArrowLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 966FCF501F3C321000F2B6DE /* MGLUserLocationHeadingArrowLayer.h */; };
+ 96E516FB20005A4000A02306 /* MGLUserLocationHeadingBeamLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 966FCF4A1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.h */; };
+ 96E516FC20005A4400A02306 /* MGLUserLocationHeadingIndicator.h in Headers */ = {isa = PBXBuildFile; fileRef = 96F3F73B1F5711F1003E2D2C /* MGLUserLocationHeadingIndicator.h */; };
+ 96E516FD20005A4700A02306 /* MGLAPIClient.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848421CBAFB9800AB86E3 /* MGLAPIClient.h */; };
+ 96E516FE20005A4C00A02306 /* MGLLocationManager.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848461CBAFB9800AB86E3 /* MGLLocationManager.h */; };
+ 96E516FF20005A4F00A02306 /* MGLMapboxEvents.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848481CBAFB9800AB86E3 /* MGLMapboxEvents.h */; };
+ 96E5170020005A6100A02306 /* Fabric.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848831CBB033F00AB86E3 /* Fabric.h */; };
+ 96E5170120005A6400A02306 /* Fabric+FABKits.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848821CBB033F00AB86E3 /* Fabric+FABKits.h */; };
+ 96E5170220005A6600A02306 /* FABAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848801CBB033F00AB86E3 /* FABAttributes.h */; };
+ 96E5170320005A6800A02306 /* FABKitProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848811CBB033F00AB86E3 /* FABKitProtocol.h */; };
+ 96E5170420005A6B00A02306 /* SMCalloutView.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848891CBB037E00AB86E3 /* SMCalloutView.h */; };
96F3F73C1F57124B003E2D2C /* MGLUserLocationHeadingIndicator.h in Headers */ = {isa = PBXBuildFile; fileRef = 96F3F73B1F5711F1003E2D2C /* MGLUserLocationHeadingIndicator.h */; };
AC518DFF201BB55A00EBC820 /* MGLTelemetryConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = AC518DFD201BB55A00EBC820 /* MGLTelemetryConfig.h */; };
AC518E00201BB55A00EBC820 /* MGLTelemetryConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = AC518DFD201BB55A00EBC820 /* MGLTelemetryConfig.h */; };
@@ -270,7 +326,6 @@
DA1DC99B1CB6E064006E619F /* MBXViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DA1DC99A1CB6E064006E619F /* MBXViewController.m */; };
DA1DC99F1CB6E088006E619F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DA1DC99E1CB6E088006E619F /* Assets.xcassets */; };
DA1F8F3D1EBD287B00367E42 /* MGLDocumentationGuideTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA1F8F3C1EBD287B00367E42 /* MGLDocumentationGuideTests.swift */; };
- DA2207BF1DC0805F0002F84D /* MGLStyleValueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA2207BE1DC0805F0002F84D /* MGLStyleValueTests.swift */; };
DA25D5C01CCD9F8400607828 /* Root.plist in Resources */ = {isa = PBXBuildFile; fileRef = DA25D5BF1CCD9F8400607828 /* Root.plist */; };
DA25D5C61CCDA06800607828 /* Root.strings in Resources */ = {isa = PBXBuildFile; fileRef = DA25D5C41CCDA06800607828 /* Root.strings */; };
DA25D5CD1CCDA11500607828 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = DA25D5B91CCD9EDE00607828 /* Settings.bundle */; };
@@ -390,7 +445,6 @@
DA8848871CBB033F00AB86E3 /* Fabric.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848831CBB033F00AB86E3 /* Fabric.h */; };
DA88488B1CBB037E00AB86E3 /* SMCalloutView.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848891CBB037E00AB86E3 /* SMCalloutView.h */; };
DA88488C1CBB037E00AB86E3 /* SMCalloutView.m in Sources */ = {isa = PBXBuildFile; fileRef = DA88488A1CBB037E00AB86E3 /* SMCalloutView.m */; };
- DA88488E1CBB047F00AB86E3 /* reachability.h in Headers */ = {isa = PBXBuildFile; fileRef = DA88488D1CBB047F00AB86E3 /* reachability.h */; };
DA8933A31CCC95B000E68420 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = DA89339F1CCC951200E68420 /* Localizable.strings */; };
DA8933BC1CCD2CA100E68420 /* Foundation.strings in Resources */ = {isa = PBXBuildFile; fileRef = DA8933BA1CCD2CA100E68420 /* Foundation.strings */; };
DA8933BF1CCD2CAD00E68420 /* Foundation.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = DA8933BD1CCD2CAD00E68420 /* Foundation.stringsdict */; };
@@ -463,8 +517,14 @@
DABFB8701CBE9A0F00D62B32 /* MGLMapView+IBAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848371CBAFB8500AB86E3 /* MGLMapView+IBAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
DABFB8721CBE9A0F00D62B32 /* MGLUserLocation.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848391CBAFB8500AB86E3 /* MGLUserLocation.h */; settings = {ATTRIBUTES = (Public, ); }; };
DABFB8731CBE9A9900D62B32 /* Mapbox.h in Headers */ = {isa = PBXBuildFile; fileRef = DA88485E1CBAFC2E00AB86E3 /* Mapbox.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DAC25FCC200FD83F009BE98E /* NSExpression+MGLPrivateAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DAC25FCB200FD83E009BE98E /* NSExpression+MGLPrivateAdditions.h */; };
+ DAC25FCD200FD83F009BE98E /* NSExpression+MGLPrivateAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DAC25FCB200FD83E009BE98E /* NSExpression+MGLPrivateAdditions.h */; };
DAC49C5C1CD02BC9009E1AA3 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = DAC49C5F1CD02BC9009E1AA3 /* Localizable.stringsdict */; };
DAC49C5D1CD02BC9009E1AA3 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = DAC49C5F1CD02BC9009E1AA3 /* Localizable.stringsdict */; };
+ DACA86262019218600E9693A /* MGLRasterDEMSource.h in Headers */ = {isa = PBXBuildFile; fileRef = DACA86242019218500E9693A /* MGLRasterDEMSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DACA86272019218600E9693A /* MGLRasterDEMSource.h in Headers */ = {isa = PBXBuildFile; fileRef = DACA86242019218500E9693A /* MGLRasterDEMSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DACA86282019218600E9693A /* MGLRasterDEMSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = DACA86252019218500E9693A /* MGLRasterDEMSource.mm */; };
+ DACA86292019218600E9693A /* MGLRasterDEMSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = DACA86252019218500E9693A /* MGLRasterDEMSource.mm */; };
DAD1656C1CF41981001FF4B9 /* MGLFeature.h in Headers */ = {isa = PBXBuildFile; fileRef = DAD165691CF41981001FF4B9 /* MGLFeature.h */; settings = {ATTRIBUTES = (Public, ); }; };
DAD1656D1CF41981001FF4B9 /* MGLFeature.h in Headers */ = {isa = PBXBuildFile; fileRef = DAD165691CF41981001FF4B9 /* MGLFeature.h */; settings = {ATTRIBUTES = (Public, ); }; };
DAD1656E1CF41981001FF4B9 /* MGLFeature_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DAD1656A1CF41981001FF4B9 /* MGLFeature_Private.h */; };
@@ -486,6 +546,11 @@
DAF0D8141DFE0EC500B28378 /* MGLVectorSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DAF0D8121DFE0EC500B28378 /* MGLVectorSource_Private.h */; };
DAF0D8181DFE6B2800B28378 /* MGLAttributionInfo_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DAF0D8171DFE6B2800B28378 /* MGLAttributionInfo_Private.h */; };
DAF0D8191DFE6B2800B28378 /* MGLAttributionInfo_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DAF0D8171DFE6B2800B28378 /* MGLAttributionInfo_Private.h */; };
+ DAF25719201901E200367EF5 /* MGLHillshadeStyleLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = DAF25717201901E100367EF5 /* MGLHillshadeStyleLayer.mm */; };
+ DAF2571A201901E200367EF5 /* MGLHillshadeStyleLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = DAF25717201901E100367EF5 /* MGLHillshadeStyleLayer.mm */; };
+ DAF2571B201901E200367EF5 /* MGLHillshadeStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = DAF25718201901E200367EF5 /* MGLHillshadeStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DAF2571C201901E200367EF5 /* MGLHillshadeStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = DAF25718201901E200367EF5 /* MGLHillshadeStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DAF25720201902BC00367EF5 /* MGLHillshadeStyleLayerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DAF2571F201902BB00367EF5 /* MGLHillshadeStyleLayerTests.mm */; };
DD0902A91DB1929D00C5BDCE /* MGLNetworkConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = DD0902A21DB18DE700C5BDCE /* MGLNetworkConfiguration.m */; };
DD0902AA1DB1929D00C5BDCE /* MGLNetworkConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = DD0902A21DB18DE700C5BDCE /* MGLNetworkConfiguration.m */; };
DD0902AB1DB192A800C5BDCE /* MGLNetworkConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = DD0902A41DB18F1B00C5BDCE /* MGLNetworkConfiguration.h */; };
@@ -505,6 +570,13 @@
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
+ 165D0CE520005351009A3C66 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = DA1DC9421CB6C1C2006E619F /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = DA8847D11CBAF91600AB86E3;
+ remoteInfo = dynamic;
+ };
DA25D5C71CCDA0C100607828 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = DA1DC9421CB6C1C2006E619F /* Project object */;
@@ -587,15 +659,31 @@
071BBAFC1EE75CD4001FB02A /* MGLImageSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLImageSource.h; sourceTree = "<group>"; };
071BBAFD1EE75CD4001FB02A /* MGLImageSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLImageSource.mm; sourceTree = "<group>"; };
071BBB051EE7761A001FB02A /* MGLImageSourceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLImageSourceTests.m; path = ../../darwin/test/MGLImageSourceTests.m; sourceTree = "<group>"; };
+ 0778DD401F67555F00A73B34 /* MGLComputedShapeSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLComputedShapeSource.h; sourceTree = "<group>"; };
+ 0778DD411F67555F00A73B34 /* MGLComputedShapeSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLComputedShapeSource.mm; sourceTree = "<group>"; };
+ 07D8C6FD1F67562800381808 /* MGLComputedShapeSourceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLComputedShapeSourceTests.m; path = ../../darwin/test/MGLComputedShapeSourceTests.m; sourceTree = "<group>"; };
+ 07D9474E1F67487E00E37934 /* MGLAbstractShapeSource_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAbstractShapeSource_Private.h; sourceTree = "<group>"; };
+ 07D9474F1F67487E00E37934 /* MGLAbstractShapeSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAbstractShapeSource.h; sourceTree = "<group>"; };
+ 07D947501F67487E00E37934 /* MGLAbstractShapeSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLAbstractShapeSource.mm; sourceTree = "<group>"; };
+ 16376B071FFD9DAF0000563E /* integration.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = integration.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ 16376B091FFD9DAF0000563E /* MBGLIntegrationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MBGLIntegrationTests.m; sourceTree = "<group>"; };
+ 16376B0B1FFD9DAF0000563E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+ 16376B2F1FFDB4B40000563E /* Integration Test Harness.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Integration Test Harness.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 16376B311FFDB4B40000563E /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
+ 16376B321FFDB4B40000563E /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
+ 16376B3A1FFDB4B40000563E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
+ 16376B3D1FFDB4B40000563E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
+ 16376B3F1FFDB4B40000563E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+ 16376B401FFDB4B40000563E /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
16376B481FFEED010000563E /* MGLMapViewLayoutTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLMapViewLayoutTests.m; sourceTree = "<group>"; };
+ 170C43782028D49800863DF0 /* MGLHeatmapColorTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLHeatmapColorTests.mm; path = ../../darwin/test/MGLHeatmapColorTests.mm; sourceTree = "<group>"; };
+ 170C43792028D49800863DF0 /* MGLHeatmapStyleLayerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLHeatmapStyleLayerTests.mm; path = ../../darwin/test/MGLHeatmapStyleLayerTests.mm; sourceTree = "<group>"; };
1753ED411E53CE6F00A9FD90 /* MGLConversion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLConversion.h; sourceTree = "<group>"; };
1F0666881EC64F8E001C16D7 /* MGLLight.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLLight.h; sourceTree = "<group>"; };
1F0666891EC64F8E001C16D7 /* MGLLight.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLLight.mm; sourceTree = "<group>"; };
1F7454941ECD450D00021D39 /* MGLLight_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLLight_Private.h; sourceTree = "<group>"; };
1F7454A61ED08AB400021D39 /* MGLLightTest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLLightTest.mm; path = ../../darwin/test/MGLLightTest.mm; sourceTree = "<group>"; };
1F95931C1E6DE2E900D5B294 /* MGLNSDateAdditionsTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLNSDateAdditionsTests.mm; path = ../../darwin/test/MGLNSDateAdditionsTests.mm; sourceTree = "<group>"; };
- 1FDD9D6D1F26936400252B09 /* MGLVectorSource+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MGLVectorSource+MGLAdditions.h"; sourceTree = "<group>"; };
- 1FDD9D6E1F26936400252B09 /* MGLVectorSource+MGLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MGLVectorSource+MGLAdditions.m"; sourceTree = "<group>"; };
20DABE861DF78148007AC5FF /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Foundation.strings"; sourceTree = "<group>"; };
20DABE881DF78148007AC5FF /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = "<group>"; };
20DABE8A1DF78149007AC5FF /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Root.strings"; sourceTree = "<group>"; };
@@ -651,12 +739,10 @@
357579861D502AFE000B822E /* MGLLineStyleLayerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLLineStyleLayerTests.mm; path = ../../darwin/test/MGLLineStyleLayerTests.mm; sourceTree = "<group>"; };
357579881D502B06000B822E /* MGLCircleStyleLayerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLCircleStyleLayerTests.mm; path = ../../darwin/test/MGLCircleStyleLayerTests.mm; sourceTree = "<group>"; };
3575798A1D502B0C000B822E /* MGLBackgroundStyleLayerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLBackgroundStyleLayerTests.mm; path = ../../darwin/test/MGLBackgroundStyleLayerTests.mm; sourceTree = "<group>"; };
- 357F09091DF84F3800941873 /* MGLStyleValueTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MGLStyleValueTests.h; path = ../../darwin/test/MGLStyleValueTests.h; sourceTree = "<group>"; };
357FE2DB1E02D2B20068B753 /* NSCoder+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSCoder+MGLAdditions.h"; path = "../../darwin/src/NSCoder+MGLAdditions.h"; sourceTree = "<group>"; };
357FE2DC1E02D2B20068B753 /* NSCoder+MGLAdditions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "NSCoder+MGLAdditions.mm"; path = "../../darwin/src/NSCoder+MGLAdditions.mm"; sourceTree = "<group>"; };
3598544C1E1D38AA00B29F84 /* MGLDistanceFormatterTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLDistanceFormatterTests.m; path = ../../darwin/test/MGLDistanceFormatterTests.m; sourceTree = "<group>"; };
- 3599A3E51DF708BC00E77FB2 /* MGLStyleValueTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLStyleValueTests.m; path = ../../darwin/test/MGLStyleValueTests.m; sourceTree = "<group>"; };
- 359F57451D2FDBD5005217F1 /* MGLUserLocationAnnotationView_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLUserLocationAnnotationView_Private.h; sourceTree = "<group>"; };
+ 359F57451D2FDBD5005217F1 /* MGLUserLocationAnnotationView_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLUserLocationAnnotationView_Private.h; sourceTree = "<group>"; };
35B82BF61D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSPredicate+MGLAdditions.h"; sourceTree = "<group>"; };
35B82BF71D6C5F8400B1B721 /* NSPredicate+MGLAdditions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSPredicate+MGLAdditions.mm"; sourceTree = "<group>"; };
35B8E08B1D6C8B5100E768D2 /* MGLPredicateTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLPredicateTests.mm; path = ../../darwin/test/MGLPredicateTests.mm; sourceTree = "<group>"; };
@@ -681,15 +767,15 @@
4018B1C31CDC277F00F666AF /* MGLAnnotationView_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAnnotationView_Private.h; sourceTree = "<group>"; };
4018B1C41CDC277F00F666AF /* MGLAnnotationView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLAnnotationView.mm; sourceTree = "<group>"; };
4018B1C51CDC277F00F666AF /* MGLAnnotationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAnnotationView.h; sourceTree = "<group>"; };
- 402E9DE01CD2C76200FD4519 /* Mapbox.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = Mapbox.playground; sourceTree = "<group>"; };
+ 402E9DE01CD2C76200FD4519 /* Mapbox.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = Mapbox.playground; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
4031ACFE1E9FD29F00A3EA26 /* MGLSDKTestHelpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MGLSDKTestHelpers.swift; path = ../../darwin/test/MGLSDKTestHelpers.swift; sourceTree = "<group>"; };
- 404326881D5B9B1A007111BD /* MGLAnnotationContainerView_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLAnnotationContainerView_Private.h; sourceTree = "<group>"; };
+ 404326881D5B9B1A007111BD /* MGLAnnotationContainerView_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAnnotationContainerView_Private.h; sourceTree = "<group>"; };
4049C29B1DB6CD6C00B3F799 /* MGLPointCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLPointCollection.h; sourceTree = "<group>"; };
4049C29C1DB6CD6C00B3F799 /* MGLPointCollection.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLPointCollection.mm; sourceTree = "<group>"; };
4049C2AB1DB6E05500B3F799 /* MGLPointCollection_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLPointCollection_Private.h; sourceTree = "<group>"; };
404C26E01D89B877000AA13D /* MGLTileSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLTileSource.h; sourceTree = "<group>"; };
404C26E11D89B877000AA13D /* MGLTileSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLTileSource.mm; sourceTree = "<group>"; };
- 404C26E61D89C515000AA13D /* MGLTileSource_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLTileSource_Private.h; sourceTree = "<group>"; };
+ 404C26E61D89C515000AA13D /* MGLTileSource_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLTileSource_Private.h; sourceTree = "<group>"; };
40599F001DEE1B2400182B5D /* api_mapbox_staging.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = api_mapbox_staging.der; sourceTree = "<group>"; };
40599F011DEE1B2400182B5D /* api_mapbox_com-digicert_2016.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = "api_mapbox_com-digicert_2016.der"; sourceTree = "<group>"; };
40599F021DEE1B2400182B5D /* api_mapbox_com-geotrust_2016.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = "api_mapbox_com-geotrust_2016.der"; sourceTree = "<group>"; };
@@ -698,13 +784,13 @@
408AA8561DAEDA0800022900 /* NSDictionary+MGLAdditions.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSDictionary+MGLAdditions.mm"; sourceTree = "<group>"; };
409D0A0C1ED614CE00C95D0C /* MGLAnnotationViewIntegrationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MGLAnnotationViewIntegrationTests.swift; sourceTree = "<group>"; };
409F43FC1E9E781C0048729D /* MGLMapViewDelegateIntegrationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MGLMapViewDelegateIntegrationTests.swift; sourceTree = "<group>"; };
- 40CF6DBA1DAC3C1800A4D18B /* MGLShape_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLShape_Private.h; sourceTree = "<group>"; };
+ 40CF6DBA1DAC3C1800A4D18B /* MGLShape_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLShape_Private.h; sourceTree = "<group>"; };
40CFA6501D787579008103BD /* MGLShapeSourceTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLShapeSourceTests.mm; path = ../../darwin/test/MGLShapeSourceTests.mm; sourceTree = "<group>"; };
40EA6BBD1EF4598900FCCDA2 /* api_mapbox_com-digicert_2017.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = "api_mapbox_com-digicert_2017.der"; sourceTree = "<group>"; };
40EA6BBE1EF4598900FCCDA2 /* api_mapbox_com-geotrust_2017.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = "api_mapbox_com-geotrust_2017.der"; sourceTree = "<group>"; };
40EDA1BD1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAnnotationContainerView.h; sourceTree = "<group>"; };
40EDA1BE1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLAnnotationContainerView.m; sourceTree = "<group>"; };
- 40F8876F1D7A1DB8008ECB67 /* MGLShapeSource_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLShapeSource_Private.h; sourceTree = "<group>"; };
+ 40F8876F1D7A1DB8008ECB67 /* MGLShapeSource_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLShapeSource_Private.h; sourceTree = "<group>"; };
40FDA7691CCAAA6800442548 /* MBXAnnotationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MBXAnnotationView.h; sourceTree = "<group>"; };
40FDA76A1CCAAA6800442548 /* MBXAnnotationView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBXAnnotationView.m; sourceTree = "<group>"; };
554180411D2E97DE00012372 /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; };
@@ -723,19 +809,22 @@
632281DD1E6F855900D75A5D /* MBXEmbeddedMapViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MBXEmbeddedMapViewController.h; sourceTree = "<group>"; };
632281DE1E6F855900D75A5D /* MBXEmbeddedMapViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBXEmbeddedMapViewController.m; sourceTree = "<group>"; };
6407D66F1E0085FD00F6A9C3 /* MGLDocumentationExampleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MGLDocumentationExampleTests.swift; path = ../../darwin/test/MGLDocumentationExampleTests.swift; sourceTree = "<group>"; };
- 7E016D7C1D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MGLPolyline+MGLAdditions.h"; sourceTree = "<group>"; };
- 7E016D7D1D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MGLPolyline+MGLAdditions.m"; sourceTree = "<group>"; };
- 7E016D821D9E890300A29A21 /* MGLPolygon+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MGLPolygon+MGLAdditions.h"; sourceTree = "<group>"; };
- 7E016D831D9E890300A29A21 /* MGLPolygon+MGLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MGLPolygon+MGLAdditions.m"; sourceTree = "<group>"; };
+ 8989B17A201A48EA0081CF59 /* MGLHeatmapStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLHeatmapStyleLayer.h; sourceTree = "<group>"; };
+ 8989B17B201A48EA0081CF59 /* MGLHeatmapStyleLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLHeatmapStyleLayer.mm; sourceTree = "<group>"; };
920A3E5C1E6F995200C16EFC /* MGLSourceQueryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLSourceQueryTests.m; path = ../../darwin/test/MGLSourceQueryTests.m; sourceTree = "<group>"; };
927FBCFA1F4DAA8300F8BF1F /* MBXSnapshotsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MBXSnapshotsViewController.h; sourceTree = "<group>"; };
927FBCFB1F4DAA8300F8BF1F /* MBXSnapshotsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBXSnapshotsViewController.m; sourceTree = "<group>"; };
927FBCFD1F4DB05500F8BF1F /* MGLMapSnapshotter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLMapSnapshotter.h; sourceTree = "<group>"; };
927FBCFE1F4DB05500F8BF1F /* MGLMapSnapshotter.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLMapSnapshotter.mm; sourceTree = "<group>"; };
92F2C3EC1F0E3C3A00268EC0 /* MGLRendererFrontend.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLRendererFrontend.h; sourceTree = "<group>"; };
+ 960369FF200565C700510F3D /* NSOrthography+MGLAdditions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSOrthography+MGLAdditions.h"; sourceTree = "<group>"; };
+ 96036A00200565C700510F3D /* NSOrthography+MGLAdditions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSOrthography+MGLAdditions.m"; sourceTree = "<group>"; };
+ 96036A0520059BBA00510F3D /* MGLNSOrthographyAdditionsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLNSOrthographyAdditionsTests.m; sourceTree = "<group>"; };
960D0C351ECF5AAF008E151F /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
9620BB361E69FE1700705A1D /* MGLSDKUpdateChecker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLSDKUpdateChecker.h; sourceTree = "<group>"; };
9620BB371E69FE1700705A1D /* MGLSDKUpdateChecker.mm */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = MGLSDKUpdateChecker.mm; sourceTree = "<group>"; };
+ 9654C1251FFC1AB900DB6A19 /* MGLPolyline_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLPolyline_Private.h; sourceTree = "<group>"; };
+ 9654C1271FFC1CC000DB6A19 /* MGLPolygon_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLPolygon_Private.h; sourceTree = "<group>"; };
9660916B1E5BBFD700A9A03B /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
9660916C1E5BBFD900A9A03B /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = "<group>"; };
9660916D1E5BBFDB00A9A03B /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
@@ -782,7 +871,6 @@
DA1DC99A1CB6E064006E619F /* MBXViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBXViewController.m; sourceTree = "<group>"; };
DA1DC99E1CB6E088006E619F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
DA1F8F3C1EBD287B00367E42 /* MGLDocumentationGuideTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MGLDocumentationGuideTests.swift; path = ../../darwin/test/MGLDocumentationGuideTests.swift; sourceTree = "<group>"; };
- DA2207BE1DC0805F0002F84D /* MGLStyleValueTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MGLStyleValueTests.swift; path = ../../darwin/test/MGLStyleValueTests.swift; sourceTree = "<group>"; };
DA25D5B91CCD9EDE00607828 /* Settings.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Settings.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
DA25D5BF1CCD9F8400607828 /* Root.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Root.plist; sourceTree = "<group>"; };
DA25D5C51CCDA06800607828 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Root.strings; sourceTree = "<group>"; };
@@ -934,8 +1022,6 @@
DA8848831CBB033F00AB86E3 /* Fabric.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Fabric.h; sourceTree = "<group>"; };
DA8848891CBB037E00AB86E3 /* SMCalloutView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SMCalloutView.h; sourceTree = "<group>"; };
DA88488A1CBB037E00AB86E3 /* SMCalloutView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SMCalloutView.m; sourceTree = "<group>"; };
- DA88488D1CBB047F00AB86E3 /* reachability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = reachability.h; path = ../../include/mbgl/platform/darwin/reachability.h; sourceTree = "<group>"; };
- DA88488F1CBB048E00AB86E3 /* reachability.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = reachability.m; path = src/reachability.m; sourceTree = "<group>"; };
DA8933A01CCC951200E68420 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = "<group>"; };
DA8933BB1CCD2CA100E68420 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Foundation.strings; sourceTree = "<group>"; };
DA8933BE1CCD2CAD00E68420 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Foundation.stringsdict; sourceTree = "<group>"; };
@@ -986,7 +1072,14 @@
DABCABBB1CB80692000A7C39 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
DABCABBF1CB80717000A7C39 /* locations.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = locations.cpp; sourceTree = "<group>"; };
DABCABC01CB80717000A7C39 /* locations.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = locations.hpp; sourceTree = "<group>"; };
+ DAC25FCB200FD83E009BE98E /* NSExpression+MGLPrivateAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSExpression+MGLPrivateAdditions.h"; sourceTree = "<group>"; };
DAC49C621CD07D74009E1AA3 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
+ DACA86242019218500E9693A /* MGLRasterDEMSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLRasterDEMSource.h; sourceTree = "<group>"; };
+ DACA86252019218500E9693A /* MGLRasterDEMSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLRasterDEMSource.mm; sourceTree = "<group>"; };
+ DACBC60B20118ABE00C4D7E2 /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/Localizable.strings; sourceTree = "<group>"; };
+ DACBC60C20118AD000C4D7E2 /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/Foundation.strings; sourceTree = "<group>"; };
+ DACBC60D20118ADE00C4D7E2 /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/Localizable.strings; sourceTree = "<group>"; };
+ DACBC60E20118AFE00C4D7E2 /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/Root.strings; sourceTree = "<group>"; };
DACCD9C81F1F473700BB09A1 /* hu */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Root.strings; sourceTree = "<group>"; };
DACFE7981F66EA2100630DA8 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = vi; path = vi.lproj/Foundation.stringsdict; sourceTree = "<group>"; };
DAD165691CF41981001FF4B9 /* MGLFeature.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLFeature.h; sourceTree = "<group>"; };
@@ -1004,6 +1097,9 @@
DAF0D80F1DFE0EA000B28378 /* MGLRasterSource_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLRasterSource_Private.h; sourceTree = "<group>"; };
DAF0D8121DFE0EC500B28378 /* MGLVectorSource_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLVectorSource_Private.h; sourceTree = "<group>"; };
DAF0D8171DFE6B2800B28378 /* MGLAttributionInfo_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAttributionInfo_Private.h; sourceTree = "<group>"; };
+ DAF25717201901E100367EF5 /* MGLHillshadeStyleLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLHillshadeStyleLayer.mm; sourceTree = "<group>"; };
+ DAF25718201901E200367EF5 /* MGLHillshadeStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLHillshadeStyleLayer.h; sourceTree = "<group>"; };
+ DAF2571F201902BB00367EF5 /* MGLHillshadeStyleLayerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLHillshadeStyleLayerTests.mm; path = ../../darwin/test/MGLHillshadeStyleLayerTests.mm; sourceTree = "<group>"; };
DAFBD0D21E3FA7A1000CD6BF /* zh-Hant */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Foundation.strings"; sourceTree = "<group>"; };
DAFBD0D31E3FA7A1000CD6BF /* zh-Hant */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; sourceTree = "<group>"; };
DAFBD0D41E3FA7A2000CD6BF /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Root.strings"; sourceTree = "<group>"; };
@@ -1021,6 +1117,21 @@
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
+ 16376B041FFD9DAF0000563E /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 165D0CE720005419009A3C66 /* Mapbox.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 16376B2C1FFDB4B40000563E /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
DA1DC9471CB6C1C2006E619F /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -1072,14 +1183,43 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
+ 16376B081FFD9DAF0000563E /* Integration Tests */ = {
+ isa = PBXGroup;
+ children = (
+ 16376B091FFD9DAF0000563E /* MBGLIntegrationTests.m */,
+ 16376B0B1FFD9DAF0000563E /* Info.plist */,
+ );
+ path = "Integration Tests";
+ sourceTree = "<group>";
+ };
+ 16376B301FFDB4B40000563E /* Integration Test Harness */ = {
+ isa = PBXGroup;
+ children = (
+ 16376B311FFDB4B40000563E /* AppDelegate.h */,
+ 16376B321FFDB4B40000563E /* AppDelegate.m */,
+ 16376B3A1FFDB4B40000563E /* Assets.xcassets */,
+ 16376B3C1FFDB4B40000563E /* LaunchScreen.storyboard */,
+ 16376B3F1FFDB4B40000563E /* Info.plist */,
+ 16376B401FFDB4B40000563E /* main.m */,
+ );
+ path = "Integration Test Harness";
+ sourceTree = "<group>";
+ };
35136D491D4277EA00C20EFD /* Sources */ = {
isa = PBXGroup;
children = (
+ 07D9474F1F67487E00E37934 /* MGLAbstractShapeSource.h */,
+ 07D9474E1F67487E00E37934 /* MGLAbstractShapeSource_Private.h */,
+ 07D947501F67487E00E37934 /* MGLAbstractShapeSource.mm */,
+ 0778DD401F67555F00A73B34 /* MGLComputedShapeSource.h */,
+ 0778DD411F67555F00A73B34 /* MGLComputedShapeSource.mm */,
071BBAFC1EE75CD4001FB02A /* MGLImageSource.h */,
071BBAFD1EE75CD4001FB02A /* MGLImageSource.mm */,
3566C76A1D4A8DFA008152BC /* MGLRasterSource.h */,
DAF0D80F1DFE0EA000B28378 /* MGLRasterSource_Private.h */,
3566C76B1D4A8DFA008152BC /* MGLRasterSource.mm */,
+ DACA86242019218500E9693A /* MGLRasterDEMSource.h */,
+ DACA86252019218500E9693A /* MGLRasterDEMSource.mm */,
3566C7641D4A77BA008152BC /* MGLShapeSource.h */,
40F8876F1D7A1DB8008ECB67 /* MGLShapeSource_Private.h */,
3566C7651D4A77BA008152BC /* MGLShapeSource.mm */,
@@ -1106,9 +1246,13 @@
FA68F1481E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.h */,
FA68F1491E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.mm */,
35D13AC11D3D19DD00AFB4E0 /* MGLFillStyleLayer.h */,
+ 8989B17A201A48EA0081CF59 /* MGLHeatmapStyleLayer.h */,
+ 8989B17B201A48EA0081CF59 /* MGLHeatmapStyleLayer.mm */,
35D13AC21D3D19DD00AFB4E0 /* MGLFillStyleLayer.mm */,
3538AA1B1D542239008EC33D /* MGLForegroundStyleLayer.h */,
3538AA1C1D542239008EC33D /* MGLForegroundStyleLayer.mm */,
+ DAF25718201901E200367EF5 /* MGLHillshadeStyleLayer.h */,
+ DAF25717201901E100367EF5 /* MGLHillshadeStyleLayer.mm */,
353933F71D3FB79F003F57D7 /* MGLLineStyleLayer.h */,
35136D3E1D42273000C20EFD /* MGLLineStyleLayer.mm */,
DA7262091DEEE3480043BB89 /* MGLOpenGLStyleLayer.h */,
@@ -1146,8 +1290,6 @@
35599DB81D46AD7F0048254D /* Categories */ = {
isa = PBXGroup;
children = (
- 1FDD9D6D1F26936400252B09 /* MGLVectorSource+MGLAdditions.h */,
- 1FDD9D6E1F26936400252B09 /* MGLVectorSource+MGLAdditions.m */,
350098DA1D484E60004B2AF0 /* NSValue+MGLStyleAttributeAdditions.h */,
350098DB1D484E60004B2AF0 /* NSValue+MGLStyleAttributeAdditions.mm */,
);
@@ -1169,9 +1311,6 @@
3575798F1D513EF1000B822E /* Layers */,
40CFA64E1D78754A008103BD /* Sources */,
1F7454A61ED08AB400021D39 /* MGLLightTest.mm */,
- 357F09091DF84F3800941873 /* MGLStyleValueTests.h */,
- 3599A3E51DF708BC00E77FB2 /* MGLStyleValueTests.m */,
- DA2207BE1DC0805F0002F84D /* MGLStyleValueTests.swift */,
);
name = Styling;
sourceTree = "<group>";
@@ -1179,11 +1318,14 @@
3575798F1D513EF1000B822E /* Layers */ = {
isa = PBXGroup;
children = (
+ 170C43782028D49800863DF0 /* MGLHeatmapColorTests.mm */,
+ 170C43792028D49800863DF0 /* MGLHeatmapStyleLayerTests.mm */,
DA2DBBCC1D51E80400D38FF9 /* MGLStyleLayerTests.h */,
DA2DBBCD1D51E80400D38FF9 /* MGLStyleLayerTests.m */,
DA3C6FF21E2859E700F962BE /* test-Bridging-Header.h */,
FAE1CDC81E9D79C600C40B5B /* MGLFillExtrusionStyleLayerTests.mm */,
3575797F1D501E09000B822E /* MGLFillStyleLayerTests.mm */,
+ DAF2571F201902BB00367EF5 /* MGLHillshadeStyleLayerTests.mm */,
357579821D502AE6000B822E /* MGLRasterStyleLayerTests.mm */,
357579841D502AF5000B822E /* MGLSymbolStyleLayerTests.mm */,
357579861D502AFE000B822E /* MGLLineStyleLayerTests.mm */,
@@ -1198,6 +1340,8 @@
children = (
357FE2DB1E02D2B20068B753 /* NSCoder+MGLAdditions.h */,
357FE2DC1E02D2B20068B753 /* NSCoder+MGLAdditions.mm */,
+ 960369FF200565C700510F3D /* NSOrthography+MGLAdditions.h */,
+ 96036A00200565C700510F3D /* NSOrthography+MGLAdditions.m */,
35CE61801D4165D9004F2359 /* UIColor+MGLAdditions.h */,
35CE61811D4165D9004F2359 /* UIColor+MGLAdditions.mm */,
30E578111DAA7D690050F07E /* UIImage+MGLAdditions.h */,
@@ -1236,6 +1380,7 @@
40CFA64E1D78754A008103BD /* Sources */ = {
isa = PBXGroup;
children = (
+ 07D8C6FD1F67562800381808 /* MGLComputedShapeSourceTests.m */,
071BBB051EE7761A001FB02A /* MGLImageSourceTests.m */,
40CFA6501D787579008103BD /* MGLShapeSourceTests.mm */,
920A3E5C1E6F995200C16EFC /* MGLSourceQueryTests.m */,
@@ -1266,6 +1411,8 @@
DABCABA91CB80692000A7C39 /* Benchmarking App */,
DA8847D31CBAF91600AB86E3 /* SDK */,
DA2E88521CC036F400F24E7B /* SDK Tests */,
+ 16376B301FFDB4B40000563E /* Integration Test Harness */,
+ 16376B081FFD9DAF0000563E /* Integration Tests */,
DA1DC9921CB6DF24006E619F /* Frameworks */,
DAC07C951CBB2CAD000CB309 /* Configuration */,
DA1DC94B1CB6C1C2006E619F /* Products */,
@@ -1282,6 +1429,8 @@
DA2E88511CC036F400F24E7B /* test.xctest */,
DA8933D51CCD306400E68420 /* Mapbox.bundle */,
DA25D5B91CCD9EDE00607828 /* Settings.bundle */,
+ 16376B071FFD9DAF0000563E /* integration.xctest */,
+ 16376B2F1FFDB4B40000563E /* Integration Test Harness.app */,
);
name = Products;
sourceTree = "<group>";
@@ -1380,6 +1529,7 @@
35E208A61D24210F00EC9A46 /* MGLNSDataAdditionsTests.m */,
1F95931C1E6DE2E900D5B294 /* MGLNSDateAdditionsTests.mm */,
DAE7DEC11E245455007505A6 /* MGLNSStringAdditionsTests.m */,
+ 96036A0520059BBA00510F3D /* MGLNSOrthographyAdditionsTests.m */,
DA2E885D1CC0382C00F24E7B /* MGLOfflinePackTests.m */,
DA2E885E1CC0382C00F24E7B /* MGLOfflineRegionTests.m */,
55E2AD121E5B125400E8C587 /* MGLOfflineStorageTests.mm */,
@@ -1419,7 +1569,6 @@
DAD165801CF4CF9A001FF4B9 /* Formatters */,
DAD165811CF4CFC4001FF4B9 /* Geometry */,
DAD165821CF4CFE3001FF4B9 /* Offline Maps */,
- DA8848911CBB049300AB86E3 /* reachability */,
DA8847DF1CBAFA5100AB86E3 /* MGLAccountManager.h */,
DA8847FF1CBAFA6200AB86E3 /* MGLAccountManager_Private.h */,
DA8848001CBAFA6200AB86E3 /* MGLAccountManager.m */,
@@ -1510,16 +1659,6 @@
path = ../vendor/SMCalloutView;
sourceTree = "<group>";
};
- DA8848911CBB049300AB86E3 /* reachability */ = {
- isa = PBXGroup;
- children = (
- DA88488D1CBB047F00AB86E3 /* reachability.h */,
- DA88488F1CBB048E00AB86E3 /* reachability.m */,
- );
- name = reachability;
- path = ..;
- sourceTree = "<group>";
- };
DA8933B91CCD2C6700E68420 /* Foundation Resources */ = {
isa = PBXGroup;
children = (
@@ -1624,8 +1763,10 @@
4049C2AB1DB6E05500B3F799 /* MGLPointCollection_Private.h */,
4049C29C1DB6CD6C00B3F799 /* MGLPointCollection.mm */,
DA8847E91CBAFA5100AB86E3 /* MGLPolygon.h */,
+ 9654C1271FFC1CC000DB6A19 /* MGLPolygon_Private.h */,
DA88480C1CBAFA6200AB86E3 /* MGLPolygon.mm */,
DA8847EA1CBAFA5100AB86E3 /* MGLPolyline.h */,
+ 9654C1251FFC1AB900DB6A19 /* MGLPolyline_Private.h */,
DA88480D1CBAFA6200AB86E3 /* MGLPolyline.mm */,
DA8847EB1CBAFA5100AB86E3 /* MGLShape.h */,
40CF6DBA1DAC3C1800A4D18B /* MGLShape_Private.h */,
@@ -1656,10 +1797,6 @@
DAD165831CF4CFED001FF4B9 /* Categories */ = {
isa = PBXGroup;
children = (
- 7E016D821D9E890300A29A21 /* MGLPolygon+MGLAdditions.h */,
- 7E016D831D9E890300A29A21 /* MGLPolygon+MGLAdditions.m */,
- 7E016D7C1D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.h */,
- 7E016D7D1D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.m */,
400532FF1DB0862B0069F638 /* NSArray+MGLAdditions.h */,
400533001DB0862B0069F638 /* NSArray+MGLAdditions.mm */,
DA8848121CBAFA6200AB86E3 /* NSBundle+MGLAdditions.h */,
@@ -1676,6 +1813,7 @@
408AA8561DAEDA0800022900 /* NSDictionary+MGLAdditions.mm */,
DA8848141CBAFA6200AB86E3 /* NSException+MGLAdditions.h */,
3510FFEE1D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h */,
+ DAC25FCB200FD83E009BE98E /* NSExpression+MGLPrivateAdditions.h */,
3510FFEF1D6D9D8C00F413B2 /* NSExpression+MGLAdditions.mm */,
35B82BF61D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h */,
35B82BF71D6C5F8400B1B721 /* NSPredicate+MGLAdditions.mm */,
@@ -1750,6 +1888,7 @@
556660DB1E1D8E8D00E2C41B /* MGLFoundation.h in Headers */,
35D13AC31D3D19DD00AFB4E0 /* MGLFillStyleLayer.h in Headers */,
DA88483A1CBAFB8500AB86E3 /* MGLAnnotationImage.h in Headers */,
+ DAF2571B201901E200367EF5 /* MGLHillshadeStyleLayer.h in Headers */,
DA35A2BB1CCA9A6900E826B2 /* MGLClockDirectionFormatter.h in Headers */,
353933FE1D3FB7DD003F57D7 /* MGLSymbolStyleLayer.h in Headers */,
DA8848861CBB033F00AB86E3 /* Fabric+FABKits.h in Headers */,
@@ -1761,9 +1900,9 @@
35E1A4D81D74336F007AA97F /* MGLValueEvaluator.h in Headers */,
DA88482C1CBAFA6200AB86E3 /* NSBundle+MGLAdditions.h in Headers */,
357FE2DD1E02D2B20068B753 /* NSCoder+MGLAdditions.h in Headers */,
- 7E016D7E1D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.h in Headers */,
35D13AB71D3D15E300AFB4E0 /* MGLStyleLayer.h in Headers */,
- DA88488E1CBB047F00AB86E3 /* reachability.h in Headers */,
+ 07D947531F67488E00E37934 /* MGLAbstractShapeSource_Private.h in Headers */,
+ 9654C1261FFC1AB900DB6A19 /* MGLPolyline_Private.h in Headers */,
40F887701D7A1E58008ECB67 /* MGLShapeSource_Private.h in Headers */,
350098DC1D484E60004B2AF0 /* NSValue+MGLStyleAttributeAdditions.h in Headers */,
DA8848231CBAFA6200AB86E3 /* MGLOfflineStorage_Private.h in Headers */,
@@ -1780,17 +1919,18 @@
DA88485A1CBAFB9800AB86E3 /* MGLUserLocation_Private.h in Headers */,
966FCF531F3C322400F2B6DE /* MGLUserLocationHeadingArrowLayer.h in Headers */,
DA27C24F1CBB4C11000B0ECD /* MGLAccountManager_Private.h in Headers */,
+ 07D947521F67488800E37934 /* MGLAbstractShapeSource.h in Headers */,
DA8847FC1CBAFA5100AB86E3 /* MGLStyle.h in Headers */,
DD9BE4F71EB263C50079A3AF /* UIViewController+MGLAdditions.h in Headers */,
DAF0D8131DFE0EC500B28378 /* MGLVectorSource_Private.h in Headers */,
354B83961D2E873E005D9406 /* MGLUserLocationAnnotationView.h in Headers */,
DA8847F01CBAFA5100AB86E3 /* MGLAnnotation.h in Headers */,
- 7E016D841D9E890300A29A21 /* MGLPolygon+MGLAdditions.h in Headers */,
400533011DB0862B0069F638 /* NSArray+MGLAdditions.h in Headers */,
1F06668A1EC64F8E001C16D7 /* MGLLight.h in Headers */,
4049C29D1DB6CD6C00B3F799 /* MGLPointCollection.h in Headers */,
40CF6DBB1DAC3C6600A4D18B /* MGLShape_Private.h in Headers */,
4018B1CA1CDC288E00F666AF /* MGLAnnotationView.h in Headers */,
+ 9654C1291FFC1CCD00DB6A19 /* MGLPolygon_Private.h in Headers */,
35E79F201D41266300957B9E /* MGLStyleLayer_Private.h in Headers */,
FA68F14A1E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.h in Headers */,
353933FB1D3FB7C0003F57D7 /* MGLRasterStyleLayer.h in Headers */,
@@ -1803,7 +1943,9 @@
DD0902AB1DB192A800C5BDCE /* MGLNetworkConfiguration.h in Headers */,
DA8848571CBAFB9800AB86E3 /* MGLMapboxEvents.h in Headers */,
35D3A1E61E9BE7EB002B38EE /* MGLScaleBar.h in Headers */,
+ 0778DD431F67556700A73B34 /* MGLComputedShapeSource.h in Headers */,
DA8848311CBAFA6200AB86E3 /* NSString+MGLAdditions.h in Headers */,
+ DACA86262019218600E9693A /* MGLRasterDEMSource.h in Headers */,
353933F81D3FB79F003F57D7 /* MGLLineStyleLayer.h in Headers */,
92F2C3ED1F0E3C3A00268EC0 /* MGLRendererFrontend.h in Headers */,
DAAF722D1DA903C700312FA4 /* MGLStyleValue_Private.h in Headers */,
@@ -1839,9 +1981,11 @@
DA8847F91CBAFA5100AB86E3 /* MGLPolygon.h in Headers */,
4049C2AC1DB6E05500B3F799 /* MGLPointCollection_Private.h in Headers */,
DA8847F81CBAFA5100AB86E3 /* MGLPointAnnotation.h in Headers */,
+ 8989B17C201A48EB0081CF59 /* MGLHeatmapStyleLayer.h in Headers */,
353933F21D3FB753003F57D7 /* MGLCircleStyleLayer.h in Headers */,
DA8847F31CBAFA5100AB86E3 /* MGLMultiPoint.h in Headers */,
30E578171DAA85520050F07E /* UIImage+MGLAdditions.h in Headers */,
+ 96036A01200565C700510F3D /* NSOrthography+MGLAdditions.h in Headers */,
1F7454961ECD450D00021D39 /* MGLLight_Private.h in Headers */,
DAD1656C1CF41981001FF4B9 /* MGLFeature.h in Headers */,
40EDA1C01CFE0E0200D9EA68 /* MGLAnnotationContainerView.h in Headers */,
@@ -1857,6 +2001,7 @@
DA88481B1CBAFA6200AB86E3 /* MGLGeometry_Private.h in Headers */,
3510FFF91D6DCC4700F413B2 /* NSCompoundPredicate+MGLAdditions.h in Headers */,
3557F7B01E1D27D300CCA5E6 /* MGLDistanceFormatter.h in Headers */,
+ DAC25FCC200FD83F009BE98E /* NSExpression+MGLPrivateAdditions.h in Headers */,
DA72620B1DEEE3480043BB89 /* MGLOpenGLStyleLayer.h in Headers */,
404C26E71D89C55D000AA13D /* MGLTileSource_Private.h in Headers */,
DA88485C1CBAFB9800AB86E3 /* MGLFaux3DUserLocationAnnotationView.h in Headers */,
@@ -1881,25 +2026,35 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
+ 96E516FE20005A4C00A02306 /* MGLLocationManager.h in Headers */,
556660CA1E1BF3A900E2C41B /* MGLFoundation.h in Headers */,
+ 96E516ED200058A200A02306 /* MGLComputedShapeSource.h in Headers */,
35B82BF91D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h in Headers */,
+ 96E5170020005A6100A02306 /* Fabric.h in Headers */,
DA35A2CA1CCAAAD200E826B2 /* NSValue+MGLAdditions.h in Headers */,
350098BC1D480108004B2AF0 /* MGLVectorSource.h in Headers */,
FA68F14B1E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.h in Headers */,
+ 96E516DE200054F700A02306 /* MGLGeometry_Private.h in Headers */,
353933FC1D3FB7C0003F57D7 /* MGLRasterStyleLayer.h in Headers */,
3566C76D1D4A8DFA008152BC /* MGLRasterSource.h in Headers */,
DAED38641D62D0FC00D7640F /* NSURL+MGLAdditions.h in Headers */,
DABFB85E1CBE99E500D62B32 /* MGLAnnotation.h in Headers */,
DABFB8641CBE99E500D62B32 /* MGLOfflineStorage.h in Headers */,
+ 96E516E32000552A00A02306 /* MGLAccountManager_Private.h in Headers */,
+ 96E5170420005A6B00A02306 /* SMCalloutView.h in Headers */,
+ 96036A02200565C700510F3D /* NSOrthography+MGLAdditions.h in Headers */,
DAD165791CF4CDFF001FF4B9 /* MGLShapeCollection.h in Headers */,
4049C29E1DB6CD6C00B3F799 /* MGLPointCollection.h in Headers */,
3566C7671D4A77BA008152BC /* MGLShapeSource.h in Headers */,
DA35A29F1CC9E94C00E826B2 /* MGLCoordinateFormatter.h in Headers */,
404C26E31D89B877000AA13D /* MGLTileSource.h in Headers */,
+ 96E516F6200059EC00A02306 /* MGLRendererFrontend.h in Headers */,
071BBB041EE76147001FB02A /* MGLImageSource.h in Headers */,
DABFB8611CBE99E500D62B32 /* MGLMultiPoint.h in Headers */,
3510FFF11D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h in Headers */,
35D3A1E71E9BE7EC002B38EE /* MGLScaleBar.h in Headers */,
+ 96E516EF2000594F00A02306 /* NSArray+MGLAdditions.h in Headers */,
+ 96E516F12000596800A02306 /* NSString+MGLAdditions.h in Headers */,
35E0CFE71D3E501500188327 /* MGLStyle_Private.h in Headers */,
CA55CD42202C16AA00CE7095 /* MGLCameraChangeReason.h in Headers */,
DABFB86D1CBE9A0F00D62B32 /* MGLAnnotationImage.h in Headers */,
@@ -1909,22 +2064,39 @@
353933FF1D3FB7DD003F57D7 /* MGLSymbolStyleLayer.h in Headers */,
DAAF722E1DA903C700312FA4 /* MGLStyleValue_Private.h in Headers */,
DABFB8661CBE99E500D62B32 /* MGLPointAnnotation.h in Headers */,
+ 96E5170220005A6600A02306 /* FABAttributes.h in Headers */,
+ 96E516E42000560B00A02306 /* MGLAbstractShapeSource_Private.h in Headers */,
+ 96E516E92000560B00A02306 /* MGLAnnotationImage_Private.h in Headers */,
+ 96E516E52000560B00A02306 /* MGLOfflinePack_Private.h in Headers */,
DD9BE4F91EB263D20079A3AF /* UIViewController+MGLAdditions.h in Headers */,
+ DAF2571C201901E200367EF5 /* MGLHillshadeStyleLayer.h in Headers */,
DABFB8621CBE99E500D62B32 /* MGLOfflinePack.h in Headers */,
+ 96E516FA20005A3D00A02306 /* MGLUserLocationHeadingArrowLayer.h in Headers */,
+ 96E516E62000560B00A02306 /* MGLOfflineRegion_Private.h in Headers */,
DAD1656D1CF41981001FF4B9 /* MGLFeature.h in Headers */,
+ 96E516E72000560B00A02306 /* MGLOfflineStorage_Private.h in Headers */,
DA17BE311CC4BDAA00402C41 /* MGLMapView_Private.h in Headers */,
DABFB86C1CBE99E500D62B32 /* MGLTypes.h in Headers */,
+ 96E516F720005A2700A02306 /* MGLAnnotationContainerView.h in Headers */,
DABFB8691CBE99E500D62B32 /* MGLShape.h in Headers */,
9620BB391E69FE1700705A1D /* MGLSDKUpdateChecker.h in Headers */,
3510FFEB1D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.h in Headers */,
35E1A4D91D74336F007AA97F /* MGLValueEvaluator.h in Headers */,
- 7E016D7F1D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.h in Headers */,
DABFB8701CBE9A0F00D62B32 /* MGLMapView+IBAdditions.h in Headers */,
+ 96E516EA2000560B00A02306 /* MGLAnnotationView_Private.h in Headers */,
+ 96E516FB20005A4000A02306 /* MGLUserLocationHeadingBeamLayer.h in Headers */,
+ 96E516DC2000547000A02306 /* MGLPolyline_Private.h in Headers */,
353AFA151D65AB17005A69F4 /* NSDate+MGLAdditions.h in Headers */,
AC518E00201BB55A00EBC820 /* MGLTelemetryConfig.h in Headers */,
3510FFFA1D6DCC4700F413B2 /* NSCompoundPredicate+MGLAdditions.h in Headers */,
DA72620C1DEEE3480043BB89 /* MGLOpenGLStyleLayer.h in Headers */,
35CE61831D4165D9004F2359 /* UIColor+MGLAdditions.h in Headers */,
+ 96E516EE2000590900A02306 /* MGLAbstractShapeSource.h in Headers */,
+ 96E516F32000597100A02306 /* NSDictionary+MGLAdditions.h in Headers */,
+ 96E516F02000595800A02306 /* NSBundle+MGLAdditions.h in Headers */,
+ 96E516F920005A3500A02306 /* MGLFaux3DUserLocationAnnotationView.h in Headers */,
+ 96E516F22000596D00A02306 /* NSException+MGLAdditions.h in Headers */,
+ 96E516EC2000560B00A02306 /* MGLUserLocationAnnotationView_Private.h in Headers */,
DABFB8671CBE99E500D62B32 /* MGLPolygon.h in Headers */,
404C26E81D89C55D000AA13D /* MGLTileSource_Private.h in Headers */,
1F7454931ECBB43F00021D39 /* MGLLight.h in Headers */,
@@ -1933,10 +2105,13 @@
35E79F211D41266300957B9E /* MGLStyleLayer_Private.h in Headers */,
350098DD1D484E60004B2AF0 /* NSValue+MGLStyleAttributeAdditions.h in Headers */,
DABFB8681CBE99E500D62B32 /* MGLPolyline.h in Headers */,
+ 96E516DF200054FB00A02306 /* MGLShape_Private.h in Headers */,
DABFB86F1CBE9A0F00D62B32 /* MGLMapView.h in Headers */,
DA6408DC1DA4E7D300908C90 /* MGLVectorStyleLayer.h in Headers */,
353933F31D3FB753003F57D7 /* MGLCircleStyleLayer.h in Headers */,
558DE7A11E5615E400C7916D /* MGLFoundation_Private.h in Headers */,
+ 96E516F820005A3000A02306 /* MGLCompactCalloutView.h in Headers */,
+ 96E516E22000551900A02306 /* MGLPointCollection_Private.h in Headers */,
3538AA1E1D542239008EC33D /* MGLForegroundStyleLayer.h in Headers */,
30E578181DAA85520050F07E /* UIImage+MGLAdditions.h in Headers */,
DA704CC31F65A475004B3F28 /* MGLMapAccessibilityElement.h in Headers */,
@@ -1944,38 +2119,86 @@
DABFB8631CBE99E500D62B32 /* MGLOfflineRegion.h in Headers */,
DA35A2B21CCA141D00E826B2 /* MGLCompassDirectionFormatter.h in Headers */,
DAF0D8141DFE0EC500B28378 /* MGLVectorSource_Private.h in Headers */,
+ 8989B17D201A48EB0081CF59 /* MGLHeatmapStyleLayer.h in Headers */,
DABFB8731CBE9A9900D62B32 /* Mapbox.h in Headers */,
357FE2DE1E02D2B20068B753 /* NSCoder+MGLAdditions.h in Headers */,
1753ED431E53CE6F00A9FD90 /* MGLConversion.h in Headers */,
+ DAC25FCD200FD83F009BE98E /* NSExpression+MGLPrivateAdditions.h in Headers */,
354B83971D2E873E005D9406 /* MGLUserLocationAnnotationView.h in Headers */,
DAF0D8111DFE0EA000B28378 /* MGLRasterSource_Private.h in Headers */,
+ 96E516FF20005A4F00A02306 /* MGLMapboxEvents.h in Headers */,
DABFB86B1CBE99E500D62B32 /* MGLTilePyramidOfflineRegion.h in Headers */,
968F36B51E4D128D003A5522 /* MGLDistanceFormatter.h in Headers */,
4018B1CB1CDC288E00F666AF /* MGLAnnotationView.h in Headers */,
DABFB85F1CBE99E500D62B32 /* MGLGeometry.h in Headers */,
- 7E016D851D9E890300A29A21 /* MGLPolygon+MGLAdditions.h in Headers */,
+ 96E516E02000550C00A02306 /* MGLFeature_Private.h in Headers */,
353933F61D3FB785003F57D7 /* MGLBackgroundStyleLayer.h in Headers */,
DABFB85D1CBE99E500D62B32 /* MGLAccountManager.h in Headers */,
+ 96E516F5200059B100A02306 /* MGLNetworkConfiguration.h in Headers */,
+ 96E516F42000597D00A02306 /* NSData+MGLAdditions.h in Headers */,
+ 96E516DD200054F200A02306 /* MGLPolygon_Private.h in Headers */,
353933F91D3FB79F003F57D7 /* MGLLineStyleLayer.h in Headers */,
+ 96E516EB2000560B00A02306 /* MGLUserLocation_Private.h in Headers */,
35D13AB81D3D15E300AFB4E0 /* MGLStyleLayer.h in Headers */,
35136D4D1D4277FC00C20EFD /* MGLSource.h in Headers */,
DA35A2BC1CCA9A6900E826B2 /* MGLClockDirectionFormatter.h in Headers */,
+ 96E516E82000560B00A02306 /* MGLAnnotationContainerView_Private.h in Headers */,
35D13AC41D3D19DD00AFB4E0 /* MGLFillStyleLayer.h in Headers */,
- 1FB7DAB01F2A4DC200410606 /* MGLVectorSource+MGLAdditions.h in Headers */,
+ 96E5170120005A6400A02306 /* Fabric+FABKits.h in Headers */,
DABFB86E1CBE9A0F00D62B32 /* MGLCalloutView.h in Headers */,
+ 96E516FC20005A4400A02306 /* MGLUserLocationHeadingIndicator.h in Headers */,
1F7454971ECD450D00021D39 /* MGLLight_Private.h in Headers */,
DABFB8601CBE99E500D62B32 /* MGLMapCamera.h in Headers */,
DA737EE21D056A4E005BDA16 /* MGLMapViewDelegate.h in Headers */,
DAF0D8191DFE6B2800B28378 /* MGLAttributionInfo_Private.h in Headers */,
DABFB86A1CBE99E500D62B32 /* MGLStyle.h in Headers */,
DA00FC8F1D5EEB0D009AABC8 /* MGLAttributionInfo.h in Headers */,
+ 96E516FD20005A4700A02306 /* MGLAPIClient.h in Headers */,
+ 96E5170320005A6800A02306 /* FABKitProtocol.h in Headers */,
+ 96E516E12000551100A02306 /* MGLMultiPoint_Private.h in Headers */,
3EA934623AD0000B7D99C3FB /* MGLRendererConfiguration.h in Headers */,
+ DACA86272019218600E9693A /* MGLRasterDEMSource.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
+ 16376B061FFD9DAF0000563E /* integration */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 16376B101FFD9DAF0000563E /* Build configuration list for PBXNativeTarget "integration" */;
+ buildPhases = (
+ 16376B031FFD9DAF0000563E /* Sources */,
+ 16376B041FFD9DAF0000563E /* Frameworks */,
+ 16376B051FFD9DAF0000563E /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 165D0CE620005351009A3C66 /* PBXTargetDependency */,
+ );
+ name = integration;
+ productName = "integration-tests";
+ productReference = 16376B071FFD9DAF0000563E /* integration.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
+ 16376B2E1FFDB4B40000563E /* Integration Test Harness */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 16376B421FFDB4B40000563E /* Build configuration list for PBXNativeTarget "Integration Test Harness" */;
+ buildPhases = (
+ 16376B2B1FFDB4B40000563E /* Sources */,
+ 16376B2C1FFDB4B40000563E /* Frameworks */,
+ 16376B2D1FFDB4B40000563E /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = "Integration Test Harness";
+ productName = "Integration Test Harness";
+ productReference = 16376B2F1FFDB4B40000563E /* Integration Test Harness.app */;
+ productType = "com.apple.product-type.application";
+ };
DA1DC9491CB6C1C2006E619F /* iosapp */ = {
isa = PBXNativeTarget;
buildConfigurationList = DA1DC9611CB6C1C2006E619F /* Build configuration list for PBXNativeTarget "iosapp" */;
@@ -2108,9 +2331,18 @@
isa = PBXProject;
attributes = {
CLASSPREFIX = MBX;
- LastUpgradeCheck = 0800;
+ LastUpgradeCheck = 0910;
ORGANIZATIONNAME = Mapbox;
TargetAttributes = {
+ 16376B061FFD9DAF0000563E = {
+ CreatedOnToolsVersion = 9.2;
+ ProvisioningStyle = Automatic;
+ TestTargetID = DA1DC9491CB6C1C2006E619F;
+ };
+ 16376B2E1FFDB4B40000563E = {
+ CreatedOnToolsVersion = 9.2;
+ ProvisioningStyle = Automatic;
+ };
DA1DC9491CB6C1C2006E619F = {
CreatedOnToolsVersion = 7.3;
LastSwiftMigration = 0820;
@@ -2163,6 +2395,7 @@
hu,
bg,
ar,
+ he,
);
mainGroup = DA1DC9411CB6C1C2006E619F;
productRefGroup = DA1DC94B1CB6C1C2006E619F /* Products */;
@@ -2176,11 +2409,30 @@
DA8933D41CCD306400E68420 /* bundle */,
DA25D5B81CCD9EDE00607828 /* settings */,
DA2E88501CC036F400F24E7B /* test */,
+ 16376B061FFD9DAF0000563E /* integration */,
+ 16376B2E1FFDB4B40000563E /* Integration Test Harness */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
+ 16376B051FFD9DAF0000563E /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 16376B471FFDB92B0000563E /* one-liner.json in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 16376B2D1FFDB4B40000563E /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 16376B3E1FFDB4B40000563E /* LaunchScreen.storyboard in Resources */,
+ 16376B3B1FFDB4B40000563E /* Assets.xcassets in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
DA1DC9481CB6C1C2006E619F /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@@ -2270,6 +2522,23 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
+ 16376B031FFD9DAF0000563E /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 16376B0A1FFD9DAF0000563E /* MBGLIntegrationTests.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 16376B2B1FFDB4B40000563E /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 16376B411FFDB4B40000563E /* main.m in Sources */,
+ 16376B331FFDB4B40000563E /* AppDelegate.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
DA1DC9461CB6C1C2006E619F /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -2292,10 +2561,11 @@
files = (
6407D6701E0085FD00F6A9C3 /* MGLDocumentationExampleTests.swift in Sources */,
DA2E88631CC0382C00F24E7B /* MGLOfflineRegionTests.m in Sources */,
- 3599A3E61DF708BC00E77FB2 /* MGLStyleValueTests.m in Sources */,
409F43FD1E9E781C0048729D /* MGLMapViewDelegateIntegrationTests.swift in Sources */,
DA2E88651CC0382C00F24E7B /* MGLStyleTests.mm in Sources */,
DA2E88611CC0382C00F24E7B /* MGLGeometryTests.mm in Sources */,
+ 170C437D2029D97900863DF0 /* MGLHeatmapStyleLayerTests.mm in Sources */,
+ 170C437C2029D96F00863DF0 /* MGLHeatmapColorTests.mm in Sources */,
357579801D501E09000B822E /* MGLFillStyleLayerTests.mm in Sources */,
35D9DDE21DA25EEC00DAAD69 /* MGLCodingTests.m in Sources */,
DA1F8F3D1EBD287B00367E42 /* MGLDocumentationGuideTests.swift in Sources */,
@@ -2310,21 +2580,23 @@
357579851D502AF5000B822E /* MGLSymbolStyleLayerTests.mm in Sources */,
357579871D502AFE000B822E /* MGLLineStyleLayerTests.mm in Sources */,
357579891D502B06000B822E /* MGLCircleStyleLayerTests.mm in Sources */,
- DA2207BF1DC0805F0002F84D /* MGLStyleValueTests.swift in Sources */,
40CFA6511D7875BB008103BD /* MGLShapeSourceTests.mm in Sources */,
DA35A2C51CCA9F8300E826B2 /* MGLClockDirectionFormatterTests.m in Sources */,
35B8E08C1D6C8B5100E768D2 /* MGLPredicateTests.mm in Sources */,
+ 96036A0620059BBA00510F3D /* MGLNSOrthographyAdditionsTests.m in Sources */,
1F95931D1E6DE2E900D5B294 /* MGLNSDateAdditionsTests.mm in Sources */,
DD58A4C61D822BD000E1F038 /* MGLExpressionTests.mm in Sources */,
3575798B1D502B0C000B822E /* MGLBackgroundStyleLayerTests.mm in Sources */,
409D0A0D1ED614CE00C95D0C /* MGLAnnotationViewIntegrationTests.swift in Sources */,
DA2E88621CC0382C00F24E7B /* MGLOfflinePackTests.m in Sources */,
55E2AD131E5B125400E8C587 /* MGLOfflineStorageTests.mm in Sources */,
+ 07D8C6FF1F67562C00381808 /* MGLComputedShapeSourceTests.m in Sources */,
920A3E5D1E6F995200C16EFC /* MGLSourceQueryTests.m in Sources */,
DA5DB12A1FABF1EE001C2326 /* MGLMapAccessibilityElementTests.m in Sources */,
FAE1CDCB1E9D79CB00C40B5B /* MGLFillExtrusionStyleLayerTests.mm in Sources */,
DA35A2AA1CCA058D00E826B2 /* MGLCoordinateFormatterTests.m in Sources */,
357579831D502AE6000B822E /* MGLRasterStyleLayerTests.mm in Sources */,
+ DAF25720201902BC00367EF5 /* MGLHillshadeStyleLayerTests.mm in Sources */,
353D23961D0B0DFE002BE09D /* MGLAnnotationViewTests.m in Sources */,
35E208A71D24210F00EC9A46 /* MGLNSDataAdditionsTests.m in Sources */,
DA0CD5901CF56F6A00A5F5A5 /* MGLFeatureTests.mm in Sources */,
@@ -2340,12 +2612,10 @@
files = (
35136D391D42271A00C20EFD /* MGLBackgroundStyleLayer.mm in Sources */,
3510FFEC1D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.mm in Sources */,
- 7E016D801D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.m in Sources */,
DAED38651D62D0FC00D7640F /* NSURL+MGLAdditions.m in Sources */,
9620BB3A1E69FE1700705A1D /* MGLSDKUpdateChecker.mm in Sources */,
354B83981D2E873E005D9406 /* MGLUserLocationAnnotationView.m in Sources */,
DA88485D1CBAFB9800AB86E3 /* MGLFaux3DUserLocationAnnotationView.m in Sources */,
- 1FB7DAB11F2A4DC800410606 /* MGLVectorSource+MGLAdditions.m in Sources */,
DAD165701CF41981001FF4B9 /* MGLFeature.mm in Sources */,
30E578191DAA855E0050F07E /* UIImage+MGLAdditions.mm in Sources */,
40EDA1C11CFE0E0500D9EA68 /* MGLAnnotationContainerView.m in Sources */,
@@ -2358,6 +2628,7 @@
DA6408DD1DA4E7D300908C90 /* MGLVectorStyleLayer.m in Sources */,
3566C7681D4A77BA008152BC /* MGLShapeSource.mm in Sources */,
400533021DB0862B0069F638 /* NSArray+MGLAdditions.mm in Sources */,
+ 96036A03200565C700510F3D /* NSOrthography+MGLAdditions.m in Sources */,
35136D421D42274500C20EFD /* MGLRasterStyleLayer.mm in Sources */,
3538AA1F1D542239008EC33D /* MGLForegroundStyleLayer.mm in Sources */,
DA00FC901D5EEB0D009AABC8 /* MGLAttributionInfo.mm in Sources */,
@@ -2372,9 +2643,11 @@
1F06668D1EC64F8E001C16D7 /* MGLLight.mm in Sources */,
DA35A2B81CCA9A5D00E826B2 /* MGLClockDirectionFormatter.m in Sources */,
DAD1657A1CF4CDFF001FF4B9 /* MGLShapeCollection.mm in Sources */,
+ DAF25719201901E200367EF5 /* MGLHillshadeStyleLayer.mm in Sources */,
35136D451D42275100C20EFD /* MGLSymbolStyleLayer.mm in Sources */,
35599DED1D46F14E0048254D /* MGLStyleValue.mm in Sources */,
DA8848211CBAFA6200AB86E3 /* MGLOfflinePack.mm in Sources */,
+ 0778DD441F67556C00A73B34 /* MGLComputedShapeSource.mm in Sources */,
3557F7B21E1D27D300CCA5E6 /* MGLDistanceFormatter.m in Sources */,
DA8848591CBAFB9800AB86E3 /* MGLMapView.mm in Sources */,
DA8848501CBAFB9800AB86E3 /* MGLAnnotationImage.m in Sources */,
@@ -2397,13 +2670,15 @@
DA88482B1CBAFA6200AB86E3 /* MGLTypes.m in Sources */,
FA68F14D1E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.mm in Sources */,
404C26E41D89B877000AA13D /* MGLTileSource.mm in Sources */,
+ 07D947541F67489200E37934 /* MGLAbstractShapeSource.mm in Sources */,
355AE0011E9281DA00F3939D /* MGLScaleBar.mm in Sources */,
DA88481D1CBAFA6200AB86E3 /* MGLMapCamera.mm in Sources */,
+ DACA86282019218600E9693A /* MGLRasterDEMSource.mm in Sources */,
DA8848261CBAFA6200AB86E3 /* MGLPolygon.mm in Sources */,
35B82BFA1D6C5F8400B1B721 /* NSPredicate+MGLAdditions.mm in Sources */,
- 7E016D861D9E890300A29A21 /* MGLPolygon+MGLAdditions.m in Sources */,
DA8848521CBAFB9800AB86E3 /* MGLAPIClient.m in Sources */,
966FCF4E1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.m in Sources */,
+ 8989B17E201A48EB0081CF59 /* MGLHeatmapStyleLayer.mm in Sources */,
DA8848301CBAFA6200AB86E3 /* NSProcessInfo+MGLAdditions.m in Sources */,
353AFA161D65AB17005A69F4 /* NSDate+MGLAdditions.mm in Sources */,
35D13AC51D3D19DD00AFB4E0 /* MGLFillStyleLayer.mm in Sources */,
@@ -2430,12 +2705,10 @@
files = (
35136D3A1D42271A00C20EFD /* MGLBackgroundStyleLayer.mm in Sources */,
3510FFED1D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.mm in Sources */,
- 7E016D811D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.m in Sources */,
354B83991D2E873E005D9406 /* MGLUserLocationAnnotationView.m in Sources */,
9620BB3B1E69FE1700705A1D /* MGLSDKUpdateChecker.mm in Sources */,
DAA4E4221CBB730400178DFB /* MGLPointAnnotation.mm in Sources */,
DAED38661D62D0FC00D7640F /* NSURL+MGLAdditions.m in Sources */,
- 1FB7DAB21F2A4DC900410606 /* MGLVectorSource+MGLAdditions.m in Sources */,
DAD165711CF41981001FF4B9 /* MGLFeature.mm in Sources */,
30E5781A1DAA855E0050F07E /* UIImage+MGLAdditions.mm in Sources */,
40EDA1C21CFE0E0500D9EA68 /* MGLAnnotationContainerView.m in Sources */,
@@ -2448,6 +2721,7 @@
3566C7691D4A77BA008152BC /* MGLShapeSource.mm in Sources */,
400533031DB086490069F638 /* NSArray+MGLAdditions.mm in Sources */,
35136D431D42274500C20EFD /* MGLRasterStyleLayer.mm in Sources */,
+ 96036A04200565C700510F3D /* NSOrthography+MGLAdditions.m in Sources */,
3538AA201D542239008EC33D /* MGLForegroundStyleLayer.mm in Sources */,
DA00FC911D5EEB0D009AABC8 /* MGLAttributionInfo.mm in Sources */,
DAA4E4201CBB730400178DFB /* MGLOfflinePack.mm in Sources */,
@@ -2462,6 +2736,7 @@
DAD1657B1CF4CDFF001FF4B9 /* MGLShapeCollection.mm in Sources */,
DAA4E4251CBB730400178DFB /* MGLShape.mm in Sources */,
35136D461D42275100C20EFD /* MGLSymbolStyleLayer.mm in Sources */,
+ DAF2571A201901E200367EF5 /* MGLHillshadeStyleLayer.mm in Sources */,
35599DEE1D46F14E0048254D /* MGLStyleValue.mm in Sources */,
DAA4E42B1CBB730400178DFB /* NSString+MGLAdditions.m in Sources */,
DAA4E4261CBB730400178DFB /* MGLStyle.mm in Sources */,
@@ -2489,17 +2764,20 @@
404C26E51D89B877000AA13D /* MGLTileSource.mm in Sources */,
355AE0021E9281DA00F3939D /* MGLScaleBar.mm in Sources */,
4018B1C81CDC287F00F666AF /* MGLAnnotationView.mm in Sources */,
+ 07D8C6FB1F67560100381808 /* MGLComputedShapeSource.mm in Sources */,
DAA4E4341CBB730400178DFB /* MGLFaux3DUserLocationAnnotationView.m in Sources */,
+ DACA86292019218600E9693A /* MGLRasterDEMSource.mm in Sources */,
35B82BFB1D6C5F8400B1B721 /* NSPredicate+MGLAdditions.mm in Sources */,
- 7E016D871D9E890300A29A21 /* MGLPolygon+MGLAdditions.m in Sources */,
DAA4E4311CBB730400178DFB /* MGLMapboxEvents.m in Sources */,
966FCF4F1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.m in Sources */,
DAA4E4231CBB730400178DFB /* MGLPolygon.mm in Sources */,
+ 8989B17F201A48EB0081CF59 /* MGLHeatmapStyleLayer.mm in Sources */,
353AFA171D65AB17005A69F4 /* NSDate+MGLAdditions.mm in Sources */,
35D13AC61D3D19DD00AFB4E0 /* MGLFillStyleLayer.mm in Sources */,
DAA4E42A1CBB730400178DFB /* NSProcessInfo+MGLAdditions.m in Sources */,
DAA4E4211CBB730400178DFB /* MGLOfflineStorage.mm in Sources */,
4049C2A01DB6CD6C00B3F799 /* MGLPointCollection.mm in Sources */,
+ 07D8C6FC1F67560400381808 /* MGLAbstractShapeSource.mm in Sources */,
35136D401D42273000C20EFD /* MGLLineStyleLayer.mm in Sources */,
DA704CC51F65A475004B3F28 /* MGLMapAccessibilityElement.mm in Sources */,
DA72620E1DEEE3480043BB89 /* MGLOpenGLStyleLayer.mm in Sources */,
@@ -2528,6 +2806,11 @@
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
+ 165D0CE620005351009A3C66 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = DA8847D11CBAF91600AB86E3 /* dynamic */;
+ targetProxy = 165D0CE520005351009A3C66 /* PBXContainerItemProxy */;
+ };
DA25D5C81CCDA0C100607828 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = DA25D5B81CCD9EDE00607828 /* settings */;
@@ -2561,6 +2844,14 @@
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
+ 16376B3C1FFDB4B40000563E /* LaunchScreen.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 16376B3D1FFDB4B40000563E /* Base */,
+ );
+ name = LaunchScreen.storyboard;
+ sourceTree = "<group>";
+ };
96E027251E57C76E004B8E66 /* Localizable.strings */ = {
isa = PBXVariantGroup;
children = (
@@ -2584,6 +2875,7 @@
DA5C09BA1EFC48550056B178 /* hu */,
DA3389651FA3EE1B001EA329 /* bg */,
DA80E9601FE84AD90065FC9B /* ar */,
+ DACBC60B20118ABE00C4D7E2 /* he */,
);
name = Localizable.strings;
sourceTree = "<group>";
@@ -2609,6 +2901,7 @@
DAE8CCAE1E6E8C76009B5CB0 /* nl */,
DACCD9C81F1F473700BB09A1 /* hu */,
DA33896A1FA3EE58001EA329 /* bg */,
+ DACBC60E20118AFE00C4D7E2 /* he */,
);
name = Root.strings;
sourceTree = "<group>";
@@ -2632,6 +2925,7 @@
DA618B1B1E68884E00CB7F44 /* ca */,
DA5C09BB1EFC486C0056B178 /* hu */,
DA3389681FA3EE48001EA329 /* bg */,
+ DACBC60D20118ADE00C4D7E2 /* he */,
);
name = Localizable.strings;
sourceTree = "<group>";
@@ -2653,6 +2947,7 @@
DA704CC71F6663A3004B3F28 /* uk */,
DA33895F1FA3EAB7001EA329 /* pt-BR */,
DA3389661FA3EE28001EA329 /* bg */,
+ DACBC60C20118AD000C4D7E2 /* he */,
);
name = Foundation.strings;
sourceTree = "<group>";
@@ -2710,6 +3005,108 @@
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
+ 16376B0E1FFD9DAF0000563E /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CODE_SIGN_IDENTITY = "iPhone Developer";
+ CODE_SIGN_STYLE = Automatic;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ INFOPLIST_FILE = "Integration Tests/Info.plist";
+ IPHONEOS_DEPLOYMENT_TARGET = 11.2;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+ PRODUCT_BUNDLE_IDENTIFIER = "com.mapbox.integration-tests";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TARGETED_DEVICE_FAMILY = "1,2";
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Integration Test Harness.app/Integration Test Harness";
+ };
+ name = Debug;
+ };
+ 16376B0F1FFD9DAF0000563E /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CODE_SIGN_IDENTITY = "iPhone Developer";
+ CODE_SIGN_STYLE = Automatic;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ INFOPLIST_FILE = "Integration Tests/Info.plist";
+ IPHONEOS_DEPLOYMENT_TARGET = 11.2;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+ PRODUCT_BUNDLE_IDENTIFIER = "com.mapbox.integration-tests";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TARGETED_DEVICE_FAMILY = "1,2";
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Integration Test Harness.app/Integration Test Harness";
+ };
+ name = Release;
+ };
+ 16376B431FFDB4B40000563E /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CODE_SIGN_IDENTITY = "iPhone Developer";
+ CODE_SIGN_STYLE = Automatic;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ INFOPLIST_FILE = "Integration Test Harness/Info.plist";
+ IPHONEOS_DEPLOYMENT_TARGET = 11.2;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ PRODUCT_BUNDLE_IDENTIFIER = "com.mapbox.Integration-Test-Harness";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 16376B441FFDB4B40000563E /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CODE_SIGN_IDENTITY = "iPhone Developer";
+ CODE_SIGN_STYLE = Automatic;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ INFOPLIST_FILE = "Integration Test Harness/Info.plist";
+ IPHONEOS_DEPLOYMENT_TARGET = 11.2;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ PRODUCT_BUNDLE_IDENTIFIER = "com.mapbox.Integration-Test-Harness";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Release;
+ };
DA1DC95F1CB6C1C2006E619F /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -2720,7 +3117,9 @@
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
@@ -2728,7 +3127,11 @@
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
@@ -2773,7 +3176,9 @@
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
@@ -2781,7 +3186,11 @@
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
@@ -2804,6 +3213,7 @@
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
+ SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SYMROOT = ../../build/ios;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
@@ -2867,6 +3277,7 @@
INFOPLIST_FILE = test/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+ OTHER_CFLAGS = "-fvisibility=hidden";
OTHER_CPLUSPLUSFLAGS = (
"$(OTHER_CFLAGS)",
"$(variant_cflags)",
@@ -2891,6 +3302,7 @@
INFOPLIST_FILE = test/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+ OTHER_CFLAGS = "-fvisibility=hidden";
OTHER_CPLUSPLUSFLAGS = (
"$(OTHER_CFLAGS)",
"$(variant_cflags)",
@@ -2910,6 +3322,7 @@
baseConfigurationReference = 55D8C9941D0F133500F42F10 /* config.xcconfig */;
buildSettings = {
BITCODE_GENERATION_MODE = bitcode;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
@@ -2951,6 +3364,7 @@
baseConfigurationReference = 55D8C9941D0F133500F42F10 /* config.xcconfig */;
buildSettings = {
BITCODE_GENERATION_MODE = bitcode;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
@@ -3098,6 +3512,24 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
+ 16376B101FFD9DAF0000563E /* Build configuration list for PBXNativeTarget "integration" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 16376B0E1FFD9DAF0000563E /* Debug */,
+ 16376B0F1FFD9DAF0000563E /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 16376B421FFDB4B40000563E /* Build configuration list for PBXNativeTarget "Integration Test Harness" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 16376B431FFDB4B40000563E /* Debug */,
+ 16376B441FFDB4B40000563E /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
DA1DC9451CB6C1C2006E619F /* Build configuration list for PBXProject "ios" */ = {
isa = XCConfigurationList;
buildConfigurations = (
diff --git a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/CI.xcscheme b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/CI.xcscheme
index 40249b8024..4679378126 100644
--- a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/CI.xcscheme
+++ b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/CI.xcscheme
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "0820"
+ LastUpgradeVersion = "0910"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -54,6 +54,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
@@ -83,6 +84,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
diff --git a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/Integration Test Harness.xcscheme b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/Integration Test Harness.xcscheme
new file mode 100644
index 0000000000..283c58ef7f
--- /dev/null
+++ b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/Integration Test Harness.xcscheme
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "0920"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "16376B2E1FFDB4B40000563E"
+ BuildableName = "Integration Test Harness.app"
+ BlueprintName = "Integration Test Harness"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ language = ""
+ shouldUseLaunchSchemeArgsEnv = "YES">
+ <Testables>
+ <TestableReference
+ skipped = "NO">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "16376B061FFD9DAF0000563E"
+ BuildableName = "integration.xctest"
+ BlueprintName = "integration"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </TestableReference>
+ </Testables>
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "16376B2E1FFDB4B40000563E"
+ BuildableName = "Integration Test Harness.app"
+ BlueprintName = "Integration Test Harness"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ language = ""
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "16376B2E1FFDB4B40000563E"
+ BuildableName = "Integration Test Harness.app"
+ BlueprintName = "Integration Test Harness"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Release"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "16376B2E1FFDB4B40000563E"
+ BuildableName = "Integration Test Harness.app"
+ BlueprintName = "Integration Test Harness"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/bench.xcscheme b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/bench.xcscheme
index f5aff5b3b4..d1a152bce7 100644
--- a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/bench.xcscheme
+++ b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/bench.xcscheme
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "0820"
+ LastUpgradeVersion = "0910"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -26,6 +26,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
@@ -45,6 +46,7 @@
buildConfiguration = "Release"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
diff --git a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic+static.xcscheme b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic+static.xcscheme
index 8e1c176bba..e5f17124f2 100644
--- a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic+static.xcscheme
+++ b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic+static.xcscheme
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "0820"
+ LastUpgradeVersion = "0910"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -54,6 +54,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
@@ -83,6 +84,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
diff --git a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme
index 7dfffccef5..3816aa5c91 100644
--- a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme
+++ b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "0820"
+ LastUpgradeVersion = "0910"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -40,6 +40,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
@@ -69,6 +70,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
diff --git a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/iosapp.xcscheme b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/iosapp.xcscheme
index 035c7818ae..7d5f2cd6d2 100644
--- a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/iosapp.xcscheme
+++ b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/iosapp.xcscheme
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "0820"
+ LastUpgradeVersion = "0910"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -26,6 +26,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
@@ -45,6 +46,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
diff --git a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/static.xcscheme b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/static.xcscheme
index 156651a4a6..fc385d3763 100644
--- a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/static.xcscheme
+++ b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/static.xcscheme
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "0820"
+ LastUpgradeVersion = "0910"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -26,6 +26,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
@@ -45,6 +46,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
diff --git a/platform/ios/jazzy.yml b/platform/ios/jazzy.yml
index 2a7a2b667c..61e9ad39e8 100644
--- a/platform/ios/jazzy.yml
+++ b/platform/ios/jazzy.yml
@@ -3,7 +3,7 @@ author: Mapbox
author_url: https://www.mapbox.com/
github_url: https://github.com/mapbox/mapbox-gl-native
dash_url: https://www.mapbox.com/ios-sdk/docsets/Mapbox.xml
-copyright: '© 2014–2017 [Mapbox](https://www.mapbox.com/). See [license](https://github.com/mapbox/mapbox-gl-native/blob/master/LICENSE.md) for more details.'
+copyright: '© 2014–2018 [Mapbox](https://www.mapbox.com/). See [license](https://github.com/mapbox/mapbox-gl-native/blob/master/LICENSE.md) for more details.'
head: |
<link rel='shortcut icon' href='https://www.mapbox.com/img/favicon.ico' type='image/x-icon' />
@@ -22,6 +22,7 @@ custom_categories:
- Using Style Functions at Runtime
- Working with Mapbox Studio
- Working with GeoJSON Data
+ - Predicates and Expressions
- For Style Authors
- Tile URL Templates
- Info.plist Keys
@@ -62,7 +63,6 @@ custom_categories:
- name: Styling the Map
children:
- MGLStyle
- - MGLStyleValue
- MGLLight
- name: Style Primitives
children:
@@ -79,8 +79,11 @@ custom_categories:
- MGLSource
- MGLTileSource
- MGLImageSource
+ - MGLAbstractShapeSource
- MGLShapeSource
+ - MGLComputedShapeSource
- MGLRasterSource
+ - MGLRasterDEMSource
- MGLVectorSource
- name: Style Layers
children:
@@ -92,6 +95,8 @@ custom_categories:
- MGLCircleStyleLayer
- MGLFillStyleLayer
- MGLFillExtrusionStyleLayer
+ - MGLHeatmapStyleLayer
+ - MGLHillshadeStyleLayer
- MGLLineStyleLayer
- MGLSymbolStyleLayer
- name: Offline Maps
diff --git a/platform/ios/resources/es.lproj/Localizable.strings b/platform/ios/resources/es.lproj/Localizable.strings
index 43f3ec3d6e..90811be973 100644
--- a/platform/ios/resources/es.lproj/Localizable.strings
+++ b/platform/ios/resources/es.lproj/Localizable.strings
Binary files differ
diff --git a/platform/ios/resources/fr.lproj/Localizable.strings b/platform/ios/resources/fr.lproj/Localizable.strings
index 7c02663d47..cd93d265d7 100644
--- a/platform/ios/resources/fr.lproj/Localizable.strings
+++ b/platform/ios/resources/fr.lproj/Localizable.strings
@@ -1,44 +1,47 @@
/* Accessibility hint */
-"ANNOTATION_A11Y_HINT" = "Voir plus d’informations";
+"ANNOTATION_A11Y_HINT" = "Afficher plus d’informations";
/* No comment provided by engineer. */
-"API_CLIENT_400_DESC" = "La tâche de données pour cette session a échouée. Requête originale : %@";
+"API_CLIENT_400_DESC" = "La tâche de session de données a échoué. La requête originale était : %@";
/* No comment provided by engineer. */
-"API_CLIENT_400_REASON" = "Le code d’erreur était %ld";
+"API_CLIENT_400_REASON" = "Le code de statut était %ld";
/* No comment provided by engineer. */
"CANCEL" = "Annuler";
/* Accessibility hint for closing the selected annotation’s callout view and returning to the map */
-"CLOSE_CALLOUT_A11Y_HINT" = "Retour à la carte";
+"CLOSE_CALLOUT_A11Y_HINT" = "Retourne à la carte";
/* Accessibility hint */
-"COMPASS_A11Y_HINT" = "Tourne la carte vers le nord";
+"COMPASS_A11Y_HINT" = "Réoriente la carte vers le nord";
/* Accessibility label */
-"COMPASS_A11Y_LABEL" = "Boussole";
+"COMPASS_A11Y_LABEL" = "Compas";
/* Compass abbreviation for north */
"COMPASS_NORTH" = "N";
/* Instructions in Interface Builder designable; {key}, {plist file name} */
-"DESIGNABLE" = "Pour afficher une carte hébergée par Mapbox, remplacez %1$@ par votre token d’accès dans %2$@\n\nPour plus d’informations, voir :";
+"DESIGNABLE" = "Pour afficher ici une carte hébergée par Mapbox, indiquez %1$@ à votre jeton d’accès dans %2$@\n\nPour des instructions détaillées, voyez :";
/* Setup documentation URL display string; keep as short as possible */
"FIRST_STEPS_URL" = "mapbox.com/help/first-steps-ios-sdk";
/* Accessibility hint */
-"INFO_A11Y_HINT" = "Montre les crédits, un formulaire de retour d’expérience, et plus.";
+"INFO_A11Y_HINT" = "Affiche les crédits d’auteurs, un formulaire de retour d’avis et plus encore";
/* Accessibility label */
"INFO_A11Y_LABEL" = "À propos de cette carte";
+/* List separator */
+"LIST_SEPARATOR" = ";";
+
/* User-friendly error description */
-"LOAD_MAP_FAILED_DESC" = "La carte n’a pas pu être chargée car une erreur inconnue est survenue.";
+"LOAD_MAP_FAILED_DESC" = "Le chargement de la carte a échoué car une erreur inconnue est survenue.";
/* User-friendly error description */
-"LOAD_STYLE_FAILED_DESC" = "La carte n’a pas pu être chargée car le style ne peut pas être chargé.";
+"LOAD_STYLE_FAILED_DESC" = "Le chargement de la carte a échoué car le style n’a pas pu être chargé.";
/* Accessibility label */
"LOGO_A11Y_LABEL" = "Mapbox";
@@ -46,23 +49,44 @@
/* Accessibility label */
"MAP_A11Y_LABEL" = "Carte";
-/* Map accessibility value */
-"MAP_A11Y_VALUE" = "Zoom %1$dx\n%2$ld annotation(s) visible(s)";
+/* Map accessibility value; {number of visible annotations} */
+"MAP_A11Y_VALUE_ANNOTATIONS" = "%ld annotation(s) visible(s).";
+
+/* Map accessibility value; {list of visible places} */
+"MAP_A11Y_VALUE_PLACES" = "Lieux visibles : %@.";
+
+/* Map accessibility value; {number of visible roads} */
+"MAP_A11Y_VALUE_ROADS" = "%ld route(s) visible(s).";
+
+/* Map accessibility value; {zoom level} */
+"MAP_A11Y_VALUE_ZOOM" = "Zoom %d×.";
/* User-friendly error description */
-"PARSE_STYLE_FAILED_DESC" = "La carte n’a pas pu être chargée car le style est corrompu.";
+"PARSE_STYLE_FAILED_DESC" = "Le chargement de la carte a échoué car le style est corrompu.";
+
+/* String format for accessibility value for road feature; {starting compass direction}, {ending compass direction} */
+"ROAD_DIRECTION_A11Y_FMT" = "%1$@ à %2$@";
+
+/* Accessibility value indicating that a road is a divided road (dual carriageway) */
+"ROAD_DIVIDED_A11Y_VALUE" = "Route à chaussées séparées";
+
+/* Accessibility value indicating that a road is a one-way road */
+"ROAD_ONEWAY_A11Y_VALUE" = "Route en sens unique";
+
+/* String format for accessibility value for road feature; {route number} */
+"ROAD_REF_A11Y_FMT" = "Route %@";
/* Action sheet title */
-"SDK_NAME" = "Mapbox Maps SDK for iOS";
+"SDK_NAME" = "SDK Mapbox Maps pour iOS";
/* Developer-only SDK update notification; {latest version, in format x.x.x} */
-"SDK_UPDATE_AVAILABLE" = "La version %@ du SDK Mapbox pour iOS est maintenant disponible :";
+"SDK_UPDATE_AVAILABLE" = "Le SDK Mapbox Maps pour iOS en version %@ est maintenant disponible :";
/* User-friendly error description */
-"STYLE_NOT_FOUND_DESC" = "La carte n’a pas pu être chargée car le style n’a pas été trouvé ou est incompatible.";
+"STYLE_NOT_FOUND_DESC" = "Le chargement de la carte a échoué car le style n’a pas été trouvé ou est incompatible.";
/* Telemetry prompt message */
-"TELEMETRY_DISABLED_MSG" = "Vous pouvez contribuer à OpenStreetMap et Mapbox en partageant des données d’utilisation anonymes.";
+"TELEMETRY_DISABLED_MSG" = "Vous pouvez aider à améliorer OpenStreetMap et Mapbox en contribuant des données anonymes d’utilisation.";
/* Telemetry prompt button */
"TELEMETRY_DISABLED_OFF" = "Ne pas participer";
@@ -71,22 +95,22 @@
"TELEMETRY_DISABLED_ON" = "Participer";
/* Telemetry prompt message */
-"TELEMETRY_ENABLED_MSG" = "Vous aidez à améliorer OpenStreetMap et Mapbox en partageant des données d’utilisation anonymes.";
+"TELEMETRY_ENABLED_MSG" = "Vous aidez à rendre OpenStreetMap et Mapbox encore meilleurs en contribuant des données anonymes d’utilisation.";
/* Telemetry prompt button */
-"TELEMETRY_ENABLED_OFF" = "Ne plus participer";
+"TELEMETRY_ENABLED_OFF" = "Arrêter de participer";
/* Telemetry prompt button */
"TELEMETRY_ENABLED_ON" = "Continuer à participer";
/* Telemetry prompt button */
-"TELEMETRY_MORE" = "En savoir plus";
+"TELEMETRY_MORE" = "Dites m’en plus";
/* Action in attribution sheet */
"TELEMETRY_NAME" = "Télémétrie Mapbox";
/* Telemetry prompt title */
-"TELEMETRY_TITLE" = "Améliorez les cartes Mapbox";
+"TELEMETRY_TITLE" = "Rend meilleure les cartes Mapbox";
/* Default user location annotation title */
"USER_DOT_TITLE" = "Vous êtes ici";
diff --git a/platform/ios/resources/fr.lproj/Localizable.stringsdict b/platform/ios/resources/fr.lproj/Localizable.stringsdict
index 4222421a12..12dde71a38 100644
--- a/platform/ios/resources/fr.lproj/Localizable.stringsdict
+++ b/platform/ios/resources/fr.lproj/Localizable.stringsdict
@@ -2,22 +2,26 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
- <key>MAP_A11Y_VALUE</key>
+ <key>MAP_A11Y_VALUE_ANNOTATIONS</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
- <string>%#@level@
-%#@count@</string>
- <key>level</key>
+ <string>%#@count@</string>
+ <key>count</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
- <string>d</string>
+ <string>ld</string>
<key>one</key>
- <string>Zoom %dx</string>
+ <string>%d annotation visible</string>
<key>other</key>
- <string>Zoom %dx</string>
+ <string>%d annotations visibles</string>
</dict>
+ </dict>
+ <key>MAP_A11Y_VALUE_ROADS</key>
+ <dict>
+ <key>NSStringLocalizedFormatKey</key>
+ <string>%#@count@</string>
<key>count</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
@@ -25,9 +29,25 @@
<key>NSStringFormatValueTypeKey</key>
<string>ld</string>
<key>one</key>
- <string>%d annotation visible</string>
+ <string>%d route visible</string>
<key>other</key>
- <string>%d annotations visibles</string>
+ <string>%d routes visibles</string>
+ </dict>
+ </dict>
+ <key>MAP_A11Y_VALUE_ZOOM</key>
+ <dict>
+ <key>NSStringLocalizedFormatKey</key>
+ <string>%#@level@</string>
+ <key>level</key>
+ <dict>
+ <key>NSStringFormatSpecTypeKey</key>
+ <string>NSStringPluralRuleType</string>
+ <key>NSStringFormatValueTypeKey</key>
+ <string>d</string>
+ <key>one</key>
+ <string>Zoom %d×</string>
+ <key>other</key>
+ <string>Zoom %d×</string>
</dict>
</dict>
</dict>
diff --git a/platform/ios/resources/he.lproj/Localizable.strings b/platform/ios/resources/he.lproj/Localizable.strings
new file mode 100644
index 0000000000..9503c16d3d
--- /dev/null
+++ b/platform/ios/resources/he.lproj/Localizable.strings
@@ -0,0 +1,117 @@
+/* Accessibility hint */
+"ANNOTATION_A11Y_HINT" = "מציג מידע נוסף";
+
+/* No comment provided by engineer. */
+"API_CLIENT_400_DESC" = "המידע של המשימה נכשל, הבקשה המקורית הייתה: %@";
+
+/* No comment provided by engineer. */
+"API_CLIENT_400_REASON" = "סטטוס הקוד היה %ld";
+
+/* No comment provided by engineer. */
+"CANCEL" = "ביטול";
+
+/* Accessibility hint for closing the selected annotation’s callout view and returning to the map */
+"CLOSE_CALLOUT_A11Y_HINT" = "חזרה אל המפה";
+
+/* Accessibility hint */
+"COMPASS_A11Y_HINT" = "קיבוע המפה לצד צפון";
+
+/* Accessibility label */
+"COMPASS_A11Y_LABEL" = "מצפן";
+
+/* Compass abbreviation for north */
+"COMPASS_NORTH" = "צ";
+
+/* Instructions in Interface Builder designable; {key}, {plist file name} */
+"DESIGNABLE" = "בכדי להציג את המפות המאוכסנות של Mapbox כאן, הגדר %1$@ את אסימון הגישה ב %2$@\n\nלהוראות מפורטות, ראה:";
+
+/* Setup documentation URL display string; keep as short as possible */
+"FIRST_STEPS_URL" = "mapbox.com/help/first-steps-ios-sdk";
+
+/* Accessibility hint */
+"INFO_A11Y_HINT" = "מציג קרדיטים, טופס משוב ועוד";
+
+/* Accessibility label */
+"INFO_A11Y_LABEL" = "אודות המפה";
+
+/* List separator */
+"LIST_SEPARATOR" = ", ";
+
+/* User-friendly error description */
+"LOAD_MAP_FAILED_DESC" = "טעינת המפה נכשלה עכב שגיאה לא ידועה.";
+
+/* User-friendly error description */
+"LOAD_STYLE_FAILED_DESC" = "טעינת המפה נכשלה - לא ניתן לטעון את הסגנון.";
+
+/* Accessibility label */
+"LOGO_A11Y_LABEL" = "Mapbox";
+
+/* Accessibility label */
+"MAP_A11Y_LABEL" = "מפה";
+
+/* Map accessibility value; {number of visible annotations} */
+"MAP_A11Y_VALUE_ANNOTATIONS" = "%ld נקודה(ות) ציון מוצגות.";
+
+/* Map accessibility value; {list of visible places} */
+"MAP_A11Y_VALUE_PLACES" = "מקומות מוצגים: %@.";
+
+/* Map accessibility value; {number of visible roads} */
+"MAP_A11Y_VALUE_ROADS" = "%ld דרך(ים) מוצגות.";
+
+/* Map accessibility value; {zoom level} */
+"MAP_A11Y_VALUE_ZOOM" = "זום x%d.";
+
+/* User-friendly error description */
+"PARSE_STYLE_FAILED_DESC" = "טעינת המפה נכשלה - הסגנון פגום.";
+
+/* String format for accessibility value for road feature; {starting compass direction}, {ending compass direction} */
+"ROAD_DIRECTION_A11Y_FMT" = "%1$@ אל %2$@";
+
+/* Accessibility value indicating that a road is a divided road (dual carriageway) */
+"ROAD_DIVIDED_A11Y_VALUE" = "דרך מחולקת";
+
+/* Accessibility value indicating that a road is a one-way road */
+"ROAD_ONEWAY_A11Y_VALUE" = "חד סטרי";
+
+/* String format for accessibility value for road feature; {route number} */
+"ROAD_REF_A11Y_FMT" = "כביש %@";
+
+/* Action sheet title */
+"SDK_NAME" = "Mapbox Maps SDK for iOS";
+
+/* Developer-only SDK update notification; {latest version, in format x.x.x} */
+"SDK_UPDATE_AVAILABLE" = "Mapbox Maps SDK for iOS version %@ is now available:";
+
+/* User-friendly error description */
+"STYLE_NOT_FOUND_DESC" = "טעינת המפה נכשלה - לא ניתן למצוא את הסגנון או שהסגנון אינו תואם.";
+
+/* Telemetry prompt message */
+"TELEMETRY_DISABLED_MSG" = "אתם מסייעים לשפר את המפות של OpenStreetMap ו Mapbox באמצעות שיתוף אנונימי של נתוני השימוש.";
+
+/* Telemetry prompt button */
+"TELEMETRY_DISABLED_OFF" = "אל תשתף";
+
+/* Telemetry prompt button */
+"TELEMETRY_DISABLED_ON" = "שתף";
+
+/* Telemetry prompt message */
+"TELEMETRY_ENABLED_MSG" = "אתם מסייעים לשפר את המפות של OpenStreetMap ו Mapbox באמצעות שיתוף אנונימי של נתוני השימוש.";
+
+/* Telemetry prompt button */
+"TELEMETRY_ENABLED_OFF" = "הפסק שיתוף";
+
+/* Telemetry prompt button */
+"TELEMETRY_ENABLED_ON" = "המשך לשתף";
+
+/* Telemetry prompt button */
+"TELEMETRY_MORE" = "ספר לי עוד";
+
+/* Action in attribution sheet */
+"TELEMETRY_NAME" = "Mapbox Telemetry";
+
+/* Telemetry prompt title */
+"TELEMETRY_TITLE" = "הפוך את המפות של Mapbox לטובות יותר";
+
+/* Default user location annotation title */
+"USER_DOT_TITLE" = "אתה נמצא כאן";
+
diff --git a/platform/ios/resources/sv.lproj/Localizable.strings b/platform/ios/resources/sv.lproj/Localizable.strings
index df4f3554ff..d136f9e52f 100644
--- a/platform/ios/resources/sv.lproj/Localizable.strings
+++ b/platform/ios/resources/sv.lproj/Localizable.strings
@@ -2,7 +2,7 @@
"ANNOTATION_A11Y_HINT" = "Visa mer information";
/* No comment provided by engineer. */
-"API_CLIENT_400_DESC" = "Sessionens anrop misslyckades. Originalanropet var: %@";
+"API_CLIENT_400_DESC" = "Förfrågan misslyckades. Originalförfrågan var: %@";
/* No comment provided by engineer. */
"API_CLIENT_400_REASON" = "Statuskoden var %ld";
@@ -23,22 +23,25 @@
"COMPASS_NORTH" = "N";
/* Instructions in Interface Builder designable; {key}, {plist file name} */
-"DESIGNABLE" = "Sätt %1$@ till din access token i %2$@ för att visa kartan som Mapbox levererar.";
+"DESIGNABLE" = "För att visa Mapbox-karta här, fyll i %1$@ till din access token i %2$@\n\nFör detaljerad information, se:";
/* Setup documentation URL display string; keep as short as possible */
"FIRST_STEPS_URL" = "mapbox.com/help/first-steps-ios-sdk";
/* Accessibility hint */
-"INFO_A11Y_HINT" = "Visa medverkande, återkopplingsformulär och mer.";
+"INFO_A11Y_HINT" = "Visa mer";
/* Accessibility label */
"INFO_A11Y_LABEL" = "Om den här kartan";
+/* List separator */
+"LIST_SEPARATOR" = ", ";
+
/* User-friendly error description */
-"LOAD_MAP_FAILED_DESC" = "Kartan kunde inte laddas på grund av att ett okänt fel inträffade.";
+"LOAD_MAP_FAILED_DESC" = "Misslyckades med att ladda kartan på grund av ett okänt fel.";
/* User-friendly error description */
-"LOAD_STYLE_FAILED_DESC" = "Kartan kunde inte laddas på grund av att stilen är skadad.";
+"LOAD_STYLE_FAILED_DESC" = "Misslyckades med att ladda kartan på grund av att kartstilen kunde inte laddas.";
/* Accessibility label */
"LOGO_A11Y_LABEL" = "Mapbox";
@@ -46,47 +49,68 @@
/* Accessibility label */
"MAP_A11Y_LABEL" = "Karta";
-/* Map accessibility value */
-"MAP_A11Y_VALUE" = "Zoom %1$dx\n%2$ld annotering(ar) synliga";
+/* Map accessibility value; {number of visible annotations} */
+"MAP_A11Y_VALUE_ANNOTATIONS" = "%ld annotering(ar) synliga.";
+
+/* Map accessibility value; {list of visible places} */
+"MAP_A11Y_VALUE_PLACES" = "Synliga platser: %@.";
+
+/* Map accessibility value; {number of visible roads} */
+"MAP_A11Y_VALUE_ROADS" = "%ld väg(ar) synliga.";
+
+/* Map accessibility value; {zoom level} */
+"MAP_A11Y_VALUE_ZOOM" = "Zoom %dx.";
/* User-friendly error description */
-"PARSE_STYLE_FAILED_DESC" = "Kartan kunde inte laddas på grund av att stilen är skadad.";
+"PARSE_STYLE_FAILED_DESC" = "Misslyckades med att ladda kartan för att kartstilen är korrupt.";
+
+/* String format for accessibility value for road feature; {starting compass direction}, {ending compass direction} */
+"ROAD_DIRECTION_A11Y_FMT" = "%1$@ till %2$@";
+
+/* Accessibility value indicating that a road is a divided road (dual carriageway) */
+"ROAD_DIVIDED_A11Y_VALUE" = "Delad väg";
+
+/* Accessibility value indicating that a road is a one-way road */
+"ROAD_ONEWAY_A11Y_VALUE" = "Enkelriktad väg";
+
+/* String format for accessibility value for road feature; {route number} */
+"ROAD_REF_A11Y_FMT" = "Rutt %@";
/* Action sheet title */
"SDK_NAME" = "Mapbox Maps SDK for iOS";
/* Developer-only SDK update notification; {latest version, in format x.x.x} */
-"SDK_UPDATE_AVAILABLE" = "Mapbox Maps SDK for iOS version %@ är nu tillgängligt:";
+"SDK_UPDATE_AVAILABLE" = "Mapbox Maps SDK for iOS version %@ är nu tillgänglig:";
/* User-friendly error description */
-"STYLE_NOT_FOUND_DESC" = "Kartan kunde inte laddas på grund av ett okänt fel.";
+"STYLE_NOT_FOUND_DESC" = "Kartan kunde inte laddas för att kartstilen kunde inte hittas eller för att den är inkompatibel.";
/* Telemetry prompt message */
-"TELEMETRY_DISABLED_MSG" = "Du kan hjälpa till att göra OpenStreetMap och Mapbox karttjänster bättre genom att bidra med anonymiserad användningsdata.";
+"TELEMETRY_DISABLED_MSG" = "Du kan göra OpenStreetMap och Mapbox kartor bättre genom att bidra med anonym data.";
/* Telemetry prompt button */
-"TELEMETRY_DISABLED_OFF" = "Avstå";
+"TELEMETRY_DISABLED_OFF" = "Delta Inte";
/* Telemetry prompt button */
"TELEMETRY_DISABLED_ON" = "Delta";
/* Telemetry prompt message */
-"TELEMETRY_ENABLED_MSG" = "Du hjälper till att göra OpenStreetMap och Mapbox karttjänster bättre genom att bidra med anonymiserad användningsdata.";
+"TELEMETRY_ENABLED_MSG" = "Du kan göra OpenStreetMap och Mapbox kartor bättre genom att bidra med anonym data.";
/* Telemetry prompt button */
-"TELEMETRY_ENABLED_OFF" = "Avsluta deltagandet";
+"TELEMETRY_ENABLED_OFF" = "Sluta delta";
/* Telemetry prompt button */
-"TELEMETRY_ENABLED_ON" = "Fortsätt deltagandet";
+"TELEMETRY_ENABLED_ON" = "Fortsätt delta";
/* Telemetry prompt button */
-"TELEMETRY_MORE" = "Visa mer";
+"TELEMETRY_MORE" = "Berätta mer";
/* Action in attribution sheet */
"TELEMETRY_NAME" = "Mapbox Telemetri";
/* Telemetry prompt title */
-"TELEMETRY_TITLE" = "Gör Mapbox kartor bättre";
+"TELEMETRY_TITLE" = "Gör Mapbox Kartor Bättre";
/* Default user location annotation title */
"USER_DOT_TITLE" = "Du är här";
diff --git a/platform/ios/resources/uk.lproj/Localizable.strings b/platform/ios/resources/uk.lproj/Localizable.strings
index 79b9779cc6..4e5a452b95 100644
--- a/platform/ios/resources/uk.lproj/Localizable.strings
+++ b/platform/ios/resources/uk.lproj/Localizable.strings
@@ -1,5 +1,5 @@
/* Accessibility hint */
-"ANNOTATION_A11Y_HINT" = "Показує більше інформації";
+"ANNOTATION_A11Y_HINT" = "Показати більше інформації";
/* No comment provided by engineer. */
"API_CLIENT_400_DESC" = "The session data task failed. Original request was: %@";
@@ -11,10 +11,10 @@
"CANCEL" = "Скасувати";
/* Accessibility hint for closing the selected annotation’s callout view and returning to the map */
-"CLOSE_CALLOUT_A11Y_HINT" = "Повернутись до мапи";
+"CLOSE_CALLOUT_A11Y_HINT" = "Повернення до мапи";
/* Accessibility hint */
-"COMPASS_A11Y_HINT" = "Обертає напрямок мапи на північ";
+"COMPASS_A11Y_HINT" = "Обертає мапу в напрмяку на північ";
/* Accessibility label */
"COMPASS_A11Y_LABEL" = "Компас";
@@ -23,22 +23,25 @@
"COMPASS_NORTH" = "Пн";
/* Instructions in Interface Builder designable; {key}, {plist file name} */
-"DESIGNABLE" = "Щоб побачити мапу з серверів Mapbox тут, вкажіть у %1$@ ваш ключ доступу для %2$@\n\nДля отримання докладих інструкцій, дивіться:";
+"DESIGNABLE" = "Для показу мапи від Mapbox тут, встановіть %1$@ для вашої мітки доступу в %2$@\n\n\nДля отримання докладних інструкцій дивіться:";
/* Setup documentation URL display string; keep as short as possible */
"FIRST_STEPS_URL" = "mapbox.com/help/first-steps-ios-sdk";
/* Accessibility hint */
-"INFO_A11Y_HINT" = "Показує інформацію про розробників, форму зворотнього зв'язку та інше";
+"INFO_A11Y_HINT" = "Показує інформацію про розробників, форму зворотнього відгуку та інше";
/* Accessibility label */
-"INFO_A11Y_LABEL" = "Про мапу";
+"INFO_A11Y_LABEL" = "Про цю мапу";
+
+/* List separator */
+"LIST_SEPARATOR" = ", ";
/* User-friendly error description */
-"LOAD_MAP_FAILED_DESC" = "Неможливо завантажити мапу через невідому помилку.";
+"LOAD_MAP_FAILED_DESC" = "Через невідому помилку неможливо завантажити мапу";
/* User-friendly error description */
-"LOAD_STYLE_FAILED_DESC" = "Неможливо завантажити мапу, бо неможливо завантажити стиль.";
+"LOAD_STYLE_FAILED_DESC" = "Збій мапи через те, що неможливо завантажити стиль";
/* Accessibility label */
"LOGO_A11Y_LABEL" = "Mapbox";
@@ -46,20 +49,41 @@
/* Accessibility label */
"MAP_A11Y_LABEL" = "Мапа";
-/* Map accessibility value */
-"MAP_A11Y_VALUE" = "Масштаб %1$dx\n%2$ld підпис(ів) показано";
+/* Map accessibility value; {number of visible annotations} */
+"MAP_A11Y_VALUE_ANNOTATIONS" = "%ld підпис(ів) видно.";
+
+/* Map accessibility value; {list of visible places} */
+"MAP_A11Y_VALUE_PLACES" = "Показано місць: %@";
+
+/* Map accessibility value; {number of visible roads} */
+"MAP_A11Y_VALUE_ROADS" = "%ld доріг видно.";
+
+/* Map accessibility value; {zoom level} */
+"MAP_A11Y_VALUE_ZOOM" = "Масштаб %d.";
/* User-friendly error description */
-"PARSE_STYLE_FAILED_DESC" = "Неможливо завантажити мапу, через помилки в стилі.";
+"PARSE_STYLE_FAILED_DESC" = "Неможливо завантажити мапу через пошкоджений стиль.";
+
+/* String format for accessibility value for road feature; {starting compass direction}, {ending compass direction} */
+"ROAD_DIRECTION_A11Y_FMT" = "%1$@ до %2$@";
+
+/* Accessibility value indicating that a road is a divided road (dual carriageway) */
+"ROAD_DIVIDED_A11Y_VALUE" = "Дорога з розподілювачем";
+
+/* Accessibility value indicating that a road is a one-way road */
+"ROAD_ONEWAY_A11Y_VALUE" = "Односторонній рух";
+
+/* String format for accessibility value for road feature; {route number} */
+"ROAD_REF_A11Y_FMT" = "Маршрут %@";
/* Action sheet title */
-"SDK_NAME" = "Mapbox Maps SDK for iOS";
+"SDK_NAME" = "Mapbox Maps SDK для iOS";
/* Developer-only SDK update notification; {latest version, in format x.x.x} */
-"SDK_UPDATE_AVAILABLE" = "Доступна версія %@ Mapbox Maps SDK for iOS:";
+"SDK_UPDATE_AVAILABLE" = "Mapbox Maps SDK для iOS версія %@ наявна:";
/* User-friendly error description */
-"STYLE_NOT_FOUND_DESC" = "Неможливо завантажити мапу, бо неможливо знайти стиль або він несумісний.";
+"STYLE_NOT_FOUND_DESC" = "Мапу неможливо завантажити через відсутність стилю або його невідповідність.";
/* Telemetry prompt message */
"TELEMETRY_DISABLED_MSG" = "Ви можете допомогти зробити мапи OpenStreetMap та Mapbox кращими надаючи анонімізовані дані про користування застосунком.";
@@ -71,7 +95,7 @@
"TELEMETRY_DISABLED_ON" = "Брати участь";
/* Telemetry prompt message */
-"TELEMETRY_ENABLED_MSG" = "Ви допомагаєте робити мапи OpenStreetMap та Mapbox кращими надаючи анонімізовані дані про користування застосунком.";
+"TELEMETRY_ENABLED_MSG" = "Ви допомагаєте робити мапи OpenStreetMap та Mapbox краще пощирюючи анонімні дані про користування мапами.";
/* Telemetry prompt button */
"TELEMETRY_ENABLED_OFF" = "Припинити участь";
@@ -86,7 +110,7 @@
"TELEMETRY_NAME" = "Телеметрія Mapbox";
/* Telemetry prompt title */
-"TELEMETRY_TITLE" = "Робіть мапи Mapbox кращими";
+"TELEMETRY_TITLE" = "Допоможіть зробити мапи Mapbox краще";
/* Default user location annotation title */
"USER_DOT_TITLE" = "Ви тут";
diff --git a/platform/ios/resources/uk.lproj/Localizable.stringsdict b/platform/ios/resources/uk.lproj/Localizable.stringsdict
index a81b01477e..29861f0d23 100644
--- a/platform/ios/resources/uk.lproj/Localizable.stringsdict
+++ b/platform/ios/resources/uk.lproj/Localizable.stringsdict
@@ -2,24 +2,28 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
- <key>MAP_A11Y_VALUE</key>
+ <key>MAP_A11Y_VALUE_ANNOTATIONS</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
- <string>%#@level@
-%#@count@</string>
- <key>level</key>
+ <string>%#@count@</string>
+ <key>count</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
- <string>d</string>
+ <string>ld</string>
<key>one</key>
- <string>Масштаб %dx</string>
+ <string>Показано %d підпис</string>
<key>few</key>
- <string>Масштаб %dx</string>
+ <string>Показано %d підписи</string>
<key>other</key>
- <string>Масштаб %dx</string>
+ <string>Показано %d підписів</string>
</dict>
+ </dict>
+ <key>MAP_A11Y_VALUE_ROADS</key>
+ <dict>
+ <key>NSStringLocalizedFormatKey</key>
+ <string>%#@count@</string>
<key>count</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
@@ -27,11 +31,29 @@
<key>NSStringFormatValueTypeKey</key>
<string>ld</string>
<key>one</key>
- <string>Показано %d підпис</string>
+ <string>%ld дорігу видно</string>
<key>few</key>
- <string>Показано %d підписи</string>
+ <string>%ld доріги видно</string>
+ <key>other</key>
+ <string>%ld доріг видно</string>
+ </dict>
+ </dict>
+ <key>MAP_A11Y_VALUE_ZOOM</key>
+ <dict>
+ <key>NSStringLocalizedFormatKey</key>
+ <string>%#@level@</string>
+ <key>level</key>
+ <dict>
+ <key>NSStringFormatSpecTypeKey</key>
+ <string>NSStringPluralRuleType</string>
+ <key>NSStringFormatValueTypeKey</key>
+ <string>d</string>
+ <key>one</key>
+ <string>Масштаб %dx</string>
+ <key>few</key>
+ <string>Масштаб %dx</string>
<key>other</key>
- <string>Показано %d підпис(ів)</string>
+ <string>Масштаб %dx</string>
</dict>
</dict>
</dict>
diff --git a/platform/ios/resources/vi.lproj/Localizable.strings b/platform/ios/resources/vi.lproj/Localizable.strings
index 762a2b2ddb..06242a39e7 100644
--- a/platform/ios/resources/vi.lproj/Localizable.strings
+++ b/platform/ios/resources/vi.lproj/Localizable.strings
Binary files differ
diff --git a/platform/ios/scripts/package.sh b/platform/ios/scripts/package.sh
index 31285182a7..6df1b687b4 100755
--- a/platform/ios/scripts/package.sh
+++ b/platform/ios/scripts/package.sh
@@ -70,7 +70,7 @@ xcodebuild \
CURRENT_SHORT_VERSION=${SHORT_VERSION} \
CURRENT_SEMANTIC_VERSION=${SEM_VERSION} \
CURRENT_COMMIT_HASH=${HASH} \
- ONLY_ACTIVE_ARCH=NO \
+ ARCHS="x86_64" \
-derivedDataPath ${DERIVED_DATA} \
-workspace ./platform/ios/ios.xcworkspace \
-scheme ${SCHEME} \
@@ -194,11 +194,6 @@ if [[ ${BUILD_DYNAMIC} == true && ${BUILDTYPE} == Release ]]; then
validate_dsym \
"${OUTPUT}/dynamic/${NAME}.framework.dSYM/Contents/Resources/DWARF/${NAME}" \
"${OUTPUT}/dynamic/${NAME}.framework/${NAME}"
-
- # To-do: remove this in 4.0.0, as we intend to remove support for i386 entirely.
- step "Removing i386 slice from dSYM"
- lipo -remove i386 "${OUTPUT}/dynamic/${NAME}.framework.dSYM/Contents/Resources/DWARF/${NAME}" -o "${OUTPUT}/dynamic/${NAME}.framework.dSYM/Contents/Resources/DWARF/${NAME}"
- lipo -info "${OUTPUT}/dynamic/${NAME}.framework.dSYM/Contents/Resources/DWARF/${NAME}"
fi
function create_podspec {
diff --git a/platform/ios/src/MGLAnnotationView.h b/platform/ios/src/MGLAnnotationView.h
index 4fa0f196ab..8006ec3e40 100644
--- a/platform/ios/src/MGLAnnotationView.h
+++ b/platform/ios/src/MGLAnnotationView.h
@@ -171,6 +171,10 @@ MGL_EXPORT
The default value of this property is `YES`. Set this property to `NO` if the
view’s legibility is important.
+
+ @note Scaling many on-screen annotation views can contribute to poor map
+ performance. Consider disabling this property if your use case involves
+ hundreds or thousands of annotation views.
*/
@property (nonatomic, assign) BOOL scalesWithViewingDistance;
diff --git a/platform/ios/src/MGLAnnotationView.mm b/platform/ios/src/MGLAnnotationView.mm
index 94d0649413..4585479582 100644
--- a/platform/ios/src/MGLAnnotationView.mm
+++ b/platform/ios/src/MGLAnnotationView.mm
@@ -12,6 +12,7 @@
@property (nonatomic, readwrite, nullable) NSString *reuseIdentifier;
@property (nonatomic, readwrite) CATransform3D lastAppliedScaleTransform;
@property (nonatomic, readwrite) CATransform3D lastAppliedRotateTransform;
+@property (nonatomic, readwrite) CGFloat lastPitch;
@property (nonatomic, weak) UIPanGestureRecognizer *panGestureRecognizer;
@property (nonatomic, weak) UILongPressGestureRecognizer *longPressRecognizer;
@property (nonatomic, weak) MGLMapView *mapView;
@@ -137,12 +138,18 @@
// along the y axis of its superview.
CGFloat maxScaleReduction = 1.0 - self.center.y / superviewHeight;
+ // Since it is possible for the map view to report a pitch less than 0 due to the nature of
+ // how the gesture information is captured, the value is guarded with MAX.
+ CGFloat pitch = MAX(self.mapView.camera.pitch, 0);
+
+ // Return early if the map view currently has no pitch and was not previously pitched.
+ if (!pitch && !_lastPitch) return;
+ _lastPitch = pitch;
+
// The pitch intensity represents how much the map view is actually pitched compared to
// what is possible. The value will range from 0% (not pitched at all) to 100% (pitched as much
// as the map view will allow). The map view's maximum pitch is defined in `mbgl::util::PITCH_MAX`.
- // Since it is possible for the map view to report a pitch less than 0 due to the nature of
- // how the gesture information is captured, the value is guarded with MAX.
- CGFloat pitchIntensity = MAX(self.mapView.camera.pitch, 0) / MGLDegreesFromRadians(mbgl::util::PITCH_MAX);
+ CGFloat pitchIntensity = pitch / MGLDegreesFromRadians(mbgl::util::PITCH_MAX);
// The pitch adjusted scale is the inverse proportion of the maximum possible scale reduction
// multiplied by the pitch intensity. For example, if the maximum scale reduction is 75% and the
diff --git a/platform/ios/src/MGLMapAccessibilityElement.mm b/platform/ios/src/MGLMapAccessibilityElement.mm
index 4e5f165fbf..79dcda4054 100644
--- a/platform/ios/src/MGLMapAccessibilityElement.mm
+++ b/platform/ios/src/MGLMapAccessibilityElement.mm
@@ -2,10 +2,13 @@
#import "MGLDistanceFormatter.h"
#import "MGLCompassDirectionFormatter.h"
#import "MGLFeature.h"
-#import "MGLVectorSource+MGLAdditions.h"
-#import "NSBundle+MGLAdditions.h"
#import "MGLGeometry_Private.h"
+#import "MGLVectorSource_Private.h"
+
+#import "NSBundle+MGLAdditions.h"
+#import "NSOrthography+MGLAdditions.h"
+#import "NSString+MGLAdditions.h"
@implementation MGLMapAccessibilityElement
@@ -48,24 +51,16 @@
NSString *languageCode = [MGLVectorSource preferredMapboxStreetsLanguage];
NSString *nameAttribute = [NSString stringWithFormat:@"name_%@", languageCode];
NSString *name = [feature attributeForKey:nameAttribute];
-
+
// If a feature hasn’t been translated into the preferred language, it
// may be in the local language, which may be written in another script.
- // Romanize it.
- NSLocale *locale = [NSLocale localeWithLocaleIdentifier:languageCode];
- NSOrthography *orthography;
-#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunguarded-availability-new"
- if ([NSOrthography respondsToSelector:@selector(defaultOrthographyForLanguage:)]) {
- orthography = [NSOrthography defaultOrthographyForLanguage:locale.localeIdentifier];
- }
-#pragma clang diagnostic pop
-#endif
- if ([orthography.dominantScript isEqualToString:@"Latn"]) {
- name = [name stringByApplyingTransform:NSStringTransformToLatin reverse:NO];
+ // Attempt to transform to the script of the preferred language, keeping
+ // the original string if no transform exists or if transformation fails.
+ if (@available(iOS 9.0, *)) {
+ NSString *dominantScript = [NSOrthography mgl_dominantScriptForMapboxStreetsLanguage:languageCode];
+ name = [name mgl_stringByTransliteratingIntoScript:dominantScript];
}
-
+
self.accessibilityLabel = name;
}
return self;
diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm
index 02f529d094..cad14343a8 100644
--- a/platform/ios/src/MGLMapView.mm
+++ b/platform/ios/src/MGLMapView.mm
@@ -18,6 +18,7 @@
#include <mbgl/style/image.hpp>
#include <mbgl/style/transition_options.hpp>
#include <mbgl/style/layers/custom_layer.hpp>
+#include <mbgl/renderer/mode.hpp>
#include <mbgl/renderer/renderer.hpp>
#include <mbgl/renderer/renderer_backend.hpp>
#include <mbgl/renderer/backend_scope.hpp>
@@ -40,11 +41,11 @@
#import "MGLGeometry_Private.h"
#import "MGLMultiPoint_Private.h"
#import "MGLOfflineStorage_Private.h"
+#import "MGLVectorSource_Private.h"
#import "MGLFoundation_Private.h"
#import "MGLRendererFrontend.h"
#import "MGLRendererConfiguration.h"
-#import "MGLVectorSource+MGLAdditions.h"
#import "NSBundle+MGLAdditions.h"
#import "NSDate+MGLAdditions.h"
#import "NSException+MGLAdditions.h"
@@ -462,6 +463,9 @@ public:
_attributionButtonConstraints = [NSMutableArray array];
[_attributionButton addObserver:self forKeyPath:@"hidden" options:NSKeyValueObservingOptionNew context:NULL];
+ UILongPressGestureRecognizer *attributionLongPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(showAttribution:)];
+ [_attributionButton addGestureRecognizer:attributionLongPress];
+
// setup compass
//
_compassView = [[UIImageView alloc] initWithImage:self.compassImage];
@@ -713,7 +717,7 @@ public:
{
MGLAssertIsMainThread();
- _rendererFrontend->onLowMemory();
+ _rendererFrontend->reduceMemoryUse();
}
#pragma mark - Layout -
@@ -1155,6 +1159,13 @@ public:
- (void)sleepGL:(__unused NSNotification *)notification
{
MGLAssertIsMainThread();
+
+ // Ideally we would wait until we actually received a memory warning but the bulk of the memory
+ // we have to release is tied up in GL buffers that we can't touch once we're in the background.
+ // Compromise position: release everything but currently rendering tiles
+ // A possible improvement would be to store a copy of the GL buffers that we could use to rapidly
+ // restart, but that we could also discard in response to a memory warning.
+ _rendererFrontend->reduceMemoryUse();
if ( ! self.dormant)
{
@@ -2012,13 +2023,28 @@ public:
#pragma mark - Attribution -
-- (void)showAttribution:(__unused id)sender
+- (void)showAttribution:(id)sender
{
+ BOOL shouldShowVersion = [sender isKindOfClass:[UILongPressGestureRecognizer class]];
+ if (shouldShowVersion)
+ {
+ UILongPressGestureRecognizer *longPress = (UILongPressGestureRecognizer *)sender;
+ if (longPress.state != UIGestureRecognizerStateBegan)
+ {
+ return;
+ }
+ }
+
NSString *title = NSLocalizedStringWithDefaultValue(@"SDK_NAME", nil, nil, @"Mapbox Maps SDK for iOS", @"Action sheet title");
UIAlertController *attributionController = [UIAlertController alertControllerWithTitle:title
message:nil
preferredStyle:UIAlertControllerStyleActionSheet];
-
+
+ if (shouldShowVersion)
+ {
+ attributionController.title = [title stringByAppendingFormat:@" %@", [NSBundle mgl_frameworkInfoDictionary][@"MGLSemanticVersionString"]];
+ }
+
NSArray *attributionInfos = [self.style attributionInfosWithFontSize:[UIFont buttonFontSize]
linkColor:nil];
for (MGLAttributionInfo *info in attributionInfos)
@@ -2297,7 +2323,7 @@ public:
- (void)emptyMemoryCache
{
- _rendererFrontend->onLowMemory();
+ _rendererFrontend->reduceMemoryUse();
}
- (void)setZoomEnabled:(BOOL)zoomEnabled
@@ -6124,7 +6150,7 @@ public:
[nativeView didFinishLoadingStyle];
}
- mbgl::gl::ProcAddress initializeExtension(const char* name) override {
+ mbgl::gl::ProcAddress getExtensionFunctionPointer(const char* name) override {
static CFBundleRef framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengles"));
if (!framework) {
throw std::runtime_error("Failed to load OpenGL framework.");
diff --git a/platform/ios/src/Mapbox.h b/platform/ios/src/Mapbox.h
index 9b2c472cf6..11720ac68e 100644
--- a/platform/ios/src/Mapbox.h
+++ b/platform/ios/src/Mapbox.h
@@ -45,13 +45,18 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[];
#import "MGLSymbolStyleLayer.h"
#import "MGLRasterStyleLayer.h"
#import "MGLCircleStyleLayer.h"
+#import "MGLHeatmapStyleLayer.h"
+#import "MGLHillshadeStyleLayer.h"
#import "MGLBackgroundStyleLayer.h"
#import "MGLOpenGLStyleLayer.h"
#import "MGLSource.h"
#import "MGLTileSource.h"
#import "MGLVectorSource.h"
#import "MGLShapeSource.h"
+#import "MGLAbstractShapeSource.h"
+#import "MGLComputedShapeSource.h"
#import "MGLRasterSource.h"
+#import "MGLRasterDEMSource.h"
#import "MGLImageSource.h"
#import "MGLTilePyramidOfflineRegion.h"
#import "MGLTypes.h"
@@ -61,3 +66,4 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[];
#import "MGLStyleValue.h"
#import "MGLAttributionInfo.h"
#import "MGLMapSnapshotter.h"
+#import "NSExpression+MGLAdditions.h"
diff --git a/platform/ios/src/NSOrthography+MGLAdditions.h b/platform/ios/src/NSOrthography+MGLAdditions.h
new file mode 100644
index 0000000000..a552fc7774
--- /dev/null
+++ b/platform/ios/src/NSOrthography+MGLAdditions.h
@@ -0,0 +1,18 @@
+#import <Foundation/Foundation.h>
+
+@interface NSOrthography (NSOrthography_MGLAdditions)
+
+/**
+ Returns a four-letter ISO 15924 code representing the name of the dominant
+ script for a given language.
+
+ On iOS 11 or newer, this method wraps
+ `+[NSOrthography defaultOrthographyForLanguage:]` and supports any language.
+ On iOS 10 and older, this method only returns values for Mapbox
+ Streets-supported languages.
+
+ @param language The ISO-639 code representing a language.
+ */
++ (NSString *)mgl_dominantScriptForMapboxStreetsLanguage:(NSString *)language;
+
+@end
diff --git a/platform/ios/src/NSOrthography+MGLAdditions.m b/platform/ios/src/NSOrthography+MGLAdditions.m
new file mode 100644
index 0000000000..99258862cc
--- /dev/null
+++ b/platform/ios/src/NSOrthography+MGLAdditions.m
@@ -0,0 +1,31 @@
+#import "NSOrthography+MGLAdditions.h"
+
+@implementation NSOrthography (MGLAdditions)
+
++ (NSString *)mgl_dominantScriptForMapboxStreetsLanguage:(NSString *)language {
+ if (@available(iOS 11.0, *)) {
+ NSLocale *locale = [NSLocale localeWithLocaleIdentifier:language];
+ NSOrthography *orthography = [NSOrthography defaultOrthographyForLanguage:locale.localeIdentifier];
+
+ return orthography.dominantScript;
+ }
+
+ // Manually map Mapbox Streets languages to ISO 15924 script codes.
+ NSSet *latinLanguages = [NSSet setWithObjects:@"de", @"en", @"es", @"fr", @"pt", nil];
+ NSSet *hansLanguages = [NSSet setWithObjects:@"zh", @"zh-Hans", nil];
+
+ if ([latinLanguages containsObject:language]) {
+ return @"Latn";
+ } else if ([hansLanguages containsObject:language]) {
+ return @"Hans";
+ } else if ([language isEqualToString:@"ru"]) {
+ return @"Cyrl";
+ } else if ([language isEqualToString:@"ar"]) {
+ return @"Arab";
+ } else {
+ // Code for undetermined script
+ return @"Zyyy";
+ }
+}
+
+@end
diff --git a/platform/ios/src/UIImage+MGLAdditions.mm b/platform/ios/src/UIImage+MGLAdditions.mm
index 8ab1d5c259..884f92e003 100644
--- a/platform/ios/src/UIImage+MGLAdditions.mm
+++ b/platform/ios/src/UIImage+MGLAdditions.mm
@@ -6,7 +6,7 @@
- (nullable instancetype)initWithMGLStyleImage:(const mbgl::style::Image *)styleImage
{
- CGImageRef image = CGImageFromMGLPremultipliedImage(styleImage->getImage().clone());
+ CGImageRef image = CGImageCreateWithMGLPremultipliedImage(styleImage->getImage().clone());
if (!image) {
return nil;
}
@@ -24,7 +24,7 @@
- (nullable instancetype)initWithMGLPremultipliedImage:(const mbgl::PremultipliedImage&&)mbglImage scale:(CGFloat)scale
{
- CGImageRef image = CGImageFromMGLPremultipliedImage(mbglImage.clone());
+ CGImageRef image = CGImageCreateWithMGLPremultipliedImage(mbglImage.clone());
if (!image) {
return nil;
}
diff --git a/platform/ios/test/MGLMapAccessibilityElementTests.m b/platform/ios/test/MGLMapAccessibilityElementTests.m
index 5c79d85de1..89a595421e 100644
--- a/platform/ios/test/MGLMapAccessibilityElementTests.m
+++ b/platform/ios/test/MGLMapAccessibilityElementTests.m
@@ -19,13 +19,17 @@
};
MGLFeatureAccessibilityElement *element = [[MGLFeatureAccessibilityElement alloc] initWithAccessibilityContainer:self feature:feature];
XCTAssertEqualObjects(element.accessibilityLabel, @"English", @"Accessibility label should be localized.");
-
+
feature.attributes = @{
@"name": @"Цинциннати",
@"name_en": @"Цинциннати",
};
element = [[MGLFeatureAccessibilityElement alloc] initWithAccessibilityContainer:self feature:feature];
- XCTAssertEqualObjects(element.accessibilityLabel, @"Cincinnati", @"Accessibility label should be romanized.");
+ if (@available(iOS 9.0, *)) {
+ XCTAssertEqualObjects(element.accessibilityLabel, @"Cincinnati", @"Accessibility label should be romanized.");
+ } else {
+ XCTAssertEqualObjects(element.accessibilityLabel, @"Цинциннати", @"Accessibility label should not be romanized.");
+ }
}
- (void)testPlaceFeatureValues {
diff --git a/platform/ios/test/MGLMapViewLayoutTests.m b/platform/ios/test/MGLMapViewLayoutTests.m
index a41e7695f9..e707cfdb41 100644
--- a/platform/ios/test/MGLMapViewLayoutTests.m
+++ b/platform/ios/test/MGLMapViewLayoutTests.m
@@ -67,7 +67,7 @@
CGFloat bottomSafeAreaInset = 0.0;
double accuracy = 0.01;
- if ( [self.mapView respondsToSelector:@selector(safeAreaInsets)] ) {
+ if (@available(iOS 11.0, *)) {
bottomSafeAreaInset = self.mapView.safeAreaInsets.bottom;
}
diff --git a/platform/ios/test/MGLNSOrthographyAdditionsTests.m b/platform/ios/test/MGLNSOrthographyAdditionsTests.m
new file mode 100644
index 0000000000..351fe4227e
--- /dev/null
+++ b/platform/ios/test/MGLNSOrthographyAdditionsTests.m
@@ -0,0 +1,19 @@
+#import <XCTest/XCTest.h>
+
+#import "NSOrthography+MGLAdditions.h"
+#import "MGLVectorSource_Private.h"
+
+@interface MGLNSOrthographyAdditionsTests : XCTestCase
+
+@end
+
+@implementation MGLNSOrthographyAdditionsTests
+
+- (void)testStreetsLanguages {
+ for (NSString *language in [MGLVectorSource mapboxStreetsLanguages]) {
+ NSString *dominantScript = [NSOrthography mgl_dominantScriptForMapboxStreetsLanguage:language];
+ XCTAssertNotEqualObjects(dominantScript, @"Zyyy", @"Mapbox Streets languages should have dominant script");
+ }
+}
+
+@end
diff --git a/platform/ios/uitest/LaunchScreen.xib b/platform/ios/uitest/LaunchScreen.xib
index c0a15ddb13..dd23975342 100644
--- a/platform/ios/uitest/LaunchScreen.xib
+++ b/platform/ios/uitest/LaunchScreen.xib
@@ -12,7 +12,7 @@
<rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
- <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="© 2015–2017 Mapbox. All rights reserved." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="8ie-xW-0ye">
+ <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="© 2015–2018 Mapbox. All rights reserved." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="8ie-xW-0ye">
<rect key="frame" x="20" y="439" width="441" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
diff --git a/platform/ios/uitest/ios-tests.xcodeproj/xcshareddata/xcschemes/Mapbox GL Tests.xcscheme b/platform/ios/uitest/ios-tests.xcodeproj/xcshareddata/xcschemes/Mapbox GL Tests.xcscheme
index e941b6daf9..b1a1db6ba1 100644
--- a/platform/ios/uitest/ios-tests.xcodeproj/xcshareddata/xcschemes/Mapbox GL Tests.xcscheme
+++ b/platform/ios/uitest/ios-tests.xcodeproj/xcshareddata/xcschemes/Mapbox GL Tests.xcscheme
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "0820"
+ LastUpgradeVersion = "0910"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
@@ -40,6 +40,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
@@ -90,6 +91,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
diff --git a/platform/linux/config.cmake b/platform/linux/config.cmake
index fddcf90278..dd5f0af112 100644
--- a/platform/linux/config.cmake
+++ b/platform/linux/config.cmake
@@ -1,6 +1,5 @@
mason_use(glfw VERSION 2017-07-13-67c9155)
mason_use(mesa VERSION 13.0.4)
-mason_use(boost_libprogram_options VERSION 1.62.0${MASON_CXXABI_SUFFIX})
mason_use(sqlite VERSION 3.14.2)
mason_use(libuv VERSION 1.9.1)
mason_use(nunicode VERSION 1.7.1)
@@ -10,6 +9,7 @@ mason_use(webp VERSION 0.5.1)
mason_use(gtest VERSION 1.8.0${MASON_CXXABI_SUFFIX})
mason_use(benchmark VERSION 1.2.0)
mason_use(icu VERSION 58.1-min-size)
+mason_use(args VERSION 6.2.0 HEADER_ONLY)
include(cmake/loop-uv.cmake)
@@ -19,7 +19,6 @@ macro(mbgl_platform_core)
if(WITH_OSMESA)
target_sources(mbgl-core
PRIVATE platform/default/headless_backend_osmesa.cpp
- PRIVATE platform/default/mbgl/gl/headless_display.cpp
)
target_link_libraries(mbgl-core
PUBLIC -lOSMesa
@@ -27,17 +26,14 @@ macro(mbgl_platform_core)
elseif(WITH_EGL)
target_sources(mbgl-core
PRIVATE platform/linux/src/headless_backend_egl.cpp
- PRIVATE platform/linux/src/headless_display_egl.cpp
)
target_link_libraries(mbgl-core
PUBLIC -lGLESv2
PUBLIC -lEGL
- PUBLIC -lgbm
)
else()
target_sources(mbgl-core
PRIVATE platform/linux/src/headless_backend_glx.cpp
- PRIVATE platform/linux/src/headless_display_glx.cpp
)
target_link_libraries(mbgl-core
PUBLIC -lGL
@@ -67,7 +63,6 @@ macro(mbgl_platform_core)
PRIVATE platform/default/mbgl/gl/headless_frontend.hpp
PRIVATE platform/default/mbgl/gl/headless_backend.cpp
PRIVATE platform/default/mbgl/gl/headless_backend.hpp
- PRIVATE platform/default/mbgl/gl/headless_display.hpp
# Thread pool
PRIVATE platform/default/mbgl/util/default_thread_pool.cpp
@@ -77,6 +72,7 @@ macro(mbgl_platform_core)
target_include_directories(mbgl-core
PRIVATE platform/default
+ PRIVATE platform/linux
)
target_add_mason_package(mbgl-core PUBLIC nunicode)
@@ -119,7 +115,7 @@ macro(mbgl_platform_glfw)
add_custom_command(
TARGET mbgl-glfw POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
- ${CMAKE_SOURCE_DIR}/common/ca-bundle.crt
+ ${CMAKE_SOURCE_DIR}/misc/ca-bundle.crt
${CMAKE_CURRENT_BINARY_DIR}/ca-bundle.crt
)
endmacro()
@@ -150,6 +146,10 @@ macro(mbgl_platform_test)
PRIVATE platform/default/mbgl/test/main.cpp
)
+ target_include_directories(mbgl-test
+ PRIVATE platform/linux
+ )
+
set_source_files_properties(
platform/default/mbgl/test/main.cpp
PROPERTIES
diff --git a/platform/linux/mbgl/gl/gl_impl.hpp b/platform/linux/mbgl/gl/gl_impl.hpp
new file mode 100644
index 0000000000..ae829f3f7e
--- /dev/null
+++ b/platform/linux/mbgl/gl/gl_impl.hpp
@@ -0,0 +1,11 @@
+#pragma once
+
+#if MBGL_USE_GLES2
+ #define GL_GLEXT_PROTOTYPES
+ #include <GLES2/gl2.h>
+ #include <GLES2/gl2ext.h>
+#else
+ #define GL_GLEXT_PROTOTYPES
+ #include <GL/gl.h>
+ #include <GL/glext.h>
+#endif
diff --git a/platform/linux/src/headless_backend_egl.cpp b/platform/linux/src/headless_backend_egl.cpp
index 0784173af7..089e344987 100644
--- a/platform/linux/src/headless_backend_egl.cpp
+++ b/platform/linux/src/headless_backend_egl.cpp
@@ -1,5 +1,4 @@
#include <mbgl/gl/headless_backend.hpp>
-#include <mbgl/gl/headless_display.hpp>
#include <mbgl/util/logging.hpp>
@@ -9,11 +8,83 @@
namespace mbgl {
-struct EGLImpl : public HeadlessBackend::Impl {
- EGLImpl(EGLContext glContext_, EGLDisplay display_, EGLConfig config_)
- : glContext(glContext_),
- display(display_),
- config(config_) {
+// This class provides a singleton that contains information about the configuration used for
+// instantiating new headless rendering contexts.
+class EGLDisplayConfig {
+private:
+ // Key for singleton construction.
+ struct Key { explicit Key() = default; };
+
+public:
+ EGLDisplayConfig(Key) {
+ display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (display == EGL_NO_DISPLAY) {
+ throw std::runtime_error("Failed to obtain a valid EGL display.\n");
+ }
+
+ EGLint major, minor, numConfigs;
+ if (!eglInitialize(display, &major, &minor)) {
+ throw std::runtime_error("eglInitialize() failed.\n");
+ }
+
+ if (!eglBindAPI(EGL_OPENGL_ES_API)) {
+ mbgl::Log::Error(mbgl::Event::OpenGL, "eglBindAPI(EGL_OPENGL_ES_API) returned error %d",
+ eglGetError());
+ throw std::runtime_error("eglBindAPI() failed");
+ }
+
+ const EGLint attribs[] = {
+#if MBGL_USE_GLES2
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+#endif
+ EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+ EGL_NONE
+ };
+
+ // Note: we're choosing an arbitrary pixel format, since we're not using the default surface
+ // anyway; all rendering will be directed to framebuffers which have their own configuration.
+ if (!eglChooseConfig(display, attribs, &config, 1, &numConfigs) || numConfigs != 1) {
+ throw std::runtime_error("Failed to choose ARGB config.\n");
+ }
+ }
+
+ ~EGLDisplayConfig() {
+ eglTerminate(display);
+ }
+
+ static std::shared_ptr<const EGLDisplayConfig> create() {
+ static std::weak_ptr<const EGLDisplayConfig> instance;
+ auto shared = instance.lock();
+ if (!shared) {
+ instance = shared = std::make_shared<EGLDisplayConfig>(Key{});
+ }
+ return shared;
+ }
+
+public:
+ EGLDisplay display = EGL_NO_DISPLAY;
+ EGLConfig config = 0;
+};
+
+class EGLBackendImpl : public HeadlessBackend::Impl {
+public:
+ EGLBackendImpl() {
+ // EGL initializes the context client version to 1 by default. We want to
+ // use OpenGL ES 2.0 which has the ability to create shader and program
+ // objects and also to write vertex and fragment shaders in the OpenGL ES
+ // Shading Language.
+ const EGLint attribs[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL_NONE
+ };
+
+ eglContext = eglCreateContext(eglDisplay->display, eglDisplay->config, EGL_NO_CONTEXT, attribs);
+ if (eglContext == EGL_NO_CONTEXT) {
+ mbgl::Log::Error(mbgl::Event::OpenGL, "eglCreateContext() returned error 0x%04x",
+ eglGetError());
+ throw std::runtime_error("Error creating the EGL context object.\n");
+ }
+
// Create a dummy pbuffer. We will render to framebuffers anyway, but we need a pbuffer to
// activate the context.
// Note that to be able to create pbuffer surfaces, we need to choose a config that
@@ -25,77 +96,49 @@ struct EGLImpl : public HeadlessBackend::Impl {
EGL_NONE
};
- glSurface = eglCreatePbufferSurface(display, config, surfAttribs);
- if (glSurface == EGL_NO_SURFACE) {
+ eglSurface = eglCreatePbufferSurface(eglDisplay->display, eglDisplay->config, surfAttribs);
+ if (eglSurface == EGL_NO_SURFACE) {
throw std::runtime_error("Could not create surface: " + std::to_string(eglGetError()));
}
}
- ~EGLImpl() {
- if (glSurface != EGL_NO_SURFACE) {
- if (!eglDestroySurface(display, glSurface)) {
- throw std::runtime_error("Failed to destroy EGL surface.\n");
+ ~EGLBackendImpl() final {
+ if (eglSurface != EGL_NO_SURFACE) {
+ if (!eglDestroySurface(eglDisplay->display, eglSurface)) {
+ Log::Error(Event::OpenGL, "Failed to destroy EGL surface.");
}
- glSurface = EGL_NO_SURFACE;
+ eglSurface = EGL_NO_SURFACE;
}
- if (!eglDestroyContext(display, glContext)) {
- throw std::runtime_error("Failed to destroy EGL context.\n");
+ if (!eglDestroyContext(eglDisplay->display, eglContext)) {
+ Log::Error(Event::OpenGL, "Failed to destroy EGL context.");
}
}
+ gl::ProcAddress getExtensionFunctionPointer(const char* name) final {
+ return eglGetProcAddress(name);
+ }
+
void activateContext() final {
- if (!eglMakeCurrent(display, glSurface, glSurface, glContext)) {
+ if (!eglMakeCurrent(eglDisplay->display, eglSurface, eglSurface, eglContext)) {
throw std::runtime_error("Switching OpenGL context failed.\n");
}
}
void deactivateContext() final {
- if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
+ if (!eglMakeCurrent(eglDisplay->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
throw std::runtime_error("Removing OpenGL context failed.\n");
}
}
- EGLContext glContext = EGL_NO_CONTEXT;
- EGLDisplay display = EGL_NO_DISPLAY;
- EGLConfig config = 0;
- EGLSurface glSurface = EGL_NO_SURFACE;
+private:
+ const std::shared_ptr<const EGLDisplayConfig> eglDisplay = EGLDisplayConfig::create();
+ EGLContext eglContext = EGL_NO_CONTEXT;
+ EGLSurface eglSurface = EGL_NO_SURFACE;
};
-gl::ProcAddress HeadlessBackend::initializeExtension(const char* name) {
- return eglGetProcAddress(name);
-}
-
-bool HeadlessBackend::hasDisplay() {
- if (!display) {
- display = HeadlessDisplay::create();
- }
- return bool(display);
-};
-
-void HeadlessBackend::createContext() {
- assert(!hasContext());
- assert(hasDisplay());
-
- EGLDisplay display_ = display->attribute<EGLDisplay>();
- EGLConfig& config = display->attribute<EGLConfig&>();
-
- // EGL initializes the context client version to 1 by default. We want to
- // use OpenGL ES 2.0 which has the ability to create shader and program
- // objects and also to write vertex and fragment shaders in the OpenGL ES
- // Shading Language.
- const EGLint attribs[] = {
- EGL_CONTEXT_CLIENT_VERSION, 2,
- EGL_NONE
- };
-
- EGLContext glContext = eglCreateContext(display_, config, EGL_NO_CONTEXT, attribs);
- if (glContext == EGL_NO_CONTEXT) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglCreateContext() returned error 0x%04x",
- eglGetError());
- throw std::runtime_error("Error creating the EGL context object.\n");
- }
-
- impl.reset(new EGLImpl(glContext, display_, config));
+void HeadlessBackend::createImpl() {
+ assert(!impl);
+ impl = std::make_unique<EGLBackendImpl>();
}
} // namespace mbgl
diff --git a/platform/linux/src/headless_backend_glx.cpp b/platform/linux/src/headless_backend_glx.cpp
index 0ba7f08630..27af98e70a 100644
--- a/platform/linux/src/headless_backend_glx.cpp
+++ b/platform/linux/src/headless_backend_glx.cpp
@@ -1,5 +1,4 @@
#include <mbgl/gl/headless_backend.hpp>
-#include <mbgl/gl/headless_display.hpp>
#include <mbgl/util/logging.hpp>
@@ -9,79 +8,123 @@
namespace mbgl {
-struct GLXImpl : public HeadlessBackend::Impl {
- GLXImpl(GLXContext glContext_, GLXPbuffer glxPbuffer_, Display* xDisplay_, GLXFBConfig* fbConfigs_)
- : glContext(glContext_),
- glxPbuffer(glxPbuffer_),
- xDisplay(xDisplay_),
- fbConfigs(fbConfigs_) {
- }
+// This class provides a singleton that contains information about the configuration used for
+// instantiating new headless rendering contexts.
+class GLXDisplayConfig {
+private:
+ // Key for singleton construction.
+ struct Key { explicit Key() = default; };
+
+public:
+ GLXDisplayConfig(Key) {
+ if (!XInitThreads()) {
+ throw std::runtime_error("Failed to XInitThreads.");
+ }
- ~GLXImpl() override {
- if (glxPbuffer) {
- glXDestroyPbuffer(xDisplay, glxPbuffer);
+ xDisplay = XOpenDisplay(nullptr);
+ if (xDisplay == nullptr) {
+ throw std::runtime_error("Failed to open X display.");
}
- glXDestroyContext(xDisplay, glContext);
- }
- void activateContext() final {
- if (!glXMakeContextCurrent(xDisplay, glxPbuffer, glxPbuffer, glContext)) {
- throw std::runtime_error("Switching OpenGL context failed.\n");
+ const auto* extensions = reinterpret_cast<const char*>(
+ glXQueryServerString(xDisplay, DefaultScreen(xDisplay), GLX_EXTENSIONS));
+ if (!extensions) {
+ throw std::runtime_error("Cannot read GLX extensions.");
+ }
+ if (!strstr(extensions, "GLX_SGIX_fbconfig")) {
+ throw std::runtime_error("Extension GLX_SGIX_fbconfig was not found.");
+ }
+ if (!strstr(extensions, "GLX_SGIX_pbuffer")) {
+ throw std::runtime_error("Cannot find glXCreateContextAttribsARB.");
}
- }
- void deactivateContext() final {
- if (!glXMakeContextCurrent(xDisplay, 0, 0, nullptr)) {
- throw std::runtime_error("Removing OpenGL context failed.\n");
+ // We're creating a dummy pbuffer anyway that we're not using.
+ static int pixelFormat[] = { GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT, None };
+
+ int configs = 0;
+ fbConfigs = glXChooseFBConfig(xDisplay, DefaultScreen(xDisplay), pixelFormat, &configs);
+ if (fbConfigs == nullptr) {
+ throw std::runtime_error("Failed to glXChooseFBConfig.");
+ }
+ if (configs <= 0) {
+ throw std::runtime_error("No Framebuffer configurations.");
}
}
- GLXContext glContext = nullptr;
- GLXPbuffer glxPbuffer = 0;
+ ~GLXDisplayConfig() {
+ XFree(fbConfigs);
+ XCloseDisplay(xDisplay);
+ }
+
+ static std::shared_ptr<const GLXDisplayConfig> create() {
+ static std::weak_ptr<const GLXDisplayConfig> instance;
+ auto shared = instance.lock();
+ if (!shared) {
+ instance = shared = std::make_shared<GLXDisplayConfig>(Key{});
+ }
+ return shared;
+ }
- // Needed for ImplDeleter.
+public:
Display* xDisplay = nullptr;
GLXFBConfig* fbConfigs = nullptr;
};
-gl::ProcAddress HeadlessBackend::initializeExtension(const char* name) {
- return glXGetProcAddress(reinterpret_cast<const GLubyte*>(name));
-}
+class GLXBackendImpl : public HeadlessBackend::Impl {
+public:
+ GLXBackendImpl() {
+ // Try to create a legacy context.
+ glContext = glXCreateNewContext(glxDisplay->xDisplay, glxDisplay->fbConfigs[0],
+ GLX_RGBA_TYPE, None, True);
+ if (glContext && !glXIsDirect(glxDisplay->xDisplay, glContext)) {
+ Log::Error(Event::OpenGL, "failed to create direct OpenGL Legacy context");
+ glXDestroyContext(glxDisplay->xDisplay, glContext);
+ glContext = nullptr;
+ }
+ if (glContext == nullptr) {
+ throw std::runtime_error("Error creating GL context object.");
+ }
-bool HeadlessBackend::hasDisplay() {
- if (!display) {
- display = HeadlessDisplay::create();
+ // Create a dummy pbuffer. We will render to framebuffers anyway, but we need a pbuffer to
+ // activate the context.
+ int pbufferAttributes[] = { GLX_PBUFFER_WIDTH, 8, GLX_PBUFFER_HEIGHT, 8, None };
+ glxPbuffer =
+ glXCreatePbuffer(glxDisplay->xDisplay, glxDisplay->fbConfigs[0], pbufferAttributes);
}
- return bool(display);
-};
-void HeadlessBackend::createContext() {
- assert(!hasContext());
+ ~GLXBackendImpl() final {
+ if (glxPbuffer) {
+ glXDestroyPbuffer(glxDisplay->xDisplay, glxPbuffer);
+ }
+ glXDestroyContext(glxDisplay->xDisplay, glContext);
+ }
- auto* xDisplay = display->attribute<Display*>();
- auto* fbConfigs = display->attribute<GLXFBConfig*>();
+ gl::ProcAddress getExtensionFunctionPointer(const char* name) final {
+ return glXGetProcAddress(reinterpret_cast<const GLubyte*>(name));
+ }
- // Try to create a legacy context.
- GLXContext glContext = glXCreateNewContext(xDisplay, fbConfigs[0], GLX_RGBA_TYPE, None, True);
- if (glContext && !glXIsDirect(xDisplay, glContext)) {
- Log::Error(Event::OpenGL, "failed to create direct OpenGL Legacy context");
- glXDestroyContext(xDisplay, glContext);
- glContext = nullptr;
+ void activateContext() final {
+ if (!glXMakeContextCurrent(glxDisplay->xDisplay, glxPbuffer, glxPbuffer, glContext)) {
+ throw std::runtime_error("Switching OpenGL context failed.\n");
+ }
}
- if (glContext == nullptr) {
- throw std::runtime_error("Error creating GL context object.");
+
+ void deactivateContext() final {
+ if (!glXMakeContextCurrent(glxDisplay->xDisplay, 0, 0, nullptr)) {
+ throw std::runtime_error("Removing OpenGL context failed.\n");
+ }
}
- // Create a dummy pbuffer. We will render to framebuffers anyway, but we need a pbuffer to
- // activate the context.
- int pbufferAttributes[] = {
- GLX_PBUFFER_WIDTH, 8,
- GLX_PBUFFER_HEIGHT, 8,
- None
- };
- GLXPbuffer glxPbuffer = glXCreatePbuffer(xDisplay, fbConfigs[0], pbufferAttributes);
+private:
+ const std::shared_ptr<const GLXDisplayConfig> glxDisplay = GLXDisplayConfig::create();
+
+ GLXContext glContext = nullptr;
+ GLXPbuffer glxPbuffer = 0;
+};
- impl = std::make_unique<mbgl::GLXImpl>(glContext, glxPbuffer, xDisplay, fbConfigs);
+void HeadlessBackend::createImpl() {
+ assert(!impl);
+ impl = std::make_unique<GLXBackendImpl>();
}
} // namespace mbgl
diff --git a/platform/linux/src/headless_display_egl.cpp b/platform/linux/src/headless_display_egl.cpp
deleted file mode 100644
index b746211924..0000000000
--- a/platform/linux/src/headless_display_egl.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-#include <mbgl/gl/headless_display.hpp>
-#include <mbgl/util/logging.hpp>
-#include <mbgl/util/string.hpp>
-
-#include <EGL/egl.h>
-
-namespace mbgl {
-
-class HeadlessDisplay::Impl {
-public:
- Impl();
- ~Impl();
-
- EGLDisplay display = EGL_NO_DISPLAY;
- EGLConfig config = 0;
-};
-
-HeadlessDisplay::Impl::Impl() {
- display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- if (display == EGL_NO_DISPLAY) {
- throw std::runtime_error("Failed to obtain a valid EGL display.\n");
- }
-
- EGLint major, minor, numConfigs;
- if (!eglInitialize(display, &major, &minor)) {
- throw std::runtime_error("eglInitialize() failed.\n");
- }
-
- if (!eglBindAPI(EGL_OPENGL_ES_API)) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglBindAPI(EGL_OPENGL_ES_API) returned error %d", eglGetError());
- throw std::runtime_error("eglBindAPI() failed");
- }
-
- const EGLint attribs[] = {
-#if MBGL_USE_GLES2
- EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
-#endif
- EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
- EGL_NONE
- };
-
- if (!eglChooseConfig(display, attribs, &config, 1, &numConfigs) || numConfigs != 1) {
- throw std::runtime_error("Failed to choose ARGB config.\n");
- }
-}
-
-HeadlessDisplay::Impl::~Impl() {
- eglTerminate(display);
-}
-
-template <>
-EGLDisplay HeadlessDisplay::attribute() const {
- return impl->display;
-}
-
-template <>
-EGLConfig& HeadlessDisplay::attribute() const {
- return impl->config;
-}
-
-HeadlessDisplay::HeadlessDisplay()
- : impl(std::make_unique<Impl>()) {
-}
-
-HeadlessDisplay::~HeadlessDisplay() {
-}
-
-} // namespace mbgl
diff --git a/platform/linux/src/headless_display_glx.cpp b/platform/linux/src/headless_display_glx.cpp
deleted file mode 100644
index 5dc342154d..0000000000
--- a/platform/linux/src/headless_display_glx.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-#include <mbgl/gl/headless_display.hpp>
-
-#include <GL/glx.h>
-
-#include <cstring>
-#include <stdexcept>
-#include <string>
-
-namespace mbgl {
-
-class HeadlessDisplay::Impl {
-public:
- Impl();
- ~Impl();
-
- Display* xDisplay = nullptr;
- GLXFBConfig* fbConfigs = nullptr;
-};
-
-HeadlessDisplay::Impl::Impl() {
- if (!XInitThreads()) {
- throw std::runtime_error("Failed to XInitThreads.");
- }
-
- xDisplay = XOpenDisplay(nullptr);
- if (xDisplay == nullptr) {
- throw std::runtime_error("Failed to open X display.");
- }
-
- const auto *extensions = reinterpret_cast<const char *>(glXQueryServerString(xDisplay, DefaultScreen(xDisplay), GLX_EXTENSIONS));
- if (!extensions) {
- throw std::runtime_error("Cannot read GLX extensions.");
- }
- if (!strstr(extensions,"GLX_SGIX_fbconfig")) {
- throw std::runtime_error("Extension GLX_SGIX_fbconfig was not found.");
- }
- if (!strstr(extensions, "GLX_SGIX_pbuffer")) {
- throw std::runtime_error("Cannot find glXCreateContextAttribsARB.");
- }
-
- // We're creating a dummy pbuffer anyway that we're not using.
- static int pixelFormat[] = {
- GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT,
- None
- };
-
- int configs = 0;
- fbConfigs = glXChooseFBConfig(xDisplay, DefaultScreen(xDisplay), pixelFormat, &configs);
- if (fbConfigs == nullptr) {
- throw std::runtime_error("Failed to glXChooseFBConfig.");
- }
- if (configs <= 0) {
- throw std::runtime_error("No Framebuffer configurations.");
- }
-}
-
-HeadlessDisplay::Impl::~Impl() {
- XFree(fbConfigs);
- XCloseDisplay(xDisplay);
-}
-
-template <>
-Display* HeadlessDisplay::attribute() const {
- return impl->xDisplay;
-}
-
-template <>
-GLXFBConfig* HeadlessDisplay::attribute() const {
- return impl->fbConfigs;
-}
-
-HeadlessDisplay::HeadlessDisplay()
- : impl(std::make_unique<Impl>()) {
-}
-
-HeadlessDisplay::~HeadlessDisplay() = default;
-
-} // namespace mbgl
diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md
index 620c6f81ed..1f671cb82a 100644
--- a/platform/macos/CHANGELOG.md
+++ b/platform/macos/CHANGELOG.md
@@ -1,5 +1,28 @@
# Changelog for Mapbox Maps SDK for macOS
+## master
+
+### Styles and rendering
+
+* Added support for a new layer type: `MGLHeatmapStyleLayer`, a powerful way to visualize point data distributions using heatmaps, fully customizable through runtime styling. [#11046](https://github.com/mapbox/mapbox-gl-native/pull/11046)
+* The layout and paint properties on subclasses of `MGLStyleLayer` are now of type `NSExpression` instead of `MGLStyleValue`. A new “Predicates and Expressions” guide provides an overview of the supported operators. ([#10726](https://github.com/mapbox/mapbox-gl-native/pull/10726))
+* Added an `MGLComputedShapeSource` class that allows applications to supply vector data to a style layer on a per-tile basis. ([#9983](https://github.com/mapbox/mapbox-gl-native/pull/9983))
+* A style can now display smooth hillshading and customize its appearance at runtime using the `MGLHillshadeStyleLayer` class. Hillshading is based on a rasterized digital elevation model supplied by the `MGLRasterDEMSource` class. ([#10642](https://github.com/mapbox/mapbox-gl-native/pull/10642))
+* Properties such as `MGLSymbolStyleLayer.iconAllowsOverlap` and `MGLSymbolStyleLayer.iconIgnoresPlacement` now account for symbols in other sources. ([#10436](https://github.com/mapbox/mapbox-gl-native/pull/10436))
+* Improved the reliability of collision detection between symbols near the edges of tiles, as well as between symbols when the map is tilted. It is no longer necessary to enable `MGLSymbolStyleLayer.symbolAvoidsEdges` to prevent symbols in adjacent tiles from overlapping with each other. ([#10436](https://github.com/mapbox/mapbox-gl-native/pull/10436))
+* Symbols can fade in and out as the map pans, rotates, or tilts. ([#10436](https://github.com/mapbox/mapbox-gl-native/pull/10436))
+* Added the `MGLTileSourceOptionTileCoordinateBounds` option to create an `MGLTileSource` that only supplies tiles within a specific geographic bounding box. ([#11141](https://github.com/mapbox/mapbox-gl-native/pull/11141))
+* Fixed an issue preventing a dynamically-added `MGLRasterStyleLayer` from drawing until the map pans. ([#10270](https://github.com/mapbox/mapbox-gl-native/pull/10270))
+* Fixed an issue preventing `MGLImageSource`s from drawing on the map when the map is zoomed in and tilted. ([#10677](https://github.com/mapbox/mapbox-gl-native/pull/10677))
+
+### Other changes
+
+* Added a Hebrew localization. ([#10967](https://github.com/mapbox/mapbox-gl-native/pull/10967))
+
+### Snapshots
+
+* Fixed a memory leak that occurred when creating a map snapshot. ([#10585](https://github.com/mapbox/mapbox-gl-native/pull/10585))
+
## v0.6.2
* Added the `MGLTileSourceOptionTileCoordinateBounds` option to create an `MGLTileSource` that only supplies tiles within a specific geographic bounding box. ([#11141](https://github.com/mapbox/mapbox-gl-native/pull/11141))
diff --git a/platform/macos/app/Assets.xcassets/Layers/background.imageset/Contents.json b/platform/macos/app/Assets.xcassets/Layers/background.imageset/Contents.json
index 3d2c878879..7473a40333 100644
--- a/platform/macos/app/Assets.xcassets/Layers/background.imageset/Contents.json
+++ b/platform/macos/app/Assets.xcassets/Layers/background.imageset/Contents.json
@@ -2,7 +2,8 @@
"images" : [
{
"idiom" : "universal",
- "filename" : "background.pdf"
+ "filename" : "background.pdf",
+ "language-direction" : "left-to-right"
}
],
"info" : {
diff --git a/platform/macos/app/Assets.xcassets/Layers/heatmap.imageset/Contents.json b/platform/macos/app/Assets.xcassets/Layers/heatmap.imageset/Contents.json
new file mode 100644
index 0000000000..10226ca23a
--- /dev/null
+++ b/platform/macos/app/Assets.xcassets/Layers/heatmap.imageset/Contents.json
@@ -0,0 +1,16 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "heatmap.pdf",
+ "language-direction" : "left-to-right"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ },
+ "properties" : {
+ "template-rendering-intent" : "template"
+ }
+} \ No newline at end of file
diff --git a/platform/macos/app/Assets.xcassets/Layers/heatmap.imageset/heatmap.pdf b/platform/macos/app/Assets.xcassets/Layers/heatmap.imageset/heatmap.pdf
new file mode 100644
index 0000000000..88195c3735
--- /dev/null
+++ b/platform/macos/app/Assets.xcassets/Layers/heatmap.imageset/heatmap.pdf
Binary files differ
diff --git a/platform/macos/app/Assets.xcassets/Layers/hillshade.imageset/Contents.json b/platform/macos/app/Assets.xcassets/Layers/hillshade.imageset/Contents.json
new file mode 100644
index 0000000000..f9922cad46
--- /dev/null
+++ b/platform/macos/app/Assets.xcassets/Layers/hillshade.imageset/Contents.json
@@ -0,0 +1,16 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "hillshade.pdf",
+ "language-direction" : "left-to-right"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ },
+ "properties" : {
+ "template-rendering-intent" : "template"
+ }
+} \ No newline at end of file
diff --git a/platform/macos/app/Assets.xcassets/Layers/hillshade.imageset/hillshade.pdf b/platform/macos/app/Assets.xcassets/Layers/hillshade.imageset/hillshade.pdf
new file mode 100644
index 0000000000..bf409e708c
--- /dev/null
+++ b/platform/macos/app/Assets.xcassets/Layers/hillshade.imageset/hillshade.pdf
@@ -0,0 +1,70 @@
+%PDF-1.5
+%
+3 0 obj
+<< /Length 4 0 R
+ /Filter /FlateDecode
+>>
+stream
+xeNA
+@ sv} xŃ(Vo"L0# jXˆ6w:!,P㒒hK灴a U18|= _?ݜ{YXS2یݮ*5/ﺠ>ݪ,
+endstream
+endobj
+4 0 obj
+ 145
+endobj
+2 0 obj
+<<
+ /ExtGState <<
+ /a0 << /CA 1 /ca 1 >>
+ /a1 << /CA 0.156863 /ca 0.156863 >>
+ >>
+>>
+endobj
+5 0 obj
+<< /Type /Page
+ /Parent 1 0 R
+ /MediaBox [ 0 0 12 12 ]
+ /Contents 3 0 R
+ /Group <<
+ /Type /Group
+ /S /Transparency
+ /I true
+ /CS /DeviceRGB
+ >>
+ /Resources 2 0 R
+>>
+endobj
+1 0 obj
+<< /Type /Pages
+ /Kids [ 5 0 R ]
+ /Count 1
+>>
+endobj
+6 0 obj
+<< /Creator (cairo 1.14.8 (http://cairographics.org))
+ /Producer (cairo 1.14.8 (http://cairographics.org))
+>>
+endobj
+7 0 obj
+<< /Type /Catalog
+ /Pages 1 0 R
+>>
+endobj
+xref
+0 8
+0000000000 65535 f
+0000000585 00000 n
+0000000259 00000 n
+0000000015 00000 n
+0000000237 00000 n
+0000000373 00000 n
+0000000650 00000 n
+0000000777 00000 n
+trailer
+<< /Size 8
+ /Root 7 0 R
+ /Info 6 0 R
+>>
+startxref
+829
+%%EOF
diff --git a/platform/macos/app/Base.lproj/MainMenu.xib b/platform/macos/app/Base.lproj/MainMenu.xib
index 3243838848..72e9c5a189 100644
--- a/platform/macos/app/Base.lproj/MainMenu.xib
+++ b/platform/macos/app/Base.lproj/MainMenu.xib
@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="12121" systemVersion="16E195" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="13771" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
- <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="12121"/>
+ <deployment identifier="macosx"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="13771"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@@ -554,6 +555,21 @@
<action selector="drawAnimatedAnnotation:" target="-1" id="CYM-WB-s97"/>
</connections>
</menuItem>
+ <menuItem title="Show All Annnotations" keyEquivalent="A" id="yMj-uM-8SN">
+ <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
+ <connections>
+ <action selector="showAllAnnotations:" target="-1" id="ahr-OR-Em2"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Remove All Annotations" id="6rC-68-vk0">
+ <string key="keyEquivalent" base64-UTF8="YES">
+CA
+</string>
+ <connections>
+ <action selector="removeAllAnnotations:" target="-1" id="6v3-0E-LsR"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="cbI-Nd-MAp"/>
<menuItem title="Add Lime Green Layer" id="UWY-vl-t2m">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
@@ -566,18 +582,16 @@
<action selector="addAnimatedImageSource:" target="-1" id="TuN-Pa-hTG"/>
</connections>
</menuItem>
- <menuItem title="Show All Annnotations" keyEquivalent="A" id="yMj-uM-8SN">
- <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
+ <menuItem title="Add Graticule" id="Msk-p2-Lwt">
+ <modifierMask key="keyEquivalentModifierMask"/>
<connections>
- <action selector="showAllAnnotations:" target="-1" id="ahr-OR-Em2"/>
+ <action selector="insertGraticuleLayer:" target="-1" id="LE5-lz-kx4"/>
</connections>
</menuItem>
- <menuItem title="Remove All Annotations" id="6rC-68-vk0">
- <string key="keyEquivalent" base64-UTF8="YES">
-CA
-</string>
+ <menuItem title="Enhance Terrain" id="2ZT-uE-kUR">
+ <modifierMask key="keyEquivalentModifierMask"/>
<connections>
- <action selector="removeAllAnnotations:" target="-1" id="6v3-0E-LsR"/>
+ <action selector="enhanceTerrain:" target="-1" id="TXX-Yv-ZY1"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="wQq-Mx-QY0"/>
@@ -660,7 +674,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="1680" height="1027"/>
+ <rect key="screenRect" x="0.0" y="0.0" width="1280" height="777"/>
<view key="contentView" id="eA4-n3-qPe">
<rect key="frame" x="0.0" y="0.0" width="350" height="84"/>
<autoresizingMask key="autoresizingMask"/>
@@ -736,7 +750,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="1680" height="1027"/>
+ <rect key="screenRect" x="0.0" y="0.0" width="1280" height="777"/>
<view key="contentView" id="8ha-hw-zOD">
<rect key="frame" x="0.0" y="0.0" width="400" height="300"/>
<autoresizingMask key="autoresizingMask"/>
@@ -745,7 +759,7 @@ CA
<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"/>
- <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <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"/>
@@ -766,7 +780,7 @@ CA
<binding destination="dWe-R6-sRz" name="value" keyPath="arrangedObjects.stateImage" id="2wd-1J-TZt"/>
</connections>
</tableColumn>
- <tableColumn editable="NO" width="116" minWidth="40" maxWidth="1000" id="2hD-LN-h0L">
+ <tableColumn identifier="" editable="NO" width="116" minWidth="40" maxWidth="1000" id="2hD-LN-h0L">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Name">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
@@ -786,7 +800,7 @@ CA
</binding>
</connections>
</tableColumn>
- <tableColumn editable="NO" width="50" minWidth="40" maxWidth="1000" id="pkI-c7-xoD">
+ <tableColumn identifier="" editable="NO" width="50" minWidth="40" maxWidth="1000" id="pkI-c7-xoD">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Downloaded Resources">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
@@ -820,7 +834,7 @@ CA
<binding destination="dWe-R6-sRz" name="value" keyPath="arrangedObjects.countOfResourcesExpected" id="mh2-k0-vvB"/>
</connections>
</tableColumn>
- <tableColumn editable="NO" width="50" minWidth="40" maxWidth="1000" id="kCO-Cd-bQt">
+ <tableColumn identifier="" editable="NO" width="50" minWidth="40" maxWidth="1000" id="kCO-Cd-bQt">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Downloaded Tiles">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
@@ -841,7 +855,7 @@ CA
</binding>
</connections>
</tableColumn>
- <tableColumn editable="NO" width="60" minWidth="10" maxWidth="3.4028234663852886e+38" id="WO5-Ci-HgG">
+ <tableColumn identifier="" editable="NO" width="60" minWidth="10" maxWidth="3.4028234663852886e+38" id="WO5-Ci-HgG">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Downloaded Tiles Size">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
diff --git a/platform/macos/app/MapDocument.m b/platform/macos/app/MapDocument.m
index 0df6b10518..7d39f93347 100644
--- a/platform/macos/app/MapDocument.m
+++ b/platform/macos/app/MapDocument.m
@@ -6,7 +6,7 @@
#import "MGLMapsnapshotter.h"
#import "MGLStyle+MBXAdditions.h"
-#import "MGLVectorSource+MGLAdditions.h"
+#import "MGLVectorSource_Private.h"
#import <Mapbox/Mapbox.h>
@@ -50,7 +50,26 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
return flattenedShapes;
}
-@interface MapDocument () <NSWindowDelegate, NSSharingServicePickerDelegate, NSMenuDelegate, NSSplitViewDelegate, MGLMapViewDelegate>
+@interface MGLVectorSource (MBXAdditions)
+
+@property (nonatomic, readonly, getter=isMapboxTerrain) BOOL mapboxTerrain;
+
+@end
+
+@implementation MGLVectorSource (MBXAdditions)
+
+- (BOOL)isMapboxTerrain {
+ NSURL *url = self.configurationURL;
+ if (![url.scheme isEqualToString:@"mapbox"]) {
+ return NO;
+ }
+ NSArray *identifiers = [url.host componentsSeparatedByString:@","];
+ return [identifiers containsObject:@"mapbox.mapbox-terrain-v2"] || [identifiers containsObject:@"mapbox.mapbox-terrain-v1"];
+}
+
+@end
+
+@interface MapDocument () <NSWindowDelegate, NSSharingServicePickerDelegate, NSMenuDelegate, NSSplitViewDelegate, MGLMapViewDelegate, MGLComputedShapeSourceDataSource>
@property (weak) IBOutlet NSArrayController *styleLayersArrayController;
@property (weak) IBOutlet NSTableView *styleLayersTableView;
@@ -698,6 +717,96 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
[self.mapView.style removeLayer:layer];
}
+- (IBAction)insertGraticuleLayer:(id)sender {
+ [self.undoManager registerUndoWithTarget:self handler:^(id _Nonnull target) {
+ [self removeGraticuleLayer:sender];
+ }];
+
+ if (!self.undoManager.isUndoing) {
+ [self.undoManager setActionName:@"Add Graticule"];
+ }
+
+ NSDictionary *sourceOptions = @{
+ MGLShapeSourceOptionMaximumZoomLevel:@14,
+ MGLShapeSourceOptionWrapsCoordinates: @YES,
+ MGLShapeSourceOptionClipsCoordinates: @YES,
+ };
+ MGLComputedShapeSource *source = [[MGLComputedShapeSource alloc] initWithIdentifier:@"graticule"
+ options:sourceOptions];
+
+ source.dataSource = self;
+ [self.mapView.style addSource:source];
+ MGLLineStyleLayer *lineLayer = [[MGLLineStyleLayer alloc] initWithIdentifier:@"graticule.lines"
+ source:source];
+ [self.mapView.style addLayer:lineLayer];
+ MGLSymbolStyleLayer *labelLayer = [[MGLSymbolStyleLayer alloc] initWithIdentifier:@"graticule.labels"
+ source:source];
+ labelLayer.text = [NSExpression expressionWithFormat:@"value"];
+ [self.mapView.style addLayer:labelLayer];
+}
+
+- (IBAction)removeGraticuleLayer:(id)sender {
+ [self.undoManager registerUndoWithTarget:self handler:^(id _Nonnull target) {
+ [self insertGraticuleLayer:sender];
+ }];
+
+ if (!self.undoManager.isUndoing) {
+ [self.undoManager setActionName:@"Delete Graticule"];
+ }
+
+ MGLStyleLayer *layer = [self.mapView.style layerWithIdentifier:@"graticule.lines"];
+ [self.mapView.style removeLayer:layer];
+
+ layer = [self.mapView.style layerWithIdentifier:@"graticule.labels"];
+ [self.mapView.style removeLayer:layer];
+
+ MGLSource *source = [self.mapView.style sourceWithIdentifier:@"graticule"];
+ [self.mapView.style removeSource:source];
+}
+
+- (IBAction)enhanceTerrain:(id)sender {
+ // Find all the identifiers of Mapbox Terrain sources used in the style.
+ NSMutableSet *terrainSourceIdentifiers = [NSMutableSet set];
+ for (MGLVectorSource *source in self.mapView.style.sources) {
+ if (![source isKindOfClass:[MGLVectorSource class]]) {
+ continue;
+ }
+
+ if (source.mapboxTerrain) {
+ [terrainSourceIdentifiers addObject:source.identifier];
+ }
+ }
+
+ // Find and remove all the style layers using those sources.
+ NSUInteger hillshadeIndex = NSNotFound;
+ NSEnumerator *layerEnumerator = self.mapView.style.layers.objectEnumerator;
+ MGLVectorStyleLayer *layer;
+ for (NSUInteger i = 0; (layer = layerEnumerator.nextObject); i++) {
+ if (![layer isKindOfClass:[MGLVectorStyleLayer class]]) {
+ continue;
+ }
+
+ if ([terrainSourceIdentifiers containsObject:layer.sourceIdentifier]
+ && [layer.sourceLayerIdentifier isEqualToString:@"hillshade"]) {
+ hillshadeIndex = i;
+ [self.mapView.style removeLayer:layer];
+ }
+ }
+
+ if (hillshadeIndex == NSNotFound) {
+ return;
+ }
+
+ // Add a Mapbox Terrain-RGB source.
+ NSURL *terrainRGBURL = [NSURL URLWithString:@"mapbox://mapbox.terrain-rgb"];
+ MGLRasterDEMSource *terrainRGBSource = [[MGLRasterDEMSource alloc] initWithIdentifier:@"terrain" configurationURL:terrainRGBURL];
+ [self.mapView.style addSource:terrainRGBSource];
+
+ // Insert a hillshade layer where the Mapbox Terrain–based layers were.
+ MGLHillshadeStyleLayer *hillshadeLayer = [[MGLHillshadeStyleLayer alloc] initWithIdentifier:@"hillshade" source:terrainRGBSource];
+ [self.mapView.style insertLayer:hillshadeLayer atIndex:hillshadeIndex];
+}
+
#pragma mark Offline packs
- (IBAction)addOfflinePack:(id)sender {
@@ -746,16 +855,16 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
self.mapView.style.transition = transition;
MGLStyleLayer *waterLayer = [self.mapView.style layerWithIdentifier:@"water"];
- MGLStyleValue *colorFunction = [MGLStyleValue<NSColor *> valueWithInterpolationMode:MGLInterpolationModeExponential cameraStops:@{
- @0.0: [MGLStyleValue<NSColor *> valueWithRawValue:[NSColor redColor]],
- @10.0: [MGLStyleValue<NSColor *> valueWithRawValue:[NSColor yellowColor]],
- @20.0: [MGLStyleValue<NSColor *> valueWithRawValue:[NSColor blackColor]],
- } options:nil];
+ NSExpression *colorExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{
+ @0.0: [NSColor redColor],
+ @10.0: [NSColor yellowColor],
+ @20.0: [NSColor blackColor],
+ }];
if ([waterLayer respondsToSelector:@selector(fillColor)]) {
- [waterLayer setValue:colorFunction forKey:@"fillColor"];
+ [waterLayer setValue:colorExpression forKey:@"fillColor"];
} else if ([waterLayer respondsToSelector:@selector(lineColor)]) {
- [waterLayer setValue:colorFunction forKey:@"lineColor"];
+ [waterLayer setValue:colorExpression forKey:@"lineColor"];
}
NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"amsterdam" ofType:@"geojson"];
@@ -764,8 +873,8 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
[self.mapView.style addSource:source];
MGLCircleStyleLayer *circleLayer = [[MGLCircleStyleLayer alloc] initWithIdentifier:@"test" source:source];
- circleLayer.circleColor = [MGLStyleValue<NSColor *> valueWithRawValue:[NSColor greenColor]];
- circleLayer.circleRadius = [MGLStyleValue<NSNumber *> valueWithRawValue:[NSNumber numberWithInteger:40]];
+ circleLayer.circleColor = [NSExpression expressionForConstantValue:[NSColor greenColor]];
+ circleLayer.circleRadius = [NSExpression expressionForConstantValue:@40];
// fillLayer.predicate = [NSPredicate predicateWithFormat:@"%K == %@", @"type", @"park"];
[self.mapView.style addLayer:circleLayer];
@@ -777,13 +886,13 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
MGLSymbolStyleLayer *theaterLayer = [[MGLSymbolStyleLayer alloc] initWithIdentifier:@"theaters" source:streetsSource];
theaterLayer.sourceLayerIdentifier = @"poi_label";
theaterLayer.predicate = [NSPredicate predicateWithFormat:@"maki == 'theatre'"];
- theaterLayer.iconImageName = [MGLStyleValue valueWithRawValue:NSImageNameIChatTheaterTemplate];
- theaterLayer.iconScale = [MGLStyleValue valueWithRawValue:@2];
- theaterLayer.iconColor = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeExponential cameraStops:@{
- @16.0: [MGLStyleValue valueWithRawValue:[NSColor redColor]],
- @18.0: [MGLStyleValue valueWithRawValue:[NSColor yellowColor]],
- @20.0: [MGLStyleValue valueWithRawValue:[NSColor blackColor]],
- } options:nil];
+ theaterLayer.iconImageName = [NSExpression expressionForConstantValue:NSImageNameIChatTheaterTemplate];
+ theaterLayer.iconScale = [NSExpression expressionForConstantValue:@2];
+ theaterLayer.iconColor = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{
+ @16.0: [NSColor redColor],
+ @18.0: [NSColor yellowColor],
+ @20.0: [NSColor blackColor],
+ }];
[self.mapView.style addLayer:theaterLayer];
}
@@ -1011,9 +1120,15 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
if (menuItem.action == @selector(insertCustomStyleLayer:)) {
return ![self.mapView.style layerWithIdentifier:@"mbx-custom"];
}
+ if (menuItem.action == @selector(insertGraticuleLayer:)) {
+ return ![self.mapView.style sourceWithIdentifier:@"graticule"];
+ }
if (menuItem.action == @selector(showAllAnnotations:) || menuItem.action == @selector(removeAllAnnotations:)) {
return self.mapView.annotations.count > 0;
}
+ if (menuItem.action == @selector(enhanceTerrain:)) {
+ return YES;
+ }
if (menuItem.action == @selector(startWorldTour:)) {
return !_isTouringWorld;
}
@@ -1185,6 +1300,53 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
return 0.8;
}
+#pragma mark - MGLComputedShapeSourceDataSource
+- (NSArray<id <MGLFeature>>*)featuresInCoordinateBounds:(MGLCoordinateBounds)bounds zoomLevel:(NSUInteger)zoom {
+ double gridSpacing;
+ if(zoom >= 13) {
+ gridSpacing = 0.01;
+ } else if(zoom >= 11) {
+ gridSpacing = 0.05;
+ } else if(zoom == 10) {
+ gridSpacing = .1;
+ } else if(zoom == 9) {
+ gridSpacing = 0.25;
+ } else if(zoom == 8) {
+ gridSpacing = 0.5;
+ } else if (zoom >= 6) {
+ gridSpacing = 1;
+ } else if(zoom == 5) {
+ gridSpacing = 2;
+ } else if(zoom >= 4) {
+ gridSpacing = 5;
+ } else if(zoom == 2) {
+ gridSpacing = 10;
+ } else {
+ gridSpacing = 20;
+ }
+
+ NSMutableArray <id <MGLFeature>> * features = [NSMutableArray array];
+ CLLocationCoordinate2D coords[2];
+
+ for (double y = ceil(bounds.ne.latitude / gridSpacing) * gridSpacing; y >= floor(bounds.sw.latitude / gridSpacing) * gridSpacing; y -= gridSpacing) {
+ coords[0] = CLLocationCoordinate2DMake(y, bounds.sw.longitude);
+ coords[1] = CLLocationCoordinate2DMake(y, bounds.ne.longitude);
+ MGLPolylineFeature *feature = [MGLPolylineFeature polylineWithCoordinates:coords count:2];
+ feature.attributes = @{@"value": @(y)};
+ [features addObject:feature];
+ }
+
+ for (double x = floor(bounds.sw.longitude / gridSpacing) * gridSpacing; x <= ceil(bounds.ne.longitude / gridSpacing) * gridSpacing; x += gridSpacing) {
+ coords[0] = CLLocationCoordinate2DMake(bounds.sw.latitude, x);
+ coords[1] = CLLocationCoordinate2DMake(bounds.ne.latitude, x);
+ MGLPolylineFeature *feature = [MGLPolylineFeature polylineWithCoordinates:coords count:2];
+ feature.attributes = @{@"value": @(x)};
+ [features addObject:feature];
+ }
+
+ return features;
+}
+
@end
@interface ValidatedToolbarItem : NSToolbarItem
diff --git a/platform/macos/app/StyleLayerIconTransformer.m b/platform/macos/app/StyleLayerIconTransformer.m
index ff2b964b87..199de86d8b 100644
--- a/platform/macos/app/StyleLayerIconTransformer.m
+++ b/platform/macos/app/StyleLayerIconTransformer.m
@@ -31,7 +31,13 @@
if ([layer isKindOfClass:[MGLSymbolStyleLayer class]]) {
return [NSImage imageNamed:@"symbol"];
}
-
+ if ([layer isKindOfClass:[MGLHeatmapStyleLayer class]]) {
+ return [NSImage imageNamed:@"heatmap"];
+ }
+ if ([layer isKindOfClass:[MGLHillshadeStyleLayer class]]) {
+ return [NSImage imageNamed:@"hillshade"];
+ }
+
return nil;
}
diff --git a/platform/macos/app/he.lproj/Localizable.strings b/platform/macos/app/he.lproj/Localizable.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/platform/macos/app/he.lproj/Localizable.strings
diff --git a/platform/macos/app/heatmap.json b/platform/macos/app/heatmap.json
new file mode 100644
index 0000000000..6469e57022
--- /dev/null
+++ b/platform/macos/app/heatmap.json
@@ -0,0 +1,809 @@
+{
+ "version": 8,
+ "name": "Basic Heatmap",
+ "center": [
+ 30.49860107152665,
+ 50.459868549177486
+ ],
+ "zoom": 14.033276876197775,
+ "bearing": 0,
+ "pitch": 0,
+ "sources": {
+ "mapbox": {
+ "url": "mapbox://mapbox.mapbox-streets-v7",
+ "type": "vector"
+ }
+ },
+ "sprite": "mapbox://sprites/mourner/cjcgg2bl16cf42snvcbbaf09z",
+ "glyphs": "mapbox://fonts/mourner/{fontstack}/{range}.pbf",
+ "layers": [
+ {
+ "id": "background",
+ "type": "background",
+ "paint": {
+ "background-color": "#dedede"
+ }
+ },
+ {
+ "id": "landuse_overlay_national_park",
+ "type": "fill",
+ "source": "mapbox",
+ "source-layer": "landuse_overlay",
+ "filter": [
+ "==",
+ "class",
+ "national_park"
+ ],
+ "paint": {
+ "fill-color": "#d2edae",
+ "fill-opacity": 0.75
+ }
+ },
+ {
+ "id": "landuse_park",
+ "type": "fill",
+ "source": "mapbox",
+ "source-layer": "landuse",
+ "filter": [
+ "==",
+ "class",
+ "park"
+ ],
+ "paint": {
+ "fill-color": "#d2edae"
+ }
+ },
+ {
+ "id": "waterway",
+ "type": "line",
+ "source": "mapbox",
+ "source-layer": "waterway",
+ "filter": [
+ "all",
+ [
+ "==",
+ "$type",
+ "LineString"
+ ],
+ [
+ "in",
+ "class",
+ "canal",
+ "river"
+ ]
+ ],
+ "paint": {
+ "line-color": "#a0cfdf",
+ "line-width": {
+ "base": 1.4,
+ "stops": [
+ [
+ 8,
+ 0.5
+ ],
+ [
+ 20,
+ 15
+ ]
+ ]
+ }
+ }
+ },
+ {
+ "id": "water",
+ "type": "fill",
+ "source": "mapbox",
+ "source-layer": "water",
+ "paint": {
+ "fill-color": "#a0cfdf"
+ }
+ },
+ {
+ "id": "building",
+ "type": "fill",
+ "source": "mapbox",
+ "source-layer": "building",
+ "paint": {
+ "fill-color": "#d6d6d6"
+ }
+ },
+ {
+ "id": "tunnel_minor",
+ "type": "line",
+ "source": "mapbox",
+ "source-layer": "road",
+ "filter": [
+ "all",
+ [
+ "==",
+ "$type",
+ "LineString"
+ ],
+ [
+ "all",
+ [
+ "==",
+ "structure",
+ "tunnel"
+ ],
+ [
+ "in",
+ "class",
+ "link",
+ "motorway_link",
+ "path",
+ "pedestrian",
+ "service",
+ "street",
+ "street_limited",
+ "track"
+ ]
+ ]
+ ],
+ "layout": {
+ "line-cap": "butt",
+ "line-join": "miter"
+ },
+ "paint": {
+ "line-color": "#efefef",
+ "line-width": {
+ "base": 1.55,
+ "stops": [
+ [
+ 4,
+ 0.25
+ ],
+ [
+ 20,
+ 30
+ ]
+ ]
+ },
+ "line-dasharray": [
+ 0.36,
+ 0.18
+ ]
+ }
+ },
+ {
+ "id": "tunnel_major",
+ "type": "line",
+ "source": "mapbox",
+ "source-layer": "road",
+ "filter": [
+ "all",
+ [
+ "==",
+ "$type",
+ "LineString"
+ ],
+ [
+ "all",
+ [
+ "==",
+ "structure",
+ "tunnel"
+ ],
+ [
+ "in",
+ "class",
+ "motorway",
+ "primary",
+ "secondary",
+ "tertiary",
+ "trunk"
+ ]
+ ]
+ ],
+ "layout": {
+ "line-cap": "butt",
+ "line-join": "miter"
+ },
+ "paint": {
+ "line-color": "#fff",
+ "line-width": {
+ "base": 1.4,
+ "stops": [
+ [
+ 6,
+ 0.5
+ ],
+ [
+ 20,
+ 30
+ ]
+ ]
+ },
+ "line-dasharray": [
+ 0.28,
+ 0.14
+ ]
+ }
+ },
+ {
+ "id": "road_minor",
+ "type": "line",
+ "source": "mapbox",
+ "source-layer": "road",
+ "filter": [
+ "all",
+ [
+ "==",
+ "$type",
+ "LineString"
+ ],
+ [
+ "all",
+ [
+ "in",
+ "class",
+ "link",
+ "motorway_link",
+ "path",
+ "pedestrian",
+ "service",
+ "street",
+ "street_limited",
+ "track"
+ ],
+ [
+ "in",
+ "structure",
+ "ford",
+ "none"
+ ]
+ ]
+ ],
+ "layout": {
+ "line-cap": "round",
+ "line-join": "round"
+ },
+ "paint": {
+ "line-color": "#efefef",
+ "line-width": {
+ "base": 1.55,
+ "stops": [
+ [
+ 4,
+ 0.25
+ ],
+ [
+ 20,
+ 30
+ ]
+ ]
+ }
+ }
+ },
+ {
+ "id": "road_major",
+ "type": "line",
+ "source": "mapbox",
+ "source-layer": "road",
+ "filter": [
+ "all",
+ [
+ "==",
+ "$type",
+ "LineString"
+ ],
+ [
+ "all",
+ [
+ "in",
+ "class",
+ "motorway",
+ "primary",
+ "secondary",
+ "tertiary",
+ "trunk"
+ ],
+ [
+ "in",
+ "structure",
+ "ford",
+ "none"
+ ]
+ ]
+ ],
+ "layout": {
+ "line-cap": "round",
+ "line-join": "round"
+ },
+ "paint": {
+ "line-color": "#fff",
+ "line-width": {
+ "base": 1.4,
+ "stops": [
+ [
+ 6,
+ 0.5
+ ],
+ [
+ 20,
+ 30
+ ]
+ ]
+ }
+ }
+ },
+ {
+ "id": "bridge_minor case",
+ "type": "line",
+ "source": "mapbox",
+ "source-layer": "road",
+ "filter": [
+ "all",
+ [
+ "==",
+ "$type",
+ "LineString"
+ ],
+ [
+ "all",
+ [
+ "==",
+ "structure",
+ "bridge"
+ ],
+ [
+ "in",
+ "class",
+ "link",
+ "motorway_link",
+ "path",
+ "pedestrian",
+ "service",
+ "street",
+ "street_limited",
+ "track"
+ ]
+ ]
+ ],
+ "layout": {
+ "line-cap": "butt",
+ "line-join": "miter"
+ },
+ "paint": {
+ "line-color": "#dedede",
+ "line-width": {
+ "base": 1.6,
+ "stops": [
+ [
+ 12,
+ 0.5
+ ],
+ [
+ 20,
+ 10
+ ]
+ ]
+ },
+ "line-gap-width": {
+ "base": 1.55,
+ "stops": [
+ [
+ 4,
+ 0.25
+ ],
+ [
+ 20,
+ 30
+ ]
+ ]
+ }
+ }
+ },
+ {
+ "id": "bridge_major case",
+ "type": "line",
+ "source": "mapbox",
+ "source-layer": "road",
+ "filter": [
+ "all",
+ [
+ "==",
+ "$type",
+ "LineString"
+ ],
+ [
+ "all",
+ [
+ "==",
+ "structure",
+ "bridge"
+ ],
+ [
+ "in",
+ "class",
+ "motorway",
+ "primary",
+ "secondary",
+ "tertiary",
+ "trunk"
+ ]
+ ]
+ ],
+ "layout": {
+ "line-cap": "butt",
+ "line-join": "miter"
+ },
+ "paint": {
+ "line-color": "#dedede",
+ "line-width": {
+ "base": 1.6,
+ "stops": [
+ [
+ 12,
+ 0.5
+ ],
+ [
+ 20,
+ 10
+ ]
+ ]
+ },
+ "line-gap-width": {
+ "base": 1.55,
+ "stops": [
+ [
+ 4,
+ 0.25
+ ],
+ [
+ 20,
+ 30
+ ]
+ ]
+ }
+ }
+ },
+ {
+ "id": "bridge_minor",
+ "type": "line",
+ "source": "mapbox",
+ "source-layer": "road",
+ "filter": [
+ "all",
+ [
+ "==",
+ "$type",
+ "LineString"
+ ],
+ [
+ "all",
+ [
+ "==",
+ "structure",
+ "bridge"
+ ],
+ [
+ "in",
+ "class",
+ "link",
+ "motorway_link",
+ "path",
+ "pedestrian",
+ "service",
+ "street",
+ "street_limited",
+ "track"
+ ]
+ ]
+ ],
+ "layout": {
+ "line-cap": "round",
+ "line-join": "round"
+ },
+ "paint": {
+ "line-color": "#efefef",
+ "line-width": {
+ "base": 1.55,
+ "stops": [
+ [
+ 4,
+ 0.25
+ ],
+ [
+ 20,
+ 30
+ ]
+ ]
+ }
+ }
+ },
+ {
+ "id": "bridge_major",
+ "type": "line",
+ "source": "mapbox",
+ "source-layer": "road",
+ "filter": [
+ "all",
+ [
+ "==",
+ "$type",
+ "LineString"
+ ],
+ [
+ "all",
+ [
+ "==",
+ "structure",
+ "bridge"
+ ],
+ [
+ "in",
+ "class",
+ "motorway",
+ "primary",
+ "secondary",
+ "tertiary",
+ "trunk"
+ ]
+ ]
+ ],
+ "layout": {
+ "line-cap": "round",
+ "line-join": "round"
+ },
+ "paint": {
+ "line-color": "#fff",
+ "line-width": {
+ "base": 1.4,
+ "stops": [
+ [
+ 6,
+ 0.5
+ ],
+ [
+ 20,
+ 30
+ ]
+ ]
+ }
+ }
+ },
+ {
+ "id": "admin_country",
+ "type": "line",
+ "source": "mapbox",
+ "source-layer": "admin",
+ "filter": [
+ "all",
+ [
+ "==",
+ "$type",
+ "LineString"
+ ],
+ [
+ "all",
+ [
+ "<=",
+ "admin_level",
+ 2
+ ],
+ [
+ "==",
+ "maritime",
+ 0
+ ]
+ ]
+ ],
+ "layout": {
+ "line-cap": "round",
+ "line-join": "round"
+ },
+ "paint": {
+ "line-color": "#8b8a8a",
+ "line-width": {
+ "base": 1.3,
+ "stops": [
+ [
+ 3,
+ 0.5
+ ],
+ [
+ 22,
+ 15
+ ]
+ ]
+ }
+ }
+ },
+ {
+ "id": "road_major_label",
+ "type": "symbol",
+ "source": "mapbox",
+ "source-layer": "road_label",
+ "filter": [
+ "all",
+ [
+ "==",
+ "$type",
+ "LineString"
+ ],
+ [
+ "in",
+ "class",
+ "motorway",
+ "primary",
+ "secondary",
+ "tertiary",
+ "trunk"
+ ]
+ ],
+ "layout": {
+ "symbol-placement": "line",
+ "text-field": "{name_en}",
+ "text-font": [
+ "Open Sans Semibold",
+ "Arial Unicode MS Bold"
+ ],
+ "text-transform": "uppercase",
+ "text-letter-spacing": 0.1,
+ "text-size": {
+ "base": 1.4,
+ "stops": [
+ [
+ 10,
+ 8
+ ],
+ [
+ 20,
+ 14
+ ]
+ ]
+ }
+ },
+ "paint": {
+ "text-color": "#666",
+ "text-halo-color": "rgba(255,255,255,0.75)",
+ "text-halo-width": 2
+ }
+ },
+ {
+ "id": "place_label_other",
+ "type": "symbol",
+ "source": "mapbox",
+ "source-layer": "place_label",
+ "minzoom": 8,
+ "filter": [
+ "all",
+ [
+ "==",
+ "$type",
+ "Point"
+ ],
+ [
+ "in",
+ "type",
+ "hamlet",
+ "island",
+ "neighbourhood",
+ "suburb",
+ "town",
+ "village"
+ ]
+ ],
+ "layout": {
+ "text-field": "{name_en}",
+ "text-font": [
+ "Open Sans Semibold",
+ "Arial Unicode MS Bold"
+ ],
+ "text-max-width": 6,
+ "text-size": {
+ "stops": [
+ [
+ 6,
+ 12
+ ],
+ [
+ 12,
+ 16
+ ]
+ ]
+ }
+ },
+ "paint": {
+ "text-color": "#666",
+ "text-halo-color": "rgba(255,255,255,0.75)",
+ "text-halo-width": 1,
+ "text-halo-blur": 1
+ }
+ },
+ {
+ "id": "place_label_city",
+ "type": "symbol",
+ "source": "mapbox",
+ "source-layer": "place_label",
+ "maxzoom": 16,
+ "filter": [
+ "all",
+ [
+ "==",
+ "$type",
+ "Point"
+ ],
+ [
+ "==",
+ "type",
+ "city"
+ ]
+ ],
+ "layout": {
+ "text-field": "{name_en}",
+ "text-font": [
+ "Open Sans Bold",
+ "Arial Unicode MS Bold"
+ ],
+ "text-max-width": 10,
+ "text-size": {
+ "stops": [
+ [
+ 3,
+ 12
+ ],
+ [
+ 8,
+ 16
+ ]
+ ]
+ }
+ },
+ "paint": {
+ "text-color": "#666",
+ "text-halo-color": "rgba(255,255,255,0.75)",
+ "text-halo-width": 1,
+ "text-halo-blur": 1
+ }
+ },
+ {
+ "id": "country_label",
+ "type": "symbol",
+ "source": "mapbox",
+ "source-layer": "country_label",
+ "maxzoom": 12,
+ "filter": [
+ "==",
+ "$type",
+ "Point"
+ ],
+ "layout": {
+ "text-field": "{name_en}",
+ "text-font": [
+ "Open Sans Regular",
+ "Arial Unicode MS Regular"
+ ],
+ "text-max-width": 10,
+ "text-size": {
+ "stops": [
+ [
+ 3,
+ 14
+ ],
+ [
+ 8,
+ 22
+ ]
+ ]
+ }
+ },
+ "paint": {
+ "text-color": "#666",
+ "text-halo-color": "rgba(255,255,255,0.75)",
+ "text-halo-width": 1,
+ "text-halo-blur": 1
+ }
+ },
+ {
+ "id": "road-heatmap",
+ "type": "heatmap",
+ "source": "mapbox",
+ "source-layer": "road",
+ "paint": {
+ "heatmap-intensity": 1
+ }
+ }
+ ]
+}
diff --git a/platform/macos/app/resources/heatmap.svg b/platform/macos/app/resources/heatmap.svg
new file mode 100644
index 0000000000..fa2a46590a
--- /dev/null
+++ b/platform/macos/app/resources/heatmap.svg
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ viewBox="0 0 16 16"
+ id="svg4148"
+ version="1.1"
+ inkscape:version="0.92.2 5c3e80d, 2017-08-06"
+ sodipodi:docname="heatmap.svg"
+ inkscape:export-filename="/Users/mxn/Desktop/symbol.png"
+ inkscape:export-xdpi="90.000244"
+ inkscape:export-ydpi="90.000244">
+ <defs
+ id="defs4150" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="23.25"
+ inkscape:cx="4.7741936"
+ inkscape:cy="7.6812068"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ units="px"
+ inkscape:window-width="1280"
+ inkscape:window-height="755"
+ inkscape:window-x="0"
+ inkscape:window-y="1"
+ inkscape:window-maximized="1"
+ inkscape:snap-bbox="true" />
+ <metadata
+ id="metadata4153">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-1036.3622)">
+ <path
+ style="opacity:1;fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:#000000;stroke-width:0.99369001;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 11.237265,1036.8591 a 4.2655011,4.1972104 0 0 0 -4.2658922,4.1979 4.2655011,4.1972104 0 0 0 0.025231,0.427 5.3408028,5.2552965 0 0 0 -1.1586611,-0.1299 5.3408028,5.2552965 0 0 0 -5.34109749,5.2556 5.3408028,5.2552965 0 0 0 5.34109749,5.2557 5.3408028,5.2552965 0 0 0 5.3410983,-5.2557 5.3408028,5.2552965 0 0 0 -0.186318,-1.3664 4.2655011,4.1972104 0 0 0 0.244542,0.012 4.2655011,4.1972104 0 0 0 4.26589,-4.198 4.2655011,4.1972104 0 0 0 -4.26589,-4.1979 z"
+ id="path818"
+ inkscape:connector-curvature="0" />
+ <path
+ style="opacity:1;fill:#000000;fill-opacity:0.15686275;fill-rule:evenodd;stroke:none;stroke-width:0.99369001;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 11.005859,1038.875 c -4.8895767,1.3679 0.321278,7.2823 2.416016,2.7578 0.450567,-1.4666 -0.923376,-2.9624 -2.416016,-2.7578 z m -5.5078121,4.5 c -5.49629855,0.4756 -2.1892763,9.0394 2.2363281,5.9121 2.667642,-1.8963 1.5392118,-6.8607 -2.2363281,-5.9121 z"
+ id="path836"
+ inkscape:connector-curvature="0" />
+ </g>
+</svg>
diff --git a/platform/macos/app/resources/hillshade.svg b/platform/macos/app/resources/hillshade.svg
new file mode 100644
index 0000000000..d3f0d72999
--- /dev/null
+++ b/platform/macos/app/resources/hillshade.svg
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ viewBox="0 0 16 16"
+ id="svg4148"
+ version="1.1"
+ inkscape:version="0.92.2 5c3e80d, 2017-08-06"
+ sodipodi:docname="hillshade.svg"
+ inkscape:export-filename="/Users/mxn/Desktop/symbol.png"
+ inkscape:export-xdpi="90.000244"
+ inkscape:export-ydpi="90.000244">
+ <defs
+ id="defs4150" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="23.25"
+ inkscape:cx="6.2795699"
+ inkscape:cy="11.122067"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ units="px"
+ inkscape:window-width="1280"
+ inkscape:window-height="755"
+ inkscape:window-x="0"
+ inkscape:window-y="1"
+ inkscape:window-maximized="1"
+ inkscape:snap-bbox="true" />
+ <metadata
+ id="metadata4153">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-1036.3622)">
+ <path
+ sodipodi:type="star"
+ style="opacity:1;fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:#000000;stroke-width:1.5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path839"
+ sodipodi:sides="3"
+ sodipodi:cx="12.189357"
+ sodipodi:cy="1045.3247"
+ sodipodi:r1="12.575057"
+ sodipodi:r2="6.2875285"
+ sodipodi:arg1="0.52359878"
+ sodipodi:arg2="1.5707963"
+ inkscape:flatsided="true"
+ inkscape:rounded="0"
+ inkscape:randomized="0"
+ d="m 23.079676,1051.6122 -21.7806381,0 10.8903191,-18.8625 z"
+ inkscape:transform-center-y="-2.1749516"
+ transform="matrix(0.6563034,0,0,0.75784422,8.3706122e-5,254.83559)" />
+ <path
+ transform="matrix(0.6563034,0,0,0.75784422,8.3706122e-5,254.83559)"
+ style="opacity:1;fill:#000000;fill-opacity:0.15686275;fill-rule:evenodd;stroke:none;stroke-width:1.50017999;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ inkscape:transform-center-y="-2.1749516"
+ d="m 23.079676,1051.6122 h -7.842948 l -3.047371,-18.8625 z"
+ id="path841"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccc" />
+ </g>
+</svg>
diff --git a/platform/macos/config.cmake b/platform/macos/config.cmake
index 33eaa82518..e929bb55c6 100644
--- a/platform/macos/config.cmake
+++ b/platform/macos/config.cmake
@@ -1,10 +1,10 @@
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.10)
mason_use(glfw VERSION 2017-07-13-67c9155)
-mason_use(boost_libprogram_options VERSION 1.62.0)
mason_use(gtest VERSION 1.8.0)
mason_use(benchmark VERSION 1.2.0)
mason_use(icu VERSION 58.1-min-size)
+mason_use(args VERSION 6.2.0 HEADER_ONLY)
include(cmake/loop-uv.cmake)
include(cmake/loop-darwin.cmake)
@@ -34,8 +34,6 @@ macro(mbgl_platform_core)
PRIVATE platform/default/mbgl/gl/headless_backend.cpp
PRIVATE platform/default/mbgl/gl/headless_backend.hpp
PRIVATE platform/darwin/src/headless_backend_cgl.cpp
- PRIVATE platform/default/mbgl/gl/headless_display.hpp
- PRIVATE platform/darwin/src/headless_display_cgl.cpp
# Snapshotting
PRIVATE platform/default/mbgl/map/map_snapshotter.cpp
@@ -54,7 +52,6 @@ macro(mbgl_platform_core)
target_compile_options(mbgl-core
PRIVATE -fobjc-arc
- PRIVATE -fvisibility=hidden
)
target_include_directories(mbgl-core
@@ -86,7 +83,6 @@ macro(mbgl_filesource)
target_compile_options(mbgl-filesource
PRIVATE -fobjc-arc
- PRIVATE -fvisibility=hidden
)
target_link_libraries(mbgl-filesource
@@ -101,11 +97,6 @@ macro(mbgl_platform_glfw)
PRIVATE mbgl-filesource
PRIVATE mbgl-loop-darwin
)
-
- target_compile_options(mbgl-glfw
- PRIVATE -fvisibility=hidden
- )
-
endmacro()
@@ -114,9 +105,6 @@ macro(mbgl_platform_render)
PRIVATE mbgl-filesource
PRIVATE mbgl-loop-darwin
)
- target_compile_options(mbgl-render
- PRIVATE -fvisibility=hidden
- )
endmacro()
@@ -125,9 +113,6 @@ macro(mbgl_platform_offline)
PRIVATE mbgl-filesource
PRIVATE mbgl-loop-darwin
)
- target_compile_options(mbgl-offline
- PRIVATE -fvisibility=hidden
- )
endmacro()
@@ -136,16 +121,16 @@ macro(mbgl_platform_test)
PRIVATE platform/default/mbgl/test/main.cpp
)
+ target_include_directories(mbgl-test
+ PRIVATE platform/macos
+ )
+
set_source_files_properties(
platform/default/mbgl/test/main.cpp
PROPERTIES
COMPILE_FLAGS -DWORK_DIRECTORY="${CMAKE_SOURCE_DIR}"
)
- target_compile_options(mbgl-test
- PRIVATE -fvisibility=hidden
- )
-
target_link_libraries(mbgl-test
PRIVATE mbgl-filesource
PRIVATE mbgl-loop-darwin
@@ -153,10 +138,6 @@ macro(mbgl_platform_test)
endmacro()
macro(mbgl_platform_benchmark)
- target_compile_options(mbgl-benchmark
- PRIVATE -fvisibility=hidden
- )
-
target_sources(mbgl-benchmark
PRIVATE benchmark/src/main.cpp
)
@@ -174,10 +155,6 @@ macro(mbgl_platform_benchmark)
endmacro()
macro(mbgl_platform_node)
- target_compile_options(mbgl-node
- PRIVATE -fvisibility=hidden
- )
-
target_link_libraries(mbgl-node
PRIVATE "-Wl,-bind_at_load"
)
diff --git a/platform/macos/docs/guides/For Style Authors.md b/platform/macos/docs/guides/For Style Authors.md
index d1b08f74a9..de838cd78f 100644
--- a/platform/macos/docs/guides/For Style Authors.md
+++ b/platform/macos/docs/guides/For Style Authors.md
@@ -179,6 +179,8 @@ In style JSON | In the SDK
`circle` | `MGLCircleStyleLayer`
`fill` | `MGLFillStyleLayer`
`fill-extrusion` | `MGLFillExtrusionStyleLayer`
+`heatmap` | `MGLHeatmapStyleLayer`
+`hillshade` | `MGLHillshadeStyleLayer`
`line` | `MGLLineStyleLayer`
`raster` | `MGLRasterStyleLayer`
`symbol` | `MGLSymbolStyleLayer`
@@ -258,12 +260,16 @@ In style JSON | In Objective-C | In Swift
## Setting attribute values
Each property representing a layout or paint attribute is set to an
-`MGLStyleValue` object, which is either an `MGLConstantStyleValue` object (for
-constant values) or an `MGLStyleFunction` object (for style functions). The
-style value object is a container for the raw value or function parameters that
-you want the attribute to be set to.
+`NSExpression` object. `NSExpression` objects play the same role as
+[expressions in the Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions),
+but you create the former using a very different syntax. `NSExpression`’s format
+string syntax is reminiscent of a spreadsheet formula or an expression in a
+database query. See the
+“[Predicates and Expressions](Predicates and Expressions.md)” guide for an
+overview of the expression support in this SDK. This SDK no longer supports
+style functions; use expressions instead.
-### Constant style values
+### Constant values in expressions
In contrast to the JSON type that the style specification defines for each
layout or paint property, the style value object often contains a more specific
@@ -274,10 +280,10 @@ or set.
In style JSON | In Objective-C | In Swift
--------------|-----------------------|---------
Color | `NSColor` | `NSColor`
-Enum | `NSValue` (see `NSValue(MGLAdditions)`) | `NSValue` (see `NSValue(MGLAdditions)`)
+Enum | `NSString` | `String`
String | `NSString` | `String`
-Boolean | `NSNumber.boolValue` | `Bool`
-Number | `NSNumber.floatValue` | `Float`
+Boolean | `NSNumber.boolValue` | `NSNumber.boolValue`
+Number | `NSNumber.floatValue` | `NSNumber.floatValue`
Array (`-dasharray`) | `NSArray<NSNumber>` | `[Float]`
Array (`-font`) | `NSArray<NSString>` | `[String]`
Array (`-offset`, `-translate`) | `NSValue` containing `CGVector` | `NSValue` containing `CGVector`
@@ -295,38 +301,72 @@ offset or translation upward, while a negative `CGVector.dy` means an offset or
translation downward. This is the reverse of how `CGVector` is interpreted on
iOS.
-### Style functions
-
-A _style function_ allows you to vary the value of a layout or paint attribute
-based on the zoom level, data provided by content sources, or both. For more
-information about style functions, see “[Using Style Functions at Runtime](using-style-functions-at-runtime.html)”.
-
-Each kind of style function is represented by a distinct class, but you
-typically create style functions as you create any other style value, using
-class methods on `MGLStyleValue`:
-
-In style specification | SDK class | SDK factory method
----------------------------|-----------------------------|-------------------
-zoom function | `MGLCameraStyleFunction` | `+[MGLStyleValue valueWithInterpolationMode:cameraStops:options:]`
-property function | `MGLSourceStyleFunction` | `+[MGLStyleValue valueWithInterpolationMode:sourceStops:attributeName:options:]`
-zoom-and-property function | `MGLCompositeStyleFunction` | `+[MGLStyleValue valueWithInterpolationMode:compositeStops:attributeName:options:]`
-
-The documentation for each individual style layer property indicates the kinds
-of style functions that are enabled for that property.
-
-When you create a style function, you specify an _interpolation mode_ and a
-series of _stops_. Each stop determines the effective value displayed at a
-particular zoom level (for camera functions) or the effective value on features
-with a particular attribute value in the content source (for source functions).
-The interpolation mode tells the SDK how to calculate the effective value
-between any two stops:
-
-In style specification | In the SDK
------------------------------|-----------
-`exponential` | `MGLInterpolationModeExponential`
-`interval` | `MGLInterpolationModeInterval`
-`categorical` | `MGLInterpolationModeCategorical`
-`identity` | `MGLInterpolationModeIdentity`
+### Expression operators
+
+In style specification | Method, function, or predicate type | Format string syntax
+-----------------------|-------------------------------------|---------------------
+`array` | |
+`boolean` | |
+`literal` | `+[NSExpression expressionForConstantValue:]` | `%@` representing `NSArray` or `NSDictionary`
+`number` | |
+`string` | |
+`to-boolean` | `boolValue` |
+`to-color` | |
+`to-number` | `mgl_numberWithFallbackValues:` |
+`to-string` | `stringValue` |
+`typeof` | |
+`geometry-type` | |
+`id` | |
+`properties` | |
+`at` | |
+`get` | `+[NSExpression expressionForKeyPath:]` | Key path
+`has` | |
+`length` | `count:` | `count({1, 2, 2, 3, 4, 7, 9})`
+`!` | `NSNotPredicateType` | `NOT (p0 OR … OR pn)`
+`!=` | `NSNotEqualToPredicateOperatorType` | `key != value`
+`<` | `NSLessThanPredicateOperatorType` | `key < value`
+`<=` | `NSLessThanOrEqualToPredicateOperatorType` | `key <= value`
+`==` | `NSEqualToPredicateOperatorType` | `key == value`
+`>` | `NSGreaterThanPredicateOperatorType` | `key > value`
+`>=` | `NSGreaterThanOrEqualToPredicateOperatorType` | `key >= value`
+`all` | `NSAndPredicateType` | `p0 AND … AND pn`
+`any` | `NSOrPredicateType` | `p0 OR … OR pn`
+`case` | `+[NSExpression expressionForConditional:trueExpression:falseExpression:]` | `TERNARY(condition, trueExpression, falseExpression)`
+`coalesce` | |
+`match` | |
+`interpolate` | `mgl_interpolateWithCurveType:parameters:stops:` |
+`step` | `mgl_stepWithMinimum:stops:` |
+`let` | `mgl_expressionWithContext:` |
+`var` | `+[NSExpression expressionForVariable:]` | `$variable`
+`concat` | `stringByAppendingString:` |
+`downcase` | `lowercase:` | `lowercase('DOWNTOWN')`
+`upcase` | `uppercase:` | `uppercase('Elysian Fields')`
+`rgb` | `+[NSColor colorWithCalibratedRed:green:blue:alpha:]` |
+`rgba` | `+[NSColor colorWithCalibratedRed:green:blue:alpha:]` |
+`to-rgba` | |
+`-` | `from:subtract:` | `2 - 1`
+`*` | `multiply:by:` | `1 * 2`
+`/` | `divide:by:` | `1 / 2`
+`%` | `modulus:by:` |
+`^` | `raise:toPower:` | `2 ** 2`
+`+` | `add:to:` | `1 + 2`
+`acos` | |
+`asin` | |
+`atan` | |
+`cos` | |
+`e` | | `%@` representing `NSNumber` containing `M_E`
+`ln` | `ln:` | `ln(2)`
+`ln2` | | `%@` representing `NSNumber` containing `M_LN2`
+`log10` | `log:` | `log(1)`
+`log2` | |
+`max` | `max:` | `max({1, 2, 2, 3, 4, 7, 9})`
+`min` | `min:` | `min({1, 2, 2, 3, 4, 7, 9})`
+`pi` | | `%@` representing `NSNumber` containing `M_PI`
+`sin` | |
+`sqrt` | `sqrt:` | `sqrt(2)`
+`tan` | |
+`zoom` | | `$zoom`
+`heatmap-density` | | `$heatmapDensity`
## Filtering sources
@@ -351,5 +391,5 @@ In style JSON | In the format string
`["any", f0, …, fn]` | `p0 OR … OR pn`
`["none", f0, …, fn]` | `NOT (p0 OR … OR pn)`
-See the `MGLVectorStyleLayer.predicate` documentation for a full description of
-the supported operators and operand types.
+See the “[Predicates and Expressions](Predicates and Expressions.md)” guide for
+a full description of the supported operators and operand types.
diff --git a/platform/macos/docs/guides/Using Style Functions at Runtime.md b/platform/macos/docs/guides/Using Style Functions at Runtime.md
index ea772a93a2..4e854aaaa0 100644
--- a/platform/macos/docs/guides/Using Style Functions at Runtime.md
+++ b/platform/macos/docs/guides/Using Style Functions at Runtime.md
@@ -27,15 +27,15 @@ The documentation for each individual style layer property notes which style fun
## Stops
-Stops are key-value pairs that that determine a style value. With a `MGLCameraSourceFunction` stop, you can use a dictionary with a zoom level for a key and a `MGLStyleValue` for the value. For example, you can use a stops dictionary with zoom levels 0, 10, and 20 as keys, and yellow, orange, and red as the values. A `MGLSourceStyleFunction` uses the relevant attribute value as the key.
+Stops are dictionary keys that are associated with layer attribute values. With feature attribute values as stops, you can use a dictionary with a zoom level for a key and an expression or constant value for the value. For example, you can use a stop dictionary with the zoom levels 0, 10, and 20 as keys and the colors yellow, orange, and red as the values. Alternatively, attribute values can be the keys.
```swift
-let stops = [
- 0: MGLStyleValue<NSColor>(rawValue: .yellow),
- 2.5: MGLStyleValue(rawValue: .orange),
- 5: MGLStyleValue(rawValue: .red),
- 7.5: MGLStyleValue(rawValue: .blue),
- 10: MGLStyleValue(rawValue: .white),
+let stops: [Float: NSColor] = [
+ 0: .yellow,
+ 2.5: .orange,
+ 5: .red,
+ 7.5: .blue,
+ 10: .white,
]
```
@@ -57,20 +57,18 @@ let symbolLayer = MGLSymbolStyleLayer(identifier: "place-city-sm", source: symbo
let source = MGLShapeSource(identifier: "earthquakes", url: url, options: nil)
mapView.style?.addSource(source)
-let stops = [
- 0: MGLStyleValue<NSColor>(rawValue: .yellow),
- 2.5: MGLStyleValue(rawValue: .orange),
- 5: MGLStyleValue(rawValue: .red),
- 7.5: MGLStyleValue(rawValue: .blue),
- 10: MGLStyleValue(rawValue: .white),
+let stops: [Float: NSColor] = [
+ 0: .yellow,
+ 2.5: .orange,
+ 5: .red,
+ 7.5: .blue,
+ 10: .white,
]
let layer = MGLCircleStyleLayer(identifier: "circles", source: source)
-layer.circleColor = MGLStyleValue(interpolationMode: .exponential,
- sourceStops: stops,
- attributeName: "mag",
- options: [.defaultValue: MGLStyleValue<NSColor>(rawValue: .green)])
-layer.circleRadius = MGLStyleValue(rawValue: 10)
+layer.circleColor = NSExpression(format: "FUNCTION(mag, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)",
+ stops)
+layer.circleRadius = NSExpression(forConstantValue: 10)
mapView.style?.insertLayer(layer, below: symbolLayer)
```
@@ -89,14 +87,13 @@ The example below increases a layer’s `circleRadius` exponentially based on a
```swift
let stops = [
- 12: MGLStyleValue<NSNumber>(rawValue: 0.5),
- 14: MGLStyleValue(rawValue: 2),
- 18: MGLStyleValue(rawValue: 18),
+ 12: 0.5,
+ 14: 2,
+ 18: 18,
]
-layer.circleRadius = MGLStyleValue(interpolationMode: .exponential,
- cameraStops: stops,
- options: [.interpolationBase: 1.5])
+layer.circleRadius = NSExpression(format: "FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'exponential', 1.5, %@)",
+ stops)
```
### Interval
@@ -106,18 +103,16 @@ layer.circleRadius = MGLStyleValue(interpolationMode: .exponential,
When we use the stops dictionary given above with an interval interpolation mode, we create ranges where earthquakes with a magnitude of 0 to just less than 2.5 would be yellow, 2.5 to just less than 5 would be orange, and so on.
```swift
-let stops = [
- 0: MGLStyleValue<NSColor>(rawValue: .yellow),
- 2.5: MGLStyleValue(rawValue: .orange),
- 5: MGLStyleValue(rawValue: .red),
- 7.5: MGLStyleValue(rawValue: .blue),
- 10: MGLStyleValue(rawValue: .white),
+let stops: [Float: NSColor] = [
+ 0: .yellow,
+ 2.5: .orange,
+ 5: .red,
+ 7.5: .blue,
+ 10: .white,
]
-layer.circleColor = MGLStyleValue(interpolationMode: .interval,
- sourceStops: stops,
- attributeName: "mag",
- options: [.defaultValue: MGLStyleValue<NSColor>(rawValue: .green)])
+layer.circleColor = NSExpression(format: "FUNCTION(mag, 'mgl_stepWithMinimum:stops:', %@, %@)",
+ NSColor.green, stops)
```
![interval mode](img/data-driven-styling/interval.png)
@@ -129,16 +124,16 @@ At each stop, `MGLInterpolationModeCategorical` produces an output value equal t
There are three main types of events in the dataset: earthquakes, explosions, and quarry blasts. In this case, the color of the circle layer will be determined by the type of event, with a default value of blue to catch any events that do not fall into any of those categories.
```swift
-let categoricalStops = [
- "earthquake": MGLStyleValue<NSColor>(rawValue: .orange),
- "explosion": MGLStyleValue(rawValue: .red),
- "quarry blast": MGLStyleValue(rawValue: .yellow),
+let colors: [String: NSColor] = [
+ "earthquake": .orange,
+ "explosion": .red,
+ "quarry blast": .yellow,
]
+let defaultColor = NSColor.blue
-layer.circleColor = MGLStyleValue(interpolationMode: .categorical,
- sourceStops: categoricalStops,
- attributeName: "type",
- options: [.defaultValue: MGLStyleValue<NSColor>(rawValue: .blue)])
+layer.circleColor = NSExpression(
+ format: "TERNARY(FUNCTION(%@, 'valueForKeyPath:', type) != nil, FUNCTION(%@, 'valueForKeyPath:', type), %@)",
+ colors, colors, defaultColor)
```
![categorical mode](img/data-driven-styling/categorical1.png) ![categorical mode](img/data-driven-styling/categorical2.png)
@@ -148,10 +143,7 @@ layer.circleColor = MGLStyleValue(interpolationMode: .categorical,
`MGLInterpolationModeIdentity` uses the attribute’s value as the style value. For example, you can set the `circleRadius` to the earthquake’s magnitude. Since the attribute value itself will be used as the style value, `sourceStops` should be set to `nil`.
```swift
-layer.circleRadius = MGLStyleValue(interpolationMode: .identity,
- sourceStops: nil,
- attributeName: "mag",
- options: [.defaultValue: MGLStyleValue<NSNumber>(rawValue: 0)])
+layer.circleRadius = NSExpression(forKeyPath: "mag")
```
![identity mode](img/data-driven-styling/identity.png)
diff --git a/platform/macos/jazzy.yml b/platform/macos/jazzy.yml
index ab4643fa65..b4dccb620f 100644
--- a/platform/macos/jazzy.yml
+++ b/platform/macos/jazzy.yml
@@ -3,7 +3,7 @@ author: Mapbox
author_url: https://www.mapbox.com/
github_url: https://github.com/mapbox/mapbox-gl-native
dash_url: https://mapbox.github.io/mapbox-gl-native/macos/docsets/Mapbox.xml
-copyright: '© 2014–2017 [Mapbox](https://www.mapbox.com/). See [license](https://github.com/mapbox/mapbox-gl-native/blob/master/LICENSE.md) for more details.'
+copyright: '© 2014–2018 [Mapbox](https://www.mapbox.com/). See [license](https://github.com/mapbox/mapbox-gl-native/blob/master/LICENSE.md) for more details.'
head: |
<link rel='shortcut icon' href='https://www.mapbox.com/img/favicon.ico' type='image/x-icon' />
@@ -18,6 +18,7 @@ custom_categories:
- name: Guides
children:
- Working with GeoJSON Data
+ - Predicates and Expressions
- For Style Authors
- Using Style Functions at Runtime
- Tile URL Templates
@@ -48,7 +49,6 @@ custom_categories:
- name: Styling the Map
children:
- MGLStyle
- - MGLStyleValue
- MGLLight
- name: Content Primitives
children:
@@ -64,8 +64,11 @@ custom_categories:
children:
- MGLSource
- MGLTileSource
+ - MGLAbstractShapeSource
- MGLShapeSource
+ - MGLComputedShapeSource
- MGLRasterSource
+ - MGLRasterDEMSource
- MGLVectorSource
- name: Style Layers
children:
@@ -77,6 +80,8 @@ custom_categories:
- MGLCircleStyleLayer
- MGLFillStyleLayer
- MGLFillExtrusionStyleLayer
+ - MGLHeatmapStyleLayer
+ - MGLHillshadeStyleLayer
- MGLLineStyleLayer
- MGLSymbolStyleLayer
- name: Offline Maps
diff --git a/platform/macos/macos.xcodeproj/project.pbxproj b/platform/macos/macos.xcodeproj/project.pbxproj
index 25ee7516be..4327670911 100644
--- a/platform/macos/macos.xcodeproj/project.pbxproj
+++ b/platform/macos/macos.xcodeproj/project.pbxproj
@@ -9,7 +9,15 @@
/* Begin PBXBuildFile section */
0721493F1EE200E900085505 /* MGLImageSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 07A019EB1ED662D800ACD43E /* MGLImageSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
07A019EF1ED665CD00ACD43E /* MGLImageSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 07A019EC1ED662D800ACD43E /* MGLImageSource.mm */; };
+ 07A240941F675674002C8210 /* MGLComputedShapeSourceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 07A240921F67566F002C8210 /* MGLComputedShapeSourceTests.m */; };
07BA4CAC1EE21887004528F5 /* MGLImageSourceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 07BA4CAB1EE21887004528F5 /* MGLImageSourceTests.m */; };
+ 07D9474B1F6743F000E37934 /* MGLAbstractShapeSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 07D947491F6741F500E37934 /* MGLAbstractShapeSource.mm */; };
+ 07D9474C1F67441500E37934 /* MGLAbstractShapeSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 07D947481F6741F500E37934 /* MGLAbstractShapeSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 07D9474D1F67441B00E37934 /* MGLAbstractShapeSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 07D947471F6741F500E37934 /* MGLAbstractShapeSource_Private.h */; };
+ 07F8E2F71F674C8800F794BB /* MGLComputedShapeSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 07F8E2F41F674C8000F794BB /* MGLComputedShapeSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 07F8E2F81F674C9000F794BB /* MGLComputedShapeSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 07F8E2F51F674C8000F794BB /* MGLComputedShapeSource.mm */; };
+ 170A82BF201BDD1B00943087 /* MGLHeatmapStyleLayerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 170A82BE201BDD1B00943087 /* MGLHeatmapStyleLayerTests.mm */; };
+ 170A82C4201FB6EC00943087 /* MGLHeatmapColorTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 170A82C2201FAFF800943087 /* MGLHeatmapColorTests.mm */; };
1753ED401E53CE6100A9FD90 /* MGLConversion.h in Headers */ = {isa = PBXBuildFile; fileRef = 1753ED3F1E53CE5200A9FD90 /* MGLConversion.h */; };
1F7454A31ECFB00300021D39 /* MGLLight_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F7454A01ECFB00300021D39 /* MGLLight_Private.h */; };
1F7454A41ECFB00300021D39 /* MGLLight.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F7454A11ECFB00300021D39 /* MGLLight.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -17,10 +25,8 @@
1F7454AB1ED1DDBD00021D39 /* MGLLightTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1F7454AA1ED1DDBD00021D39 /* MGLLightTest.mm */; };
1F95931B1E6DE2B600D5B294 /* MGLNSDateAdditionsTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1F95931A1E6DE2B600D5B294 /* MGLNSDateAdditionsTests.mm */; };
1F9EF4061FBA1B0E0063FBB0 /* mapbox_helmet.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 1F9EF4051FBA1B0D0063FBB0 /* mapbox_helmet.pdf */; };
- 1FCDF1421F2A4F3600A46694 /* MGLVectorSource+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1FCDF1401F2A4F3600A46694 /* MGLVectorSource+MGLAdditions.h */; };
- 1FCDF1431F2A4F3600A46694 /* MGLVectorSource+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FCDF1411F2A4F3600A46694 /* MGLVectorSource+MGLAdditions.m */; };
30E5781B1DAA857E0050F07E /* NSImage+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 30E578141DAA7D920050F07E /* NSImage+MGLAdditions.h */; };
- 3508EC641D749D39009B0EE4 /* NSExpression+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3508EC621D749D39009B0EE4 /* NSExpression+MGLAdditions.h */; };
+ 3508EC641D749D39009B0EE4 /* NSExpression+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3508EC621D749D39009B0EE4 /* NSExpression+MGLAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
3508EC651D749D39009B0EE4 /* NSExpression+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3508EC631D749D39009B0EE4 /* NSExpression+MGLAdditions.mm */; };
3526EABD1DF9B19800006B43 /* MGLCodingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3526EABC1DF9B19800006B43 /* MGLCodingTests.m */; };
352742781D4C220900A1ECE6 /* MGLStyleValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 352742771D4C220900A1ECE6 /* MGLStyleValue.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -49,7 +55,6 @@
35724FC41D630502002A4AB4 /* amsterdam.geojson in Resources */ = {isa = PBXBuildFile; fileRef = 358EB3AE1D61F0DB00E46D9C /* amsterdam.geojson */; };
359819591E02F611008FC139 /* NSCoder+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 359819571E02F611008FC139 /* NSCoder+MGLAdditions.h */; };
3598195A1E02F611008FC139 /* NSCoder+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 359819581E02F611008FC139 /* NSCoder+MGLAdditions.mm */; };
- 3599A3E81DF70E2000E77FB2 /* MGLStyleValueTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3599A3E71DF70E2000E77FB2 /* MGLStyleValueTests.m */; };
35C5D8471D6DD66D00E95907 /* NSComparisonPredicate+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 35C5D8431D6DD66D00E95907 /* NSComparisonPredicate+MGLAdditions.h */; };
35C5D8481D6DD66D00E95907 /* NSComparisonPredicate+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 35C5D8441D6DD66D00E95907 /* NSComparisonPredicate+MGLAdditions.mm */; };
35C5D8491D6DD66D00E95907 /* NSCompoundPredicate+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 35C5D8451D6DD66D00E95907 /* NSCompoundPredicate+MGLAdditions.h */; };
@@ -66,11 +71,7 @@
4049C2A51DB6CE7F00B3F799 /* MGLPointCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = 4049C2A11DB6CE7800B3F799 /* MGLPointCollection.h */; settings = {ATTRIBUTES = (Public, ); }; };
4049C2AD1DB8020600B3F799 /* MGLPointCollection.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4049C2A71DB6D09B00B3F799 /* MGLPointCollection.mm */; };
408AA85B1DAEECFE00022900 /* MGLShape_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 408AA85A1DAEECF100022900 /* MGLShape_Private.h */; };
- 408AA8651DAEEE3400022900 /* MGLPolygon+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 408AA8601DAEED3300022900 /* MGLPolygon+MGLAdditions.h */; };
- 408AA8661DAEEE3600022900 /* MGLPolyline+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 408AA8611DAEED3300022900 /* MGLPolyline+MGLAdditions.h */; };
408AA8671DAEEE3900022900 /* NSDictionary+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 408AA85F1DAEED3300022900 /* NSDictionary+MGLAdditions.h */; };
- 408AA8681DAEEE5200022900 /* MGLPolygon+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 408AA85C1DAEED3300022900 /* MGLPolygon+MGLAdditions.m */; };
- 408AA8691DAEEE5500022900 /* MGLPolyline+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 408AA85D1DAEED3300022900 /* MGLPolyline+MGLAdditions.m */; };
408AA86A1DAEEE5D00022900 /* NSDictionary+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 408AA85E1DAEED3300022900 /* NSDictionary+MGLAdditions.mm */; };
40ABDB561DB0022100372083 /* NSImage+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 405C03971DB0004E001AC280 /* NSImage+MGLAdditions.mm */; };
40B77E451DB11BC9003DA2FE /* NSArray+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 40B77E431DB11BB0003DA2FE /* NSArray+MGLAdditions.h */; };
@@ -86,15 +87,19 @@
55D120A31F7906E6004B6D81 /* libmbgl-filesource.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 55D120A41F7906E6004B6D81 /* libmbgl-filesource.a */; };
55D120A51F790A0C004B6D81 /* libmbgl-filesource.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 55D120A41F7906E6004B6D81 /* libmbgl-filesource.a */; };
55E2AD111E5B0A6900E8C587 /* MGLOfflineStorageTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 55E2AD101E5B0A6900E8C587 /* MGLOfflineStorageTests.mm */; };
+ 89462399200D199100DA8EF2 /* heatmap.json in Resources */ = {isa = PBXBuildFile; fileRef = 89462398200D199100DA8EF2 /* heatmap.json */; };
+ 8946239D200E744800DA8EF2 /* MGLHeatmapStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 8946239A200E73CA00DA8EF2 /* MGLHeatmapStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 894623A0200E748000DA8EF2 /* MGLHeatmapStyleLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8946239B200E73CA00DA8EF2 /* MGLHeatmapStyleLayer.mm */; };
92092EF01F5EB10E00AF5130 /* MGLMapSnapshotter.h in Headers */ = {isa = PBXBuildFile; fileRef = 92092EEE1F5EB10E00AF5130 /* MGLMapSnapshotter.h */; settings = {ATTRIBUTES = (Public, ); }; };
92092EF11F5EB10E00AF5130 /* MGLMapSnapshotter.mm in Sources */ = {isa = PBXBuildFile; fileRef = 92092EEF1F5EB10E00AF5130 /* MGLMapSnapshotter.mm */; };
920A3E591E6F859D00C16EFC /* MGLSourceQueryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 920A3E581E6F859D00C16EFC /* MGLSourceQueryTests.m */; };
92F2C3EB1F0E3A1900268EC0 /* MGLRendererFrontend.h in Headers */ = {isa = PBXBuildFile; fileRef = 92F2C3EA1F0E3A1900268EC0 /* MGLRendererFrontend.h */; };
+ 9654C12B1FFC38E000DB6A19 /* MGLPolyline_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 9654C12A1FFC38E000DB6A19 /* MGLPolyline_Private.h */; };
+ 9654C12D1FFC394700DB6A19 /* MGLPolygon_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 9654C12C1FFC394700DB6A19 /* MGLPolygon_Private.h */; };
96E027311E57C9A7004B8E66 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 96E027331E57C9A7004B8E66 /* Localizable.strings */; };
DA00FC8A1D5EEAC3009AABC8 /* MGLAttributionInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = DA00FC881D5EEAC3009AABC8 /* MGLAttributionInfo.h */; settings = {ATTRIBUTES = (Public, ); }; };
DA00FC8B1D5EEAC3009AABC8 /* MGLAttributionInfo.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA00FC891D5EEAC3009AABC8 /* MGLAttributionInfo.mm */; };
DA0CD58E1CF56F5800A5F5A5 /* MGLFeatureTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA0CD58D1CF56F5800A5F5A5 /* MGLFeatureTests.mm */; };
- DA2207BC1DC076940002F84D /* MGLStyleValueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA2207BB1DC076940002F84D /* MGLStyleValueTests.swift */; };
DA2784FE1DF03060001D5B8D /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DA2784FD1DF03060001D5B8D /* Media.xcassets */; };
DA29875A1E1A4290002299F5 /* MGLDocumentationExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA2987591E1A4290002299F5 /* MGLDocumentationExampleTests.swift */; };
DA35A2A41CC9EB1A00E826B2 /* MGLCoordinateFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = DA35A2A31CC9EB1A00E826B2 /* MGLCoordinateFormatter.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -160,7 +165,10 @@
DAA998FC1E9F545C002E6EA6 /* MGLFillExtrusionStyleLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = DAA998FA1E9F545C002E6EA6 /* MGLFillExtrusionStyleLayer.mm */; };
DAA999011E9F5EC5002E6EA6 /* MGLFillExtrusionStyleLayerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DAA999001E9F5EC5002E6EA6 /* MGLFillExtrusionStyleLayerTests.mm */; };
DAB2CCE51DF632ED001B2FE1 /* LimeGreenStyleLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = DAB2CCE41DF632ED001B2FE1 /* LimeGreenStyleLayer.m */; };
+ DAC25FCA200FD5E2009BE98E /* NSExpression+MGLPrivateAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DAC25FC9200FD5E2009BE98E /* NSExpression+MGLPrivateAdditions.h */; };
DAC2ABC51CC6D343006D18C4 /* MGLAnnotationImage_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DAC2ABC41CC6D343006D18C4 /* MGLAnnotationImage_Private.h */; };
+ DACA8622201920BE00E9693A /* MGLRasterDEMSource.h in Headers */ = {isa = PBXBuildFile; fileRef = DACA8620201920BE00E9693A /* MGLRasterDEMSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DACA8623201920BE00E9693A /* MGLRasterDEMSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = DACA8621201920BE00E9693A /* MGLRasterDEMSource.mm */; };
DACB0C391E18DFFD005DDBEA /* MGLStyle+MBXAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DACB0C381E18DFFD005DDBEA /* MGLStyle+MBXAdditions.m */; };
DACC22141CF3D3E200D220D9 /* MGLFeature.h in Headers */ = {isa = PBXBuildFile; fileRef = DACC22121CF3D3E200D220D9 /* MGLFeature.h */; settings = {ATTRIBUTES = (Public, ); }; };
DACC22151CF3D3E200D220D9 /* MGLFeature.mm in Sources */ = {isa = PBXBuildFile; fileRef = DACC22131CF3D3E200D220D9 /* MGLFeature.mm */; };
@@ -246,6 +254,9 @@
DAEDC4371D606291000224FF /* MGLAttributionButtonTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DAEDC4361D606291000224FF /* MGLAttributionButtonTests.m */; };
DAF0D80E1DFE0E5D00B28378 /* MGLPointCollection_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DAF0D80D1DFE0E5D00B28378 /* MGLPointCollection_Private.h */; };
DAF0D8161DFE6B1800B28378 /* MGLAttributionInfo_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DAF0D8151DFE6B1800B28378 /* MGLAttributionInfo_Private.h */; };
+ DAF25715201901C200367EF5 /* MGLHillshadeStyleLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = DAF25713201901C100367EF5 /* MGLHillshadeStyleLayer.mm */; };
+ DAF25716201901C200367EF5 /* MGLHillshadeStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = DAF25714201901C200367EF5 /* MGLHillshadeStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DAF25721201902C100367EF5 /* MGLHillshadeStyleLayerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DAF2571D201902A500367EF5 /* MGLHillshadeStyleLayerTests.mm */; };
DD0902B21DB1AC6400C5BDCE /* MGLNetworkConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = DD0902AF1DB1AC6400C5BDCE /* MGLNetworkConfiguration.m */; };
DD0902B31DB1AC6400C5BDCE /* MGLNetworkConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = DD0902B01DB1AC6400C5BDCE /* MGLNetworkConfiguration.h */; };
DD58A4C91D822C6700E1F038 /* MGLExpressionTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DD58A4C71D822C6200E1F038 /* MGLExpressionTests.mm */; };
@@ -285,7 +296,15 @@
/* Begin PBXFileReference section */
07A019EB1ED662D800ACD43E /* MGLImageSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLImageSource.h; sourceTree = "<group>"; };
07A019EC1ED662D800ACD43E /* MGLImageSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLImageSource.mm; sourceTree = "<group>"; };
+ 07A240921F67566F002C8210 /* MGLComputedShapeSourceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLComputedShapeSourceTests.m; sourceTree = "<group>"; };
07BA4CAB1EE21887004528F5 /* MGLImageSourceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLImageSourceTests.m; sourceTree = "<group>"; };
+ 07D947471F6741F500E37934 /* MGLAbstractShapeSource_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAbstractShapeSource_Private.h; sourceTree = "<group>"; };
+ 07D947481F6741F500E37934 /* MGLAbstractShapeSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAbstractShapeSource.h; sourceTree = "<group>"; };
+ 07D947491F6741F500E37934 /* MGLAbstractShapeSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLAbstractShapeSource.mm; sourceTree = "<group>"; };
+ 07F8E2F41F674C8000F794BB /* MGLComputedShapeSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLComputedShapeSource.h; sourceTree = "<group>"; };
+ 07F8E2F51F674C8000F794BB /* MGLComputedShapeSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLComputedShapeSource.mm; sourceTree = "<group>"; };
+ 170A82BE201BDD1B00943087 /* MGLHeatmapStyleLayerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLHeatmapStyleLayerTests.mm; sourceTree = "<group>"; };
+ 170A82C2201FAFF800943087 /* MGLHeatmapColorTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLHeatmapColorTests.mm; sourceTree = "<group>"; };
1753ED3F1E53CE5200A9FD90 /* MGLConversion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLConversion.h; sourceTree = "<group>"; };
1F7454A01ECFB00300021D39 /* MGLLight_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLLight_Private.h; sourceTree = "<group>"; };
1F7454A11ECFB00300021D39 /* MGLLight.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLLight.h; sourceTree = "<group>"; };
@@ -293,8 +312,6 @@
1F7454AA1ED1DDBD00021D39 /* MGLLightTest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLLightTest.mm; sourceTree = "<group>"; };
1F95931A1E6DE2B600D5B294 /* MGLNSDateAdditionsTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLNSDateAdditionsTests.mm; path = ../../darwin/test/MGLNSDateAdditionsTests.mm; sourceTree = "<group>"; };
1F9EF4051FBA1B0D0063FBB0 /* mapbox_helmet.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = mapbox_helmet.pdf; sourceTree = "<group>"; };
- 1FCDF1401F2A4F3600A46694 /* MGLVectorSource+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MGLVectorSource+MGLAdditions.h"; sourceTree = "<group>"; };
- 1FCDF1411F2A4F3600A46694 /* MGLVectorSource+MGLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MGLVectorSource+MGLAdditions.m"; sourceTree = "<group>"; };
30E578141DAA7D920050F07E /* NSImage+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSImage+MGLAdditions.h"; path = "src/NSImage+MGLAdditions.h"; sourceTree = SOURCE_ROOT; };
3508EC621D749D39009B0EE4 /* NSExpression+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSExpression+MGLAdditions.h"; sourceTree = "<group>"; };
3508EC631D749D39009B0EE4 /* NSExpression+MGLAdditions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSExpression+MGLAdditions.mm"; sourceTree = "<group>"; };
@@ -311,7 +328,6 @@
3527429E1D4C25BD00A1ECE6 /* MGLStyleValue.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLStyleValue.mm; sourceTree = "<group>"; };
352903991D6C63B80002C7DF /* NSPredicate+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSPredicate+MGLAdditions.h"; sourceTree = "<group>"; };
3529039A1D6C63B80002C7DF /* NSPredicate+MGLAdditions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSPredicate+MGLAdditions.mm"; sourceTree = "<group>"; };
- 353722EB1DF850ED004D2F3F /* MGLStyleValueTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLStyleValueTests.h; sourceTree = "<group>"; };
3537CA731D3F93A600380318 /* MGLStyle_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLStyle_Private.h; sourceTree = "<group>"; };
3538AA211D542685008EC33D /* MGLStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLStyleLayer.h; sourceTree = "<group>"; };
3538AA221D542685008EC33D /* MGLStyleLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLStyleLayer.mm; sourceTree = "<group>"; };
@@ -325,7 +341,6 @@
358EB3AE1D61F0DB00E46D9C /* amsterdam.geojson */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = amsterdam.geojson; path = ../../darwin/test/amsterdam.geojson; sourceTree = "<group>"; };
359819571E02F611008FC139 /* NSCoder+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSCoder+MGLAdditions.h"; sourceTree = "<group>"; };
359819581E02F611008FC139 /* NSCoder+MGLAdditions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSCoder+MGLAdditions.mm"; sourceTree = "<group>"; };
- 3599A3E71DF70E2000E77FB2 /* MGLStyleValueTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLStyleValueTests.m; sourceTree = "<group>"; };
35C5D8431D6DD66D00E95907 /* NSComparisonPredicate+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSComparisonPredicate+MGLAdditions.h"; sourceTree = "<group>"; };
35C5D8441D6DD66D00E95907 /* NSComparisonPredicate+MGLAdditions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSComparisonPredicate+MGLAdditions.mm"; sourceTree = "<group>"; };
35C5D8451D6DD66D00E95907 /* NSCompoundPredicate+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSCompoundPredicate+MGLAdditions.h"; sourceTree = "<group>"; };
@@ -345,12 +360,8 @@
405C03961DB0004E001AC280 /* NSImage+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSImage+MGLAdditions.h"; sourceTree = "<group>"; };
405C03971DB0004E001AC280 /* NSImage+MGLAdditions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSImage+MGLAdditions.mm"; sourceTree = "<group>"; };
408AA85A1DAEECF100022900 /* MGLShape_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLShape_Private.h; sourceTree = "<group>"; };
- 408AA85C1DAEED3300022900 /* MGLPolygon+MGLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MGLPolygon+MGLAdditions.m"; sourceTree = "<group>"; };
- 408AA85D1DAEED3300022900 /* MGLPolyline+MGLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MGLPolyline+MGLAdditions.m"; sourceTree = "<group>"; };
408AA85E1DAEED3300022900 /* NSDictionary+MGLAdditions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSDictionary+MGLAdditions.mm"; sourceTree = "<group>"; };
408AA85F1DAEED3300022900 /* NSDictionary+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+MGLAdditions.h"; sourceTree = "<group>"; };
- 408AA8601DAEED3300022900 /* MGLPolygon+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MGLPolygon+MGLAdditions.h"; sourceTree = "<group>"; };
- 408AA8611DAEED3300022900 /* MGLPolyline+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MGLPolyline+MGLAdditions.h"; sourceTree = "<group>"; };
40B77E421DB11BB0003DA2FE /* NSArray+MGLAdditions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSArray+MGLAdditions.mm"; sourceTree = "<group>"; };
40B77E431DB11BB0003DA2FE /* NSArray+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+MGLAdditions.h"; sourceTree = "<group>"; };
40E1601A1DF216E6005EA6D9 /* MGLStyleLayerTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLStyleLayerTests.h; sourceTree = "<group>"; };
@@ -365,10 +376,15 @@
55D9B4B01D005D3900C1CCE2 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
55E2AD101E5B0A6900E8C587 /* MGLOfflineStorageTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLOfflineStorageTests.mm; path = ../../darwin/test/MGLOfflineStorageTests.mm; sourceTree = "<group>"; };
55FE0E8D1D100A0900FD240B /* config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = config.xcconfig; path = ../../build/macos/config.xcconfig; sourceTree = "<group>"; };
+ 89462398200D199100DA8EF2 /* heatmap.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = heatmap.json; sourceTree = "<group>"; };
+ 8946239A200E73CA00DA8EF2 /* MGLHeatmapStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLHeatmapStyleLayer.h; sourceTree = "<group>"; };
+ 8946239B200E73CA00DA8EF2 /* MGLHeatmapStyleLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLHeatmapStyleLayer.mm; sourceTree = "<group>"; };
92092EEE1F5EB10E00AF5130 /* MGLMapSnapshotter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLMapSnapshotter.h; sourceTree = "<group>"; };
92092EEF1F5EB10E00AF5130 /* MGLMapSnapshotter.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLMapSnapshotter.mm; sourceTree = "<group>"; };
920A3E581E6F859D00C16EFC /* MGLSourceQueryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLSourceQueryTests.m; sourceTree = "<group>"; };
92F2C3EA1F0E3A1900268EC0 /* MGLRendererFrontend.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLRendererFrontend.h; sourceTree = "<group>"; };
+ 9654C12A1FFC38E000DB6A19 /* MGLPolyline_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLPolyline_Private.h; sourceTree = "<group>"; };
+ 9654C12C1FFC394700DB6A19 /* MGLPolygon_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLPolygon_Private.h; sourceTree = "<group>"; };
966091701E5BBFF700A9A03B /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
966091711E5BBFF900A9A03B /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = "<group>"; };
966091721E5BBFFA00A9A03B /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Localizable.strings; sourceTree = "<group>"; };
@@ -388,7 +404,6 @@
DA1AC01E1E5B8826006DF1D6 /* lt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = lt; path = lt.lproj/Localizable.strings; sourceTree = "<group>"; };
DA1AC01F1E5B8904006DF1D6 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = uk; path = uk.lproj/Foundation.stringsdict; sourceTree = "<group>"; };
DA2207BA1DC076930002F84D /* test-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "test-Bridging-Header.h"; sourceTree = "<group>"; };
- DA2207BB1DC076940002F84D /* MGLStyleValueTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MGLStyleValueTests.swift; sourceTree = "<group>"; };
DA2784FD1DF03060001D5B8D /* Media.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Media.xcassets; path = ../../darwin/test/Media.xcassets; sourceTree = "<group>"; };
DA2987591E1A4290002299F5 /* MGLDocumentationExampleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MGLDocumentationExampleTests.swift; path = ../../darwin/test/MGLDocumentationExampleTests.swift; sourceTree = "<group>"; };
DA3389601FA3EAC4001EA329 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Foundation.strings"; sourceTree = "<group>"; };
@@ -505,9 +520,15 @@
DAA999001E9F5EC5002E6EA6 /* MGLFillExtrusionStyleLayerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLFillExtrusionStyleLayerTests.mm; sourceTree = "<group>"; };
DAB2CCE31DF632ED001B2FE1 /* LimeGreenStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LimeGreenStyleLayer.h; sourceTree = "<group>"; };
DAB2CCE41DF632ED001B2FE1 /* LimeGreenStyleLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LimeGreenStyleLayer.m; sourceTree = "<group>"; };
+ DAC25FC9200FD5E2009BE98E /* NSExpression+MGLPrivateAdditions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSExpression+MGLPrivateAdditions.h"; sourceTree = "<group>"; };
DAC2ABC41CC6D343006D18C4 /* MGLAnnotationImage_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAnnotationImage_Private.h; sourceTree = "<group>"; };
+ DACA8620201920BE00E9693A /* MGLRasterDEMSource.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLRasterDEMSource.h; sourceTree = "<group>"; };
+ DACA8621201920BE00E9693A /* MGLRasterDEMSource.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLRasterDEMSource.mm; sourceTree = "<group>"; };
DACB0C371E18DFFD005DDBEA /* MGLStyle+MBXAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MGLStyle+MBXAdditions.h"; sourceTree = "<group>"; };
DACB0C381E18DFFD005DDBEA /* MGLStyle+MBXAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MGLStyle+MBXAdditions.m"; sourceTree = "<group>"; };
+ DACBC6082011885800C4D7E2 /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/Localizable.strings; sourceTree = "<group>"; };
+ DACBC6092011888C00C4D7E2 /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/Foundation.strings; sourceTree = "<group>"; };
+ DACBC60A2011889E00C4D7E2 /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/Localizable.strings; sourceTree = "<group>"; };
DACC22121CF3D3E200D220D9 /* MGLFeature.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLFeature.h; sourceTree = "<group>"; };
DACC22131CF3D3E200D220D9 /* MGLFeature.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLFeature.mm; sourceTree = "<group>"; };
DACC22171CF3D4F700D220D9 /* MGLFeature_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLFeature_Private.h; sourceTree = "<group>"; };
@@ -605,6 +626,9 @@
DAEDC4361D606291000224FF /* MGLAttributionButtonTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLAttributionButtonTests.m; sourceTree = "<group>"; };
DAF0D80D1DFE0E5D00B28378 /* MGLPointCollection_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLPointCollection_Private.h; sourceTree = "<group>"; };
DAF0D8151DFE6B1800B28378 /* MGLAttributionInfo_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAttributionInfo_Private.h; sourceTree = "<group>"; };
+ DAF25713201901C100367EF5 /* MGLHillshadeStyleLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLHillshadeStyleLayer.mm; sourceTree = "<group>"; };
+ DAF25714201901C200367EF5 /* MGLHillshadeStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLHillshadeStyleLayer.h; sourceTree = "<group>"; };
+ DAF2571D201902A500367EF5 /* MGLHillshadeStyleLayerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLHillshadeStyleLayerTests.mm; sourceTree = "<group>"; };
DAFBD0D51E3FA969000CD6BF /* zh-Hant */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; sourceTree = "<group>"; };
DAFBD0D61E3FA983000CD6BF /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Foundation.strings"; sourceTree = "<group>"; };
DD0902AF1DB1AC6400C5BDCE /* MGLNetworkConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLNetworkConfiguration.m; sourceTree = "<group>"; };
@@ -658,6 +682,10 @@
35602BF91D3EA99F0050646F /* MGLFillStyleLayer.mm */,
35602BFD1D3EA9B40050646F /* MGLForegroundStyleLayer.h */,
35602BFE1D3EA9B40050646F /* MGLForegroundStyleLayer.mm */,
+ 8946239A200E73CA00DA8EF2 /* MGLHeatmapStyleLayer.h */,
+ 8946239B200E73CA00DA8EF2 /* MGLHeatmapStyleLayer.mm */,
+ DAF25714201901C200367EF5 /* MGLHillshadeStyleLayer.h */,
+ DAF25713201901C100367EF5 /* MGLHillshadeStyleLayer.mm */,
DA8F25891D51CA540010E6B5 /* MGLLineStyleLayer.h */,
DA8F258A1D51CA540010E6B5 /* MGLLineStyleLayer.mm */,
DA7262051DEEDD460043BB89 /* MGLOpenGLStyleLayer.h */,
@@ -678,8 +706,6 @@
352742791D4C235C00A1ECE6 /* Categories */ = {
isa = PBXGroup;
children = (
- 1FCDF1401F2A4F3600A46694 /* MGLVectorSource+MGLAdditions.h */,
- 1FCDF1411F2A4F3600A46694 /* MGLVectorSource+MGLAdditions.m */,
DA8F25A61D51CB270010E6B5 /* NSValue+MGLStyleAttributeAdditions.h */,
DA8F25A71D51CB270010E6B5 /* NSValue+MGLStyleAttributeAdditions.mm */,
);
@@ -689,9 +715,16 @@
3527427E1D4C242B00A1ECE6 /* Sources */ = {
isa = PBXGroup;
children = (
+ 07D947481F6741F500E37934 /* MGLAbstractShapeSource.h */,
+ 07D947471F6741F500E37934 /* MGLAbstractShapeSource_Private.h */,
+ 07D947491F6741F500E37934 /* MGLAbstractShapeSource.mm */,
+ 07F8E2F41F674C8000F794BB /* MGLComputedShapeSource.h */,
+ 07F8E2F51F674C8000F794BB /* MGLComputedShapeSource.mm */,
352742831D4C244700A1ECE6 /* MGLRasterSource.h */,
DA7DC9821DED647F0027472F /* MGLRasterSource_Private.h */,
352742841D4C244700A1ECE6 /* MGLRasterSource.mm */,
+ DACA8620201920BE00E9693A /* MGLRasterDEMSource.h */,
+ DACA8621201920BE00E9693A /* MGLRasterDEMSource.mm */,
352742871D4C245800A1ECE6 /* MGLShapeSource.h */,
DA87A99B1DC9D8DD00810D09 /* MGLShapeSource_Private.h */,
352742881D4C245800A1ECE6 /* MGLShapeSource.mm */,
@@ -804,6 +837,7 @@
DA839EA61CC2E3400062CAFB /* Info.plist */,
96E027331E57C9A7004B8E66 /* Localizable.strings */,
DA839E981CC2E3400062CAFB /* Supporting Files */,
+ 89462398200D199100DA8EF2 /* heatmap.json */,
);
name = "Demo App";
path = app;
@@ -820,6 +854,7 @@
DA87A99A1DC9D88800810D09 /* Sources */ = {
isa = PBXGroup;
children = (
+ 07A240921F67566F002C8210 /* MGLComputedShapeSourceTests.m */,
DA87A9961DC9D88400810D09 /* MGLShapeSourceTests.mm */,
DA87A9971DC9D88400810D09 /* MGLTileSetTests.mm */,
920A3E581E6F859D00C16EFC /* MGLSourceQueryTests.m */,
@@ -853,11 +888,14 @@
DA8F257C1D51C5F40010E6B5 /* Layers */ = {
isa = PBXGroup;
children = (
+ 170A82C2201FAFF800943087 /* MGLHeatmapColorTests.mm */,
+ 170A82BE201BDD1B00943087 /* MGLHeatmapStyleLayerTests.mm */,
40E1601A1DF216E6005EA6D9 /* MGLStyleLayerTests.h */,
40E1601B1DF216E6005EA6D9 /* MGLStyleLayerTests.m */,
DA2207BA1DC076930002F84D /* test-Bridging-Header.h */,
DAA999001E9F5EC5002E6EA6 /* MGLFillExtrusionStyleLayerTests.mm */,
DA8F25741D51C5F40010E6B5 /* MGLFillStyleLayerTests.mm */,
+ DAF2571D201902A500367EF5 /* MGLHillshadeStyleLayerTests.mm */,
DA8F25751D51C5F40010E6B5 /* MGLRasterStyleLayerTests.mm */,
DA8F25761D51C5F40010E6B5 /* MGLSymbolStyleLayerTests.mm */,
DA8F25771D51C5F40010E6B5 /* MGLLineStyleLayerTests.mm */,
@@ -873,9 +911,6 @@
DA8F257C1D51C5F40010E6B5 /* Layers */,
DA87A99A1DC9D88800810D09 /* Sources */,
1F7454AA1ED1DDBD00021D39 /* MGLLightTest.mm */,
- 353722EB1DF850ED004D2F3F /* MGLStyleValueTests.h */,
- 3599A3E71DF70E2000E77FB2 /* MGLStyleValueTests.m */,
- DA2207BB1DC076940002F84D /* MGLStyleValueTests.swift */,
);
name = Styling;
path = ../../darwin/test;
@@ -938,8 +973,10 @@
DAF0D80D1DFE0E5D00B28378 /* MGLPointCollection_Private.h */,
4049C2A71DB6D09B00B3F799 /* MGLPointCollection.mm */,
DAE6C3541CC31E0400DB3429 /* MGLPolygon.h */,
+ 9654C12C1FFC394700DB6A19 /* MGLPolygon_Private.h */,
DAE6C3771CC31E2A00DB3429 /* MGLPolygon.mm */,
DAE6C3551CC31E0400DB3429 /* MGLPolyline.h */,
+ 9654C12A1FFC38E000DB6A19 /* MGLPolyline_Private.h */,
DAE6C3781CC31E2A00DB3429 /* MGLPolyline.mm */,
DAE6C3561CC31E0400DB3429 /* MGLShape.h */,
408AA85A1DAEECF100022900 /* MGLShape_Private.h */,
@@ -970,10 +1007,6 @@
DAD1657F1CF4CF50001FF4B9 /* Categories */ = {
isa = PBXGroup;
children = (
- 408AA8601DAEED3300022900 /* MGLPolygon+MGLAdditions.h */,
- 408AA85C1DAEED3300022900 /* MGLPolygon+MGLAdditions.m */,
- 408AA8611DAEED3300022900 /* MGLPolyline+MGLAdditions.h */,
- 408AA85D1DAEED3300022900 /* MGLPolyline+MGLAdditions.m */,
40B77E431DB11BB0003DA2FE /* NSArray+MGLAdditions.h */,
40B77E421DB11BB0003DA2FE /* NSArray+MGLAdditions.mm */,
DAE6C37D1CC31E2A00DB3429 /* NSBundle+MGLAdditions.h */,
@@ -990,6 +1023,7 @@
408AA85E1DAEED3300022900 /* NSDictionary+MGLAdditions.mm */,
DAE6C37F1CC31E2A00DB3429 /* NSException+MGLAdditions.h */,
3508EC621D749D39009B0EE4 /* NSExpression+MGLAdditions.h */,
+ DAC25FC9200FD5E2009BE98E /* NSExpression+MGLPrivateAdditions.h */,
3508EC631D749D39009B0EE4 /* NSExpression+MGLAdditions.mm */,
352903991D6C63B80002C7DF /* NSPredicate+MGLAdditions.h */,
3529039A1D6C63B80002C7DF /* NSPredicate+MGLAdditions.mm */,
@@ -1145,15 +1179,16 @@
buildActionMask = 2147483647;
files = (
556660C61E1BEA0100E2C41B /* MGLFoundation.h in Headers */,
+ 07F8E2F71F674C8800F794BB /* MGLComputedShapeSource.h in Headers */,
DA8F258F1D51CA600010E6B5 /* MGLRasterStyleLayer.h in Headers */,
3508EC641D749D39009B0EE4 /* NSExpression+MGLAdditions.h in Headers */,
DAE6C38D1CC31E2A00DB3429 /* MGLOfflineRegion_Private.h in Headers */,
DA7DC9831DED647F0027472F /* MGLRasterSource_Private.h in Headers */,
1753ED401E53CE6100A9FD90 /* MGLConversion.h in Headers */,
- 408AA8651DAEEE3400022900 /* MGLPolygon+MGLAdditions.h in Headers */,
DA8F259C1D51CB000010E6B5 /* MGLStyleValue_Private.h in Headers */,
DAE6C35B1CC31E0400DB3429 /* MGLAnnotation.h in Headers */,
DAE6C3B61CC31EF300DB3429 /* MGLMapView_Private.h in Headers */,
+ DAF25716201901C200367EF5 /* MGLHillshadeStyleLayer.h in Headers */,
92092EF01F5EB10E00AF5130 /* MGLMapSnapshotter.h in Headers */,
3527428D1D4C24AB00A1ECE6 /* MGLCircleStyleLayer.h in Headers */,
DA00FC8A1D5EEAC3009AABC8 /* MGLAttributionInfo.h in Headers */,
@@ -1181,13 +1216,16 @@
DA7DC9811DED5F5C0027472F /* MGLVectorSource_Private.h in Headers */,
DAE6C3861CC31E2A00DB3429 /* MGLGeometry_Private.h in Headers */,
DAE6C3841CC31E2A00DB3429 /* MGLAccountManager_Private.h in Headers */,
+ DACA8622201920BE00E9693A /* MGLRasterDEMSource.h in Headers */,
DAE6C3691CC31E0400DB3429 /* MGLTypes.h in Headers */,
+ 07D9474D1F67441B00E37934 /* MGLAbstractShapeSource_Private.h in Headers */,
DAE6C3991CC31E2A00DB3429 /* NSException+MGLAdditions.h in Headers */,
DA8F25871D51C9E10010E6B5 /* MGLBackgroundStyleLayer.h in Headers */,
4049C2A51DB6CE7F00B3F799 /* MGLPointCollection.h in Headers */,
30E5781B1DAA857E0050F07E /* NSImage+MGLAdditions.h in Headers */,
DAE6C3661CC31E0400DB3429 /* MGLShape.h in Headers */,
DA551B831DB496AC0009AFAF /* MGLTileSource_Private.h in Headers */,
+ DAC25FCA200FD5E2009BE98E /* NSExpression+MGLPrivateAdditions.h in Headers */,
DA7262071DEEDD460043BB89 /* MGLOpenGLStyleLayer.h in Headers */,
352742811D4C243B00A1ECE6 /* MGLSource.h in Headers */,
DAE6C3C21CC31F4500DB3429 /* Mapbox.h in Headers */,
@@ -1196,6 +1234,7 @@
35602BFA1D3EA99F0050646F /* MGLFillStyleLayer.h in Headers */,
DA35A2A41CC9EB1A00E826B2 /* MGLCoordinateFormatter.h in Headers */,
35C5D8491D6DD66D00E95907 /* NSCompoundPredicate+MGLAdditions.h in Headers */,
+ 07D9474C1F67441500E37934 /* MGLAbstractShapeSource.h in Headers */,
DD0902B31DB1AC6400C5BDCE /* MGLNetworkConfiguration.h in Headers */,
DAE6C3621CC31E0400DB3429 /* MGLOverlay.h in Headers */,
DAE6C3651CC31E0400DB3429 /* MGLPolyline.h in Headers */,
@@ -1207,12 +1246,11 @@
1F7454A31ECFB00300021D39 /* MGLLight_Private.h in Headers */,
359819591E02F611008FC139 /* NSCoder+MGLAdditions.h in Headers */,
DAE6C38E1CC31E2A00DB3429 /* MGLOfflineStorage_Private.h in Headers */,
- 408AA8661DAEEE3600022900 /* MGLPolyline+MGLAdditions.h in Headers */,
DA87A9A01DC9DC6200810D09 /* MGLValueEvaluator.h in Headers */,
+ 8946239D200E744800DA8EF2 /* MGLHeatmapStyleLayer.h in Headers */,
DAE6C3601CC31E0400DB3429 /* MGLOfflineRegion.h in Headers */,
DAE6C3681CC31E0400DB3429 /* MGLTilePyramidOfflineRegion.h in Headers */,
DA35A2CF1CCAAED300E826B2 /* NSValue+MGLAdditions.h in Headers */,
- 1FCDF1421F2A4F3600A46694 /* MGLVectorSource+MGLAdditions.h in Headers */,
DAE6C3A61CC31E9400DB3429 /* MGLMapViewDelegate.h in Headers */,
DAE6C38B1CC31E2A00DB3429 /* MGLOfflinePack_Private.h in Headers */,
558DE7A61E56161C00C7916D /* MGLFoundation_Private.h in Headers */,
@@ -1236,8 +1274,10 @@
DAE6C3A51CC31E9400DB3429 /* MGLMapView+IBAdditions.h in Headers */,
DA35A2AD1CCA091800E826B2 /* MGLCompassDirectionFormatter.h in Headers */,
352742851D4C244700A1ECE6 /* MGLRasterSource.h in Headers */,
+ 9654C12D1FFC394700DB6A19 /* MGLPolygon_Private.h in Headers */,
408AA85B1DAEECFE00022900 /* MGLShape_Private.h in Headers */,
DACC22181CF3D4F700D220D9 /* MGLFeature_Private.h in Headers */,
+ 9654C12B1FFC38E000DB6A19 /* MGLPolyline_Private.h in Headers */,
DA6408D71DA4E5DA00908C90 /* MGLVectorStyleLayer.h in Headers */,
352742891D4C245800A1ECE6 /* MGLShapeSource.h in Headers */,
1F7454A41ECFB00300021D39 /* MGLLight.h in Headers */,
@@ -1329,7 +1369,7 @@
isa = PBXProject;
attributes = {
CLASSPREFIX = MBX;
- LastUpgradeCheck = 0800;
+ LastUpgradeCheck = 0920;
ORGANIZATIONNAME = Mapbox;
TargetAttributes = {
DA839E911CC2E3400062CAFB = {
@@ -1374,6 +1414,7 @@
hu,
bg,
ar,
+ he,
);
mainGroup = DA839E891CC2E3400062CAFB;
productRefGroup = DA839E931CC2E3400062CAFB /* Products */;
@@ -1397,6 +1438,7 @@
DA839EA01CC2E3400062CAFB /* MapDocument.xib in Resources */,
353BAEF81D6463B8009A8DA9 /* amsterdam.geojson in Resources */,
96E027311E57C9A7004B8E66 /* Localizable.strings in Resources */,
+ 89462399200D199100DA8EF2 /* heatmap.json in Resources */,
DA839EA51CC2E3400062CAFB /* MainMenu.xib in Resources */,
DA5589771D320C41006B7F64 /* wms.json in Resources */,
DAE6C2E21CC304F900DB3429 /* Credits.rtf in Resources */,
@@ -1454,13 +1496,13 @@
07A019EF1ED665CD00ACD43E /* MGLImageSource.mm in Sources */,
92092EF11F5EB10E00AF5130 /* MGLMapSnapshotter.mm in Sources */,
40ABDB561DB0022100372083 /* NSImage+MGLAdditions.mm in Sources */,
+ DAF25715201901C200367EF5 /* MGLHillshadeStyleLayer.mm in Sources */,
DAE6C3901CC31E2A00DB3429 /* MGLPointAnnotation.mm in Sources */,
DAE6C3981CC31E2A00DB3429 /* NSBundle+MGLAdditions.m in Sources */,
DAE6C3B71CC31EF300DB3429 /* MGLMapView.mm in Sources */,
40B77E461DB11BCD003DA2FE /* NSArray+MGLAdditions.mm in Sources */,
DAE6C38C1CC31E2A00DB3429 /* MGLOfflinePack.mm in Sources */,
35D65C5B1D65AD5500722C23 /* NSDate+MGLAdditions.mm in Sources */,
- 1FCDF1431F2A4F3600A46694 /* MGLVectorSource+MGLAdditions.m in Sources */,
DD0902B21DB1AC6400C5BDCE /* MGLNetworkConfiguration.m in Sources */,
1F7454A51ECFB00300021D39 /* MGLLight.mm in Sources */,
DAE6C3B11CC31EF300DB3429 /* MGLAnnotationImage.m in Sources */,
@@ -1475,8 +1517,10 @@
558DE7A71E56161C00C7916D /* MGLFoundation.mm in Sources */,
DAE6C39D1CC31E2A00DB3429 /* NSString+MGLAdditions.m in Sources */,
3598195A1E02F611008FC139 /* NSCoder+MGLAdditions.mm in Sources */,
+ 07D9474B1F6743F000E37934 /* MGLAbstractShapeSource.mm in Sources */,
DAE6C3941CC31E2A00DB3429 /* MGLStyle.mm in Sources */,
DAE6C3871CC31E2A00DB3429 /* MGLGeometry.mm in Sources */,
+ 894623A0200E748000DA8EF2 /* MGLHeatmapStyleLayer.mm in Sources */,
3527428E1D4C24AB00A1ECE6 /* MGLCircleStyleLayer.mm in Sources */,
35602C011D3EA9B40050646F /* MGLForegroundStyleLayer.mm in Sources */,
408AA86A1DAEEE5D00022900 /* NSDictionary+MGLAdditions.mm in Sources */,
@@ -1486,6 +1530,7 @@
DA35A2D01CCAAED300E826B2 /* NSValue+MGLAdditions.m in Sources */,
3538AA241D542685008EC33D /* MGLStyleLayer.mm in Sources */,
DA35A2C01CCA9B1A00E826B2 /* MGLClockDirectionFormatter.m in Sources */,
+ 07F8E2F81F674C9000F794BB /* MGLComputedShapeSource.mm in Sources */,
DAE6C3BA1CC31EF300DB3429 /* MGLOpenGLLayer.mm in Sources */,
DAE6C38A1CC31E2A00DB3429 /* MGLMultiPoint.mm in Sources */,
DAE6C3961CC31E2A00DB3429 /* MGLTypes.m in Sources */,
@@ -1502,7 +1547,6 @@
DAE6C38F1CC31E2A00DB3429 /* MGLOfflineStorage.mm in Sources */,
DAED38601D62CED700D7640F /* NSURL+MGLAdditions.m in Sources */,
35C5D84A1D6DD66D00E95907 /* NSCompoundPredicate+MGLAdditions.mm in Sources */,
- 408AA8681DAEEE5200022900 /* MGLPolygon+MGLAdditions.m in Sources */,
DAE6C3951CC31E2A00DB3429 /* MGLTilePyramidOfflineRegion.mm in Sources */,
DAE6C3851CC31E2A00DB3429 /* MGLAccountManager.m in Sources */,
DA00FC8B1D5EEAC3009AABC8 /* MGLAttributionInfo.mm in Sources */,
@@ -1513,8 +1557,8 @@
DAD165751CF4CD7A001FF4B9 /* MGLShapeCollection.mm in Sources */,
35C5D8481D6DD66D00E95907 /* NSComparisonPredicate+MGLAdditions.mm in Sources */,
DA35A2AE1CCA091800E826B2 /* MGLCompassDirectionFormatter.m in Sources */,
+ DACA8623201920BE00E9693A /* MGLRasterDEMSource.mm in Sources */,
DA8F258C1D51CA540010E6B5 /* MGLLineStyleLayer.mm in Sources */,
- 408AA8691DAEEE5500022900 /* MGLPolyline+MGLAdditions.m in Sources */,
DA8F25941D51CA750010E6B5 /* MGLSymbolStyleLayer.mm in Sources */,
3529039C1D6C63B80002C7DF /* NSPredicate+MGLAdditions.mm in Sources */,
DA8F25981D51CAC70010E6B5 /* MGLVectorSource.mm in Sources */,
@@ -1531,6 +1575,7 @@
DAE6C3D41CC34C9900DB3429 /* MGLOfflineRegionTests.m in Sources */,
DAE6C3D61CC34C9900DB3429 /* MGLStyleTests.mm in Sources */,
1F7454AB1ED1DDBD00021D39 /* MGLLightTest.mm in Sources */,
+ 07A240941F675674002C8210 /* MGLComputedShapeSourceTests.m in Sources */,
DAEDC4371D606291000224FF /* MGLAttributionButtonTests.m in Sources */,
920A3E591E6F859D00C16EFC /* MGLSourceQueryTests.m in Sources */,
DA35A2B61CCA14D700E826B2 /* MGLCompassDirectionFormatterTests.m in Sources */,
@@ -1538,10 +1583,13 @@
DAE6C3D21CC34C9900DB3429 /* MGLGeometryTests.mm in Sources */,
DA87A9A41DCACC5000810D09 /* MGLSymbolStyleLayerTests.mm in Sources */,
40E1601D1DF217D6005EA6D9 /* MGLStyleLayerTests.m in Sources */,
+ 170A82BF201BDD1B00943087 /* MGLHeatmapStyleLayerTests.mm in Sources */,
1F95931B1E6DE2B600D5B294 /* MGLNSDateAdditionsTests.mm in Sources */,
+ DAF25721201902C100367EF5 /* MGLHillshadeStyleLayerTests.mm in Sources */,
DA87A9A61DCACC5000810D09 /* MGLCircleStyleLayerTests.mm in Sources */,
DA87A99E1DC9DC2100810D09 /* MGLPredicateTests.mm in Sources */,
DD58A4C91D822C6700E1F038 /* MGLExpressionTests.mm in Sources */,
+ 170A82C4201FB6EC00943087 /* MGLHeatmapColorTests.mm in Sources */,
4031ACFC1E9EB3C100A3EA26 /* MGLMapViewDelegateIntegrationTests.swift in Sources */,
4031AD031E9FD6AA00A3EA26 /* MGLSDKTestHelpers.swift in Sources */,
DA87A9A71DCACC5000810D09 /* MGLBackgroundStyleLayerTests.mm in Sources */,
@@ -1558,12 +1606,10 @@
55E2AD111E5B0A6900E8C587 /* MGLOfflineStorageTests.mm in Sources */,
3526EABD1DF9B19800006B43 /* MGLCodingTests.m in Sources */,
DA87A9A21DC9DCF100810D09 /* MGLFillStyleLayerTests.mm in Sources */,
- 3599A3E81DF70E2000E77FB2 /* MGLStyleValueTests.m in Sources */,
DA57D4B11EBC699800793288 /* MGLDocumentationGuideTests.swift in Sources */,
DAEDC4321D6033F1000224FF /* MGLAttributionInfoTests.m in Sources */,
DA0CD58E1CF56F5800A5F5A5 /* MGLFeatureTests.mm in Sources */,
556660D61E1D07E400E2C41B /* MGLVersionNumber.m in Sources */,
- DA2207BC1DC076940002F84D /* MGLStyleValueTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1606,6 +1652,7 @@
DA704CBE1F637531004B3F28 /* hu */,
DA3389611FA3EDCE001EA329 /* bg */,
DA80E95D1FE84A300065FC9B /* ar */,
+ DACBC6082011885800C4D7E2 /* he */,
);
name = Localizable.strings;
sourceTree = "<group>";
@@ -1656,6 +1703,7 @@
DAE8CCAC1E6E8B8D009B5CB0 /* nl */,
DA704CBF1F637548004B3F28 /* hu */,
DA3389641FA3EE00001EA329 /* bg */,
+ DACBC60A2011889E00C4D7E2 /* he */,
);
name = Localizable.strings;
sourceTree = "<group>";
@@ -1678,6 +1726,7 @@
DA704CC61F666385004B3F28 /* uk */,
DA3389601FA3EAC4001EA329 /* pt-BR */,
DA3389621FA3EDEF001EA329 /* bg */,
+ DACBC6092011888C00C4D7E2 /* he */,
);
name = Foundation.strings;
sourceTree = "<group>";
@@ -1717,7 +1766,9 @@
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
@@ -1725,12 +1776,19 @@
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
+ CURRENT_COMMIT_HASH = deadbeef;
+ CURRENT_SEMANTIC_VERSION = 1.0.0;
+ CURRENT_SHORT_VERSION = 1.0;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
@@ -1765,7 +1823,9 @@
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
@@ -1773,12 +1833,19 @@
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
+ CURRENT_COMMIT_HASH = deadbeef;
+ CURRENT_SEMANTIC_VERSION = 1.0.0;
+ CURRENT_SHORT_VERSION = 1.0;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
@@ -1792,6 +1859,7 @@
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.10;
MTL_ENABLE_DEBUG_INFO = NO;
+ SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SYMROOT = "$(PROJECT_DIR)/cmake";
};
name = Release;
diff --git a/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/CI.xcscheme b/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/CI.xcscheme
index a049928fdd..335441cc23 100644
--- a/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/CI.xcscheme
+++ b/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/CI.xcscheme
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "0810"
+ LastUpgradeVersion = "0920"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -56,7 +56,7 @@
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
- BlueprintIdentifier = "8E8A234D3E364CDFA2918983"
+ BlueprintIdentifier = "E933DEF922EF4BC1AD735333"
BuildableName = "mbgl-test"
BlueprintName = "mbgl-test"
ReferencedContainer = "container:../../build/macos/mbgl.xcodeproj">
@@ -70,7 +70,7 @@
buildForAnalyzing = "NO">
<BuildableReference
BuildableIdentifier = "primary"
- BlueprintIdentifier = "11FCEF1E29744645907C1E4B"
+ BlueprintIdentifier = "1D3EE78DAFFA4A42AFD7160D"
BuildableName = "mbgl-benchmark"
BlueprintName = "mbgl-benchmark"
ReferencedContainer = "container:../../build/macos/mbgl.xcodeproj">
@@ -82,6 +82,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
@@ -111,6 +112,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
diff --git a/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme b/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme
index 95351f7f04..4dadaee743 100644
--- a/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme
+++ b/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "0810"
+ LastUpgradeVersion = "0920"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -40,6 +40,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
@@ -69,6 +70,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
diff --git a/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/macosapp.xcscheme b/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/macosapp.xcscheme
index a58ef5c9cf..b0ce01fbf0 100644
--- a/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/macosapp.xcscheme
+++ b/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/macosapp.xcscheme
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "0810"
+ LastUpgradeVersion = "0920"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -26,6 +26,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
@@ -55,6 +56,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
diff --git a/platform/macos/sdk/Info.plist b/platform/macos/sdk/Info.plist
index 3b2b38a58a..a3a7d6b902 100644
--- a/platform/macos/sdk/Info.plist
+++ b/platform/macos/sdk/Info.plist
@@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>CFBundleShortVersionString</key>
- <string>1.0</string>
+ <string>$(CURRENT_SHORT_VERSION)</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
@@ -20,5 +20,9 @@
<string>????</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
+ <key>MGLCommitHash</key>
+ <string>$(CURRENT_COMMIT_HASH)</string>
+ <key>MGLSemanticVersionString</key>
+ <string>$(CURRENT_SEMANTIC_VERSION)</string>
</dict>
</plist>
diff --git a/platform/macos/sdk/es.lproj/Localizable.strings b/platform/macos/sdk/es.lproj/Localizable.strings
index bf61010704..feb5e934fe 100644
--- a/platform/macos/sdk/es.lproj/Localizable.strings
+++ b/platform/macos/sdk/es.lproj/Localizable.strings
Binary files differ
diff --git a/platform/macos/sdk/fr.lproj/Localizable.strings b/platform/macos/sdk/fr.lproj/Localizable.strings
index 9d73f23d05..da60467659 100644
--- a/platform/macos/sdk/fr.lproj/Localizable.strings
+++ b/platform/macos/sdk/fr.lproj/Localizable.strings
@@ -1,17 +1,17 @@
-/* User-friendly error description */
-"LOAD_MAP_FAILED_DESC" = "La carte n’a pas pu être chargée car une erreur inconnue est survenue.";
+/* User-friendly error description */
+"LOAD_MAP_FAILED_DESC" = "Le chargement de la carte a échoué car une erreur inconnue est survenue.";
/* User-friendly error description */
-"LOAD_STYLE_FAILED_DESC" = "La carte n’a pas pu être chargée car le style ne peut pas être chargé.";
+"LOAD_STYLE_FAILED_DESC" = "Le chargement de la carte a échoué car le style n’a pas pu être chargé";
/* Accessibility title */
"MAP_A11Y_TITLE" = "Mapbox";
/* User-friendly error description */
-"PARSE_STYLE_FAILED_DESC" = "La carte n’a pas pu être chargée car le style est corrompu.";
+"PARSE_STYLE_FAILED_DESC" = "Le chargement de la carte a échoué car le style est corrompu.";
/* User-friendly error description */
-"STYLE_NOT_FOUND_DESC" = "La carte n’a pas pu être chargée car le style n’a pas été trouvé ou est incompatible.";
+"STYLE_NOT_FOUND_DESC" = "Le chargement de la carte a échoué car le style n’a pas été trouvé ou est incompatible.";
/* Label of Zoom In button */
"ZOOM_IN_LABEL" = "+";
diff --git a/platform/macos/sdk/he.lproj/Localizable.strings b/platform/macos/sdk/he.lproj/Localizable.strings
new file mode 100644
index 0000000000..0d9564894e
--- /dev/null
+++ b/platform/macos/sdk/he.lproj/Localizable.strings
@@ -0,0 +1,27 @@
+/* User-friendly error description */
+"LOAD_MAP_FAILED_DESC" = "טעינת המפה נכשלה עכב שגיאה לא ידועה.";
+
+/* User-friendly error description */
+"LOAD_STYLE_FAILED_DESC" = "טעינת המפה נכשלה - לא ניתן לטעון את הסגנון.";
+
+/* Accessibility title */
+"MAP_A11Y_TITLE" = "Mapbox";
+
+/* User-friendly error description */
+"PARSE_STYLE_FAILED_DESC" = "טעינת המפה נכשלה - הסגנון פגום.";
+
+/* User-friendly error description */
+"STYLE_NOT_FOUND_DESC" = "טעינת המפה נכשלה - לא ניתן למצוא את הסגנון או שהסגנון אינו תואם.";
+
+/* Label of Zoom In button */
+"ZOOM_IN_LABEL" = "+";
+
+/* Tooltip of Zoom In button */
+"ZOOM_IN_TOOLTIP" = "Zoom In";
+
+/* Label of Zoom Out button; U+2212 MINUS SIGN */
+"ZOOM_OUT_LABEL" = "−";
+
+/* Tooltip of Zoom Out button */
+"ZOOM_OUT_TOOLTIP" = "Zoom Out";
+
diff --git a/platform/macos/sdk/sv.lproj/Localizable.strings b/platform/macos/sdk/sv.lproj/Localizable.strings
index c94660d7d4..0612033f3d 100644
--- a/platform/macos/sdk/sv.lproj/Localizable.strings
+++ b/platform/macos/sdk/sv.lproj/Localizable.strings
@@ -1,27 +1,27 @@
-/* User-friendly error description */
-"LOAD_MAP_FAILED_DESC" = "Kartan kunde inte laddas på grund av ett okänt fel.";
+/* User-friendly error description */
+"LOAD_MAP_FAILED_DESC" = "Misslyckades med att ladda kartan på grund av ett okänt fel.";
/* User-friendly error description */
-"LOAD_STYLE_FAILED_DESC" = "Kartan kunde inte laddas på grund av att stilen inte kunde laddas.";
+"LOAD_STYLE_FAILED_DESC" = "Misslyckades med att ladda kartan för att kartstilen kunde inte laddas.";
/* Accessibility title */
"MAP_A11Y_TITLE" = "Mapbox";
/* User-friendly error description */
-"PARSE_STYLE_FAILED_DESC" = "Kartan kunde inte laddas på grund av att stilen är skadad.";
+"PARSE_STYLE_FAILED_DESC" = "Kunde inte ladda kartan för att kartstilen är korrupt.";
/* User-friendly error description */
-"STYLE_NOT_FOUND_DESC" = "Kartan kunde inte laddas på grund av att stilen saknas eller är inkompatibel.";
+"STYLE_NOT_FOUND_DESC" = "Kunde inte ladda kartan för att kartstilen kunde inte hittas eller är den inkompatibel.";
/* Label of Zoom In button */
"ZOOM_IN_LABEL" = "+";
/* Tooltip of Zoom In button */
-"ZOOM_IN_TOOLTIP" = "Zooma in";
+"ZOOM_IN_TOOLTIP" = "Zooma In";
/* Label of Zoom Out button; U+2212 MINUS SIGN */
"ZOOM_OUT_LABEL" = "−";
/* Tooltip of Zoom Out button */
-"ZOOM_OUT_TOOLTIP" = "Zooma ut";
+"ZOOM_OUT_TOOLTIP" = "Zooma Ut";
diff --git a/platform/macos/sdk/uk.lproj/Localizable.strings b/platform/macos/sdk/uk.lproj/Localizable.strings
index 07782e81d5..74ec8ebaee 100644
--- a/platform/macos/sdk/uk.lproj/Localizable.strings
+++ b/platform/macos/sdk/uk.lproj/Localizable.strings
@@ -1,4 +1,4 @@
-/* User-friendly error description */
+/* User-friendly error description */
"LOAD_MAP_FAILED_DESC" = "Неможливо завантажити мапу через невідому помилку.";
/* User-friendly error description */
diff --git a/platform/macos/sdk/vi.lproj/Localizable.strings b/platform/macos/sdk/vi.lproj/Localizable.strings
index f130d2f350..11256ca5e9 100644
--- a/platform/macos/sdk/vi.lproj/Localizable.strings
+++ b/platform/macos/sdk/vi.lproj/Localizable.strings
Binary files differ
diff --git a/platform/macos/src/MGLMapView.mm b/platform/macos/src/MGLMapView.mm
index f2c6afb2f7..9ec9012198 100644
--- a/platform/macos/src/MGLMapView.mm
+++ b/platform/macos/src/MGLMapView.mm
@@ -2887,7 +2887,7 @@ public:
[nativeView sourceDidChange:nativeSource];
}
- mbgl::gl::ProcAddress initializeExtension(const char* name) override {
+ mbgl::gl::ProcAddress getExtensionFunctionPointer(const char* name) override {
static CFBundleRef framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl"));
if (!framework) {
throw std::runtime_error("Failed to load OpenGL framework.");
diff --git a/platform/macos/src/Mapbox.h b/platform/macos/src/Mapbox.h
index a082a4771e..0e4b546cf7 100644
--- a/platform/macos/src/Mapbox.h
+++ b/platform/macos/src/Mapbox.h
@@ -44,12 +44,17 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[];
#import "MGLRasterStyleLayer.h"
#import "MGLCircleStyleLayer.h"
#import "MGLBackgroundStyleLayer.h"
+#import "MGLHeatmapStyleLayer.h"
+#import "MGLHillshadeStyleLayer.h"
#import "MGLOpenGLStyleLayer.h"
#import "MGLSource.h"
#import "MGLTileSource.h"
#import "MGLVectorSource.h"
#import "MGLShapeSource.h"
+#import "MGLAbstractShapeSource.h"
+#import "MGLComputedShapeSource.h"
#import "MGLRasterSource.h"
+#import "MGLRasterDEMSource.h"
#import "MGLImageSource.h"
#import "MGLTilePyramidOfflineRegion.h"
#import "MGLTypes.h"
@@ -57,3 +62,4 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[];
#import "MGLStyleValue.h"
#import "MGLAttributionInfo.h"
#import "MGLMapSnapshotter.h"
+#import "NSExpression+MGLAdditions.h"
diff --git a/platform/macos/src/NSImage+MGLAdditions.mm b/platform/macos/src/NSImage+MGLAdditions.mm
index 7500a8a207..2666dfe790 100644
--- a/platform/macos/src/NSImage+MGLAdditions.mm
+++ b/platform/macos/src/NSImage+MGLAdditions.mm
@@ -5,7 +5,7 @@
@implementation NSImage (MGLAdditions)
- (nullable instancetype)initWithMGLPremultipliedImage:(mbgl::PremultipliedImage&&)src {
- CGImageRef image = CGImageFromMGLPremultipliedImage(std::move(src));
+ CGImageRef image = CGImageCreateWithMGLPremultipliedImage(std::move(src));
if (!image) {
return nil;
}
@@ -16,7 +16,7 @@
}
- (nullable instancetype)initWithMGLStyleImage:(const mbgl::style::Image *)styleImage {
- CGImageRef image = CGImageFromMGLPremultipliedImage(styleImage->getImage().clone());
+ CGImageRef image = CGImageCreateWithMGLPremultipliedImage(styleImage->getImage().clone());
if (!image) {
return nil;
}
diff --git a/platform/node/CHANGELOG.md b/platform/node/CHANGELOG.md
index 1d166a9a79..feb2b4185d 100644
--- a/platform/node/CHANGELOG.md
+++ b/platform/node/CHANGELOG.md
@@ -1,3 +1,14 @@
+# master
+- The `Map` constructor now accepts a `mode` option which can be either `"static"` (default) or `"tile"`. It must be set to `"tile"` when rendering individual tiles in order for the symbols to match across tiles.
+
+# 3.5.8 - October 19, 2017
+- Fixes an issue that causes memory leaks when not deleting the frontend object
+ in NodeMap::release()
+- Fixes a crash in Earcut: [#10245](https://github.com/mapbox/mapbox-gl-native/pull/10245)
+
+# 3.5.7 - October 9, 2017
+- Fixed an issue causing synchronous resource requests to stall [#10153](https://github.com/mapbox/mapbox-gl-native/pull/10153)
+
# 3.5.6 - September 29, 2017
- Protects against requests which throw [#9554](https://github.com/mapbox/mapbox-gl-native/pull/9554)
- Fixed an issue around reusing a map object [#9554](https://github.com/mapbox/mapbox-gl-native/pull/9554)
diff --git a/platform/node/DEVELOPING.md b/platform/node/DEVELOPING.md
index 4e8da881a8..b313d75c13 100644
--- a/platform/node/DEVELOPING.md
+++ b/platform/node/DEVELOPING.md
@@ -1,6 +1,6 @@
# Developing the Mapbox GL Native Node.js module
-This document explains how to build the [Node.js](https://nodejs.org/) bindings for [../../README.md](Mapbox GL Native) for contributing to the development of the bindings themselves. If you just want to use the module, you can simply install it via `npm`; see [README.md](README.md) for installation and usage instructions.
+This document explains how to build the [Node.js](https://nodejs.org/) bindings for [Mapbox GL Native](../../README.md) for contributing to the development of the bindings themselves. If you just want to use the module, you can simply install it via `npm`; see [README.md](README.md) for installation and usage instructions.
## Building
diff --git a/platform/node/src/node_conversion.hpp b/platform/node/src/node_conversion.hpp
index d266745548..7c5bbf4386 100644
--- a/platform/node/src/node_conversion.hpp
+++ b/platform/node/src/node_conversion.hpp
@@ -9,111 +9,136 @@
#include <mbgl/util/optional.hpp>
#include <mbgl/util/feature.hpp>
#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/geojson.hpp>
namespace mbgl {
namespace style {
namespace conversion {
-inline bool isUndefined(v8::Local<v8::Value> value) {
- Nan::HandleScope scope;
- return value->IsUndefined() || value->IsNull();
-}
+template <>
+class ConversionTraits<v8::Local<v8::Value>> {
+public:
+ static bool isUndefined(const v8::Local<v8::Value>& value) {
+ Nan::HandleScope scope;
+ return value->IsUndefined() || value->IsNull();
+ }
-inline bool isArray(v8::Local<v8::Value> value) {
- Nan::HandleScope scope;
- return value->IsArray();
-}
+ static bool isArray(const v8::Local<v8::Value>& value) {
+ Nan::HandleScope scope;
+ return value->IsArray();
+ }
-inline std::size_t arrayLength(v8::Local<v8::Value> value) {
- Nan::HandleScope scope;
- return value.As<v8::Array>()->Length();
-}
+ static std::size_t arrayLength(const v8::Local<v8::Value>& value) {
+ Nan::HandleScope scope;
+ // const_cast because v8::Local<T>::As is not marked const until node v8.0
+ v8::Local<v8::Array> array = const_cast<v8::Local<v8::Value>&>(value).As<v8::Array>();
+ return array->Length();
+ }
-inline v8::Local<v8::Value> arrayMember(v8::Local<v8::Value> value, std::size_t i) {
- Nan::EscapableHandleScope scope;
- return scope.Escape(Nan::Get(value.As<v8::Array>(), i).ToLocalChecked());
-}
+ static v8::Local<v8::Value> arrayMember(const v8::Local<v8::Value>& value, std::size_t i) {
+ Nan::EscapableHandleScope scope;
+ // const_cast because v8::Local<T>::As is not marked const until node v8.0
+ v8::Local<v8::Array> array = const_cast<v8::Local<v8::Value>&>(value).As<v8::Array>();
+ return scope.Escape(Nan::Get(array, i).ToLocalChecked());
+ }
-inline bool isObject(v8::Local<v8::Value> value) {
- Nan::HandleScope scope;
- return value->IsObject() && !value->IsArray();
-}
+ static bool isObject(const v8::Local<v8::Value>& value) {
+ Nan::HandleScope scope;
+ return value->IsObject() && !value->IsArray();
+ }
-inline optional<v8::Local<v8::Value>> objectMember(v8::Local<v8::Value> value, const char * name) {
- Nan::EscapableHandleScope scope;
- if (!Nan::Has(Nan::To<v8::Object>(value).ToLocalChecked(), Nan::New(name).ToLocalChecked()).FromJust()) {
- return {};
+ static optional<v8::Local<v8::Value>> objectMember(const v8::Local<v8::Value>& value, const char * name) {
+ Nan::EscapableHandleScope scope;
+ if (!Nan::Has(Nan::To<v8::Object>(value).ToLocalChecked(), Nan::New(name).ToLocalChecked()).FromJust()) {
+ return {};
+ }
+ Nan::MaybeLocal<v8::Value> result = Nan::Get(Nan::To<v8::Object>(value).ToLocalChecked(), Nan::New(name).ToLocalChecked());
+ if (result.IsEmpty()) {
+ return {};
+ }
+ return {scope.Escape(result.ToLocalChecked())};
}
- Nan::MaybeLocal<v8::Value> result = Nan::Get(Nan::To<v8::Object>(value).ToLocalChecked(), Nan::New(name).ToLocalChecked());
- if (result.IsEmpty()) {
+
+ template <class Fn>
+ static optional<Error> eachMember(const v8::Local<v8::Value>& value, Fn&& fn) {
+ Nan::HandleScope scope;
+ v8::Local<v8::Array> names = Nan::GetOwnPropertyNames(Nan::To<v8::Object>(value).ToLocalChecked()).ToLocalChecked();
+ for (uint32_t i = 0; i < names->Length(); ++i) {
+ v8::Local<v8::Value> k = Nan::Get(names, i).ToLocalChecked();
+ v8::Local<v8::Value> v = Nan::Get(Nan::To<v8::Object>(value).ToLocalChecked(), k).ToLocalChecked();
+ optional<Error> result = fn(*Nan::Utf8String(k), std::move(v));
+ if (result) {
+ return result;
+ }
+ }
return {};
}
- return scope.Escape(result.ToLocalChecked());
-}
-template <class Fn>
-optional<Error> eachMember(v8::Local<v8::Value> value, Fn&& fn) {
- Nan::HandleScope scope;
- v8::Local<v8::Array> names = Nan::GetOwnPropertyNames(Nan::To<v8::Object>(value).ToLocalChecked()).ToLocalChecked();
- for (uint32_t i = 0; i < names->Length(); ++i) {
- v8::Local<v8::Value> k = Nan::Get(names, i).ToLocalChecked();
- v8::Local<v8::Value> v = Nan::Get(Nan::To<v8::Object>(value).ToLocalChecked(), k).ToLocalChecked();
- optional<Error> result = fn(*Nan::Utf8String(k), v);
- if (result) {
- return result;
+ static optional<bool> toBool(const v8::Local<v8::Value>& value) {
+ Nan::HandleScope scope;
+ if (!value->IsBoolean()) {
+ return {};
}
+ return value->BooleanValue();
}
- return {};
-}
-inline optional<bool> toBool(v8::Local<v8::Value> value) {
- Nan::HandleScope scope;
- if (!value->IsBoolean()) {
- return {};
+ static optional<float> toNumber(const v8::Local<v8::Value>& value) {
+ Nan::HandleScope scope;
+ if (!value->IsNumber()) {
+ return {};
+ }
+ return value->NumberValue();
}
- return value->BooleanValue();
-}
-inline optional<float> toNumber(v8::Local<v8::Value> value) {
- Nan::HandleScope scope;
- if (!value->IsNumber()) {
- return {};
+ static optional<double> toDouble(const v8::Local<v8::Value>& value) {
+ Nan::HandleScope scope;
+ if (!value->IsNumber()) {
+ return {};
+ }
+ return value->NumberValue();
}
- return value->NumberValue();
-}
-inline optional<double> toDouble(v8::Local<v8::Value> value) {
- Nan::HandleScope scope;
- if (!value->IsNumber()) {
- return {};
+ static optional<std::string> toString(const v8::Local<v8::Value>& value) {
+ Nan::HandleScope scope;
+ if (!value->IsString()) {
+ return {};
+ }
+ return std::string(*Nan::Utf8String(value));
}
- return value->NumberValue();
-}
-inline optional<std::string> toString(v8::Local<v8::Value> value) {
- Nan::HandleScope scope;
- if (!value->IsString()) {
- return {};
+ static optional<Value> toValue(const v8::Local<v8::Value>& value) {
+ if (value->IsFalse()) {
+ return { false };
+ } else if (value->IsTrue()) {
+ return { true };
+ } else if (value->IsString()) {
+ return { std::string(*Nan::Utf8String(value)) };
+ } else if (value->IsUint32()) {
+ return { std::uint64_t(value->Uint32Value()) };
+ } else if (value->IsInt32()) {
+ return { std::int64_t(value->Int32Value()) };
+ } else if (value->IsNumber()) {
+ return { value->NumberValue() };
+ } else {
+ return {};
+ }
}
- return std::string(*Nan::Utf8String(value));
-}
-inline optional<Value> toValue(v8::Local<v8::Value> value) {
- if (value->IsFalse()) {
- return { false };
- } else if (value->IsTrue()) {
- return { true };
- } else if (value->IsString()) {
- return { std::string(*Nan::Utf8String(value)) };
- } else if (value->IsUint32()) {
- return { std::uint64_t(value->Uint32Value()) };
- } else if (value->IsInt32()) {
- return { std::int64_t(value->Int32Value()) };
- } else if (value->IsNumber()) {
- return { value->NumberValue() };
- } else {
- return {};
+ static optional<GeoJSON> toGeoJSON(const v8::Local<v8::Value>& value, Error& error) {
+ try {
+ Nan::JSON JSON;
+ std::string string = *Nan::Utf8String(JSON.Stringify(value->ToObject()).ToLocalChecked());
+ return parseGeoJSON(string, error);
+ } catch (const std::exception& ex) {
+ error = { ex.what() };
+ return {};
+ }
}
+};
+
+template <class T, class...Args>
+optional<T> convert(const v8::Local<v8::Value>& value, Error& error, Args&&...args) {
+ return convert<T>(Convertible(value), error, std::forward<Args>(args)...);
}
} // namespace conversion
diff --git a/platform/node/src/node_expression.cpp b/platform/node/src/node_expression.cpp
new file mode 100644
index 0000000000..84515060a3
--- /dev/null
+++ b/platform/node/src/node_expression.cpp
@@ -0,0 +1,241 @@
+#include "node_conversion.hpp"
+#include "node_expression.hpp"
+#include "node_feature.hpp"
+
+#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/style/expression/is_constant.hpp>
+#include <mbgl/style/conversion/geojson.hpp>
+#include <mbgl/util/geojson.hpp>
+#include <nan.h>
+
+using namespace mbgl::style;
+using namespace mbgl::style::expression;
+
+namespace node_mbgl {
+
+Nan::Persistent<v8::Function> NodeExpression::constructor;
+
+void NodeExpression::Init(v8::Local<v8::Object> target) {
+ v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New);
+ tpl->SetClassName(Nan::New("Expression").ToLocalChecked());
+ tpl->InstanceTemplate()->SetInternalFieldCount(1); // what is this doing?
+
+ Nan::SetPrototypeMethod(tpl, "evaluate", Evaluate);
+ Nan::SetPrototypeMethod(tpl, "getType", GetType);
+ Nan::SetPrototypeMethod(tpl, "isFeatureConstant", IsFeatureConstant);
+ Nan::SetPrototypeMethod(tpl, "isZoomConstant", IsZoomConstant);
+
+ Nan::SetPrototypeMethod(tpl, "serialize", Serialize);
+
+ Nan::SetMethod(tpl, "parse", Parse);
+
+ constructor.Reset(tpl->GetFunction()); // what is this doing?
+ Nan::Set(target, Nan::New("Expression").ToLocalChecked(), tpl->GetFunction());
+}
+
+type::Type parseType(v8::Local<v8::Object> type) {
+ static std::unordered_map<std::string, type::Type> types = {
+ {"string", type::String},
+ {"number", type::Number},
+ {"noolean", type::Boolean},
+ {"object", type::Object},
+ {"color", type::Color},
+ {"value", type::Value}
+ };
+
+ v8::Local<v8::Value> v8kind = Nan::Get(type, Nan::New("kind").ToLocalChecked()).ToLocalChecked();
+ std::string kind(*v8::String::Utf8Value(v8kind));
+
+ if (kind == "array") {
+ type::Type itemType = parseType(Nan::Get(type, Nan::New("itemType").ToLocalChecked()).ToLocalChecked()->ToObject());
+ mbgl::optional<std::size_t> N;
+
+ v8::Local<v8::String> Nkey = Nan::New("N").ToLocalChecked();
+ if (Nan::Has(type, Nkey).FromMaybe(false)) {
+ N = Nan::Get(type, Nkey).ToLocalChecked()->ToInt32()->Value();
+ }
+ return type::Array(itemType, N);
+ }
+
+ return types[kind];
+}
+
+void NodeExpression::Parse(const Nan::FunctionCallbackInfo<v8::Value>& info) {
+ v8::Local<v8::Function> cons = Nan::New(constructor);
+
+ if (info.Length() < 1 || info[0]->IsUndefined()) {
+ return Nan::ThrowTypeError("Requires a JSON style expression argument.");
+ }
+
+ mbgl::optional<type::Type> expected;
+ if (info.Length() > 1 && info[1]->IsObject()) {
+ expected = parseType(info[1]->ToObject());
+ }
+
+ auto expr = info[0];
+
+ try {
+ ParsingContext ctx(expected);
+ ParseResult parsed = ctx.parse(mbgl::style::conversion::Convertible(expr));
+ if (parsed) {
+ assert(ctx.getErrors().size() == 0);
+ auto nodeExpr = new NodeExpression(std::move(*parsed));
+ const int argc = 0;
+ v8::Local<v8::Value> argv[0] = {};
+ auto wrapped = Nan::NewInstance(cons, argc, argv).ToLocalChecked();
+ nodeExpr->Wrap(wrapped);
+ info.GetReturnValue().Set(wrapped);
+ return;
+ }
+
+ v8::Local<v8::Array> result = Nan::New<v8::Array>();
+ for (std::size_t i = 0; i < ctx.getErrors().size(); i++) {
+ const auto& error = ctx.getErrors()[i];
+ v8::Local<v8::Object> err = Nan::New<v8::Object>();
+ Nan::Set(err,
+ Nan::New("key").ToLocalChecked(),
+ Nan::New(error.key.c_str()).ToLocalChecked());
+ Nan::Set(err,
+ Nan::New("error").ToLocalChecked(),
+ Nan::New(error.message.c_str()).ToLocalChecked());
+ Nan::Set(result, Nan::New((uint32_t)i), err);
+ }
+ info.GetReturnValue().Set(result);
+ } catch(std::exception &ex) {
+ return Nan::ThrowError(ex.what());
+ }
+}
+
+void NodeExpression::New(const Nan::FunctionCallbackInfo<v8::Value>& info) {
+ if (!info.IsConstructCall()) {
+ return Nan::ThrowTypeError("Use the new operator to create new Expression objects");
+ }
+
+ info.GetReturnValue().Set(info.This());
+}
+
+struct ToValue {
+ v8::Local<v8::Value> operator()(mbgl::NullValue) {
+ Nan::EscapableHandleScope scope;
+ return scope.Escape(Nan::Null());
+ }
+
+ v8::Local<v8::Value> operator()(bool t) {
+ Nan::EscapableHandleScope scope;
+ return scope.Escape(Nan::New(t));
+ }
+
+ v8::Local<v8::Value> operator()(double t) {
+ Nan::EscapableHandleScope scope;
+ return scope.Escape(Nan::New(t));
+ }
+
+ v8::Local<v8::Value> operator()(const std::string& t) {
+ Nan::EscapableHandleScope scope;
+ return scope.Escape(Nan::New(t).ToLocalChecked());
+ }
+
+ v8::Local<v8::Value> operator()(const std::vector<Value>& array) {
+ Nan::EscapableHandleScope scope;
+ v8::Local<v8::Array> result = Nan::New<v8::Array>();
+ for (unsigned int i = 0; i < array.size(); i++) {
+ result->Set(i, toJS(array[i]));
+ }
+ return scope.Escape(result);
+ }
+
+ v8::Local<v8::Value> operator()(const mbgl::Color& color) {
+ return operator()(std::vector<Value> {
+ static_cast<double>(color.r),
+ static_cast<double>(color.g),
+ static_cast<double>(color.b),
+ static_cast<double>(color.a)
+ });
+ }
+
+ v8::Local<v8::Value> operator()(const std::unordered_map<std::string, Value>& map) {
+ Nan::EscapableHandleScope scope;
+ v8::Local<v8::Object> result = Nan::New<v8::Object>();
+ for (const auto& entry : map) {
+ Nan::Set(result, Nan::New(entry.first).ToLocalChecked(), toJS(entry.second));
+ }
+
+ return scope.Escape(result);
+ }
+};
+
+v8::Local<v8::Value> toJS(const Value& value) {
+ return Value::visit(value, ToValue());
+}
+
+void NodeExpression::Evaluate(const Nan::FunctionCallbackInfo<v8::Value>& info) {
+ NodeExpression* nodeExpr = ObjectWrap::Unwrap<NodeExpression>(info.Holder());
+ const std::unique_ptr<Expression>& expression = nodeExpr->expression;
+
+ if (info.Length() < 2 || !info[0]->IsObject()) {
+ return Nan::ThrowTypeError("Requires globals and feature arguments.");
+ }
+
+ mbgl::optional<float> zoom;
+ v8::Local<v8::Value> v8zoom = Nan::Get(info[0]->ToObject(), Nan::New("zoom").ToLocalChecked()).ToLocalChecked();
+ if (v8zoom->IsNumber()) zoom = v8zoom->NumberValue();
+
+ mbgl::optional<double> heatmapDensity;
+ v8::Local<v8::Value> v8heatmapDensity = Nan::Get(info[0]->ToObject(), Nan::New("heatmapDensity").ToLocalChecked()).ToLocalChecked();
+ if (v8heatmapDensity->IsNumber()) heatmapDensity = v8heatmapDensity->NumberValue();
+
+ Nan::JSON NanJSON;
+ conversion::Error conversionError;
+ mbgl::optional<mbgl::GeoJSON> geoJSON = conversion::convert<mbgl::GeoJSON>(info[1], conversionError);
+ if (!geoJSON) {
+ Nan::ThrowTypeError(conversionError.message.c_str());
+ return;
+ }
+
+ try {
+ mapbox::geojson::feature feature = geoJSON->get<mapbox::geojson::feature>();
+ auto result = expression->evaluate(zoom, feature, heatmapDensity);
+ if (result) {
+ info.GetReturnValue().Set(toJS(*result));
+ } else {
+ v8::Local<v8::Object> res = Nan::New<v8::Object>();
+ Nan::Set(res,
+ Nan::New("error").ToLocalChecked(),
+ Nan::New(result.error().message.c_str()).ToLocalChecked());
+ info.GetReturnValue().Set(res);
+ }
+ } catch(std::exception &ex) {
+ return Nan::ThrowTypeError(ex.what());
+ }
+}
+
+void NodeExpression::GetType(const Nan::FunctionCallbackInfo<v8::Value>& info) {
+ NodeExpression* nodeExpr = ObjectWrap::Unwrap<NodeExpression>(info.Holder());
+ const std::unique_ptr<Expression>& expression = nodeExpr->expression;
+
+ const type::Type type = expression->getType();
+ const std::string name = type.match([&] (const auto& t) { return t.getName(); });
+ info.GetReturnValue().Set(Nan::New(name.c_str()).ToLocalChecked());
+}
+
+void NodeExpression::IsFeatureConstant(const Nan::FunctionCallbackInfo<v8::Value>& info) {
+ NodeExpression* nodeExpr = ObjectWrap::Unwrap<NodeExpression>(info.Holder());
+ const std::unique_ptr<Expression>& expression = nodeExpr->expression;
+ info.GetReturnValue().Set(Nan::New(isFeatureConstant(*expression)));
+}
+
+void NodeExpression::IsZoomConstant(const Nan::FunctionCallbackInfo<v8::Value>& info) {
+ NodeExpression* nodeExpr = ObjectWrap::Unwrap<NodeExpression>(info.Holder());
+ const std::unique_ptr<Expression>& expression = nodeExpr->expression;
+ info.GetReturnValue().Set(Nan::New(isZoomConstant(*expression)));
+}
+
+void NodeExpression::Serialize(const Nan::FunctionCallbackInfo<v8::Value>& info) {
+ NodeExpression* nodeExpr = ObjectWrap::Unwrap<NodeExpression>(info.Holder());
+ const std::unique_ptr<Expression>& expression = nodeExpr->expression;
+
+ const mbgl::Value serialized = expression->serialize();
+ info.GetReturnValue().Set(toJS(serialized));
+}
+
+} // namespace node_mbgl
diff --git a/platform/node/src/node_expression.hpp b/platform/node/src/node_expression.hpp
new file mode 100644
index 0000000000..05af217bde
--- /dev/null
+++ b/platform/node/src/node_expression.hpp
@@ -0,0 +1,43 @@
+#pragma once
+
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/expression/expression.hpp>
+#include <exception>
+#include <memory>
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#pragma GCC diagnostic ignored "-Wshadow"
+#include <nan.h>
+#pragma GCC diagnostic pop
+
+using namespace mbgl::style::expression;
+
+namespace node_mbgl {
+
+v8::Local<v8::Value> toJS(const Value&);
+
+class NodeExpression : public Nan::ObjectWrap {
+public:
+ static void Init(v8::Local<v8::Object>);
+
+private:
+ NodeExpression(std::unique_ptr<Expression> expression_) :
+ expression(std::move(expression_))
+ {};
+
+ static void New(const Nan::FunctionCallbackInfo<v8::Value>&);
+ static void Parse(const Nan::FunctionCallbackInfo<v8::Value>&);
+ static void Evaluate(const Nan::FunctionCallbackInfo<v8::Value>&);
+ static void GetType(const Nan::FunctionCallbackInfo<v8::Value>&);
+ static void IsFeatureConstant(const Nan::FunctionCallbackInfo<v8::Value>&);
+ static void IsZoomConstant(const Nan::FunctionCallbackInfo<v8::Value>&);
+
+ static void Serialize(const Nan::FunctionCallbackInfo<v8::Value>&);
+
+ static Nan::Persistent<v8::Function> constructor;
+
+ std::unique_ptr<Expression> expression;
+};
+
+} // namespace node_mbgl
diff --git a/platform/node/src/node_geojson.hpp b/platform/node/src/node_geojson.hpp
deleted file mode 100644
index 8a3927e2cf..0000000000
--- a/platform/node/src/node_geojson.hpp
+++ /dev/null
@@ -1,17 +0,0 @@
-#include <mbgl/style/conversion/geojson.hpp>
-
-#include <string>
-namespace mbgl {
-namespace style {
-namespace conversion {
-
-template <>
-optional<GeoJSON> Converter<GeoJSON>::operator()(const v8::Local<v8::Value>& value, Error& error) const {
- Nan::JSON JSON;
- std::string string = *Nan::Utf8String(JSON.Stringify(value->ToObject()).ToLocalChecked());
- return convert<GeoJSON>(string, error);
-}
-
-} // namespace conversion
-} // namespace style
-} // namespace mbgl
diff --git a/platform/node/src/node_map.cpp b/platform/node/src/node_map.cpp
index 9a7ebce445..ac14df0228 100644
--- a/platform/node/src/node_map.cpp
+++ b/platform/node/src/node_map.cpp
@@ -2,7 +2,6 @@
#include "node_request.hpp"
#include "node_feature.hpp"
#include "node_conversion.hpp"
-#include "node_geojson.hpp"
#include <mbgl/util/exception.hpp>
#include <mbgl/renderer/renderer.hpp>
@@ -10,8 +9,21 @@
#include <mbgl/style/conversion/source.hpp>
#include <mbgl/style/conversion/layer.hpp>
#include <mbgl/style/conversion/filter.hpp>
+#include <mbgl/style/conversion/light.hpp>
+
+#include <mbgl/style/layers/background_layer.hpp>
+#include <mbgl/style/layers/circle_layer.hpp>
+#include <mbgl/style/layers/fill_layer.hpp>
+#include <mbgl/style/layers/fill_extrusion_layer.hpp>
+#include <mbgl/style/layers/heatmap_layer.hpp>
+#include <mbgl/style/layers/hillshade_layer.hpp>
+#include <mbgl/style/layers/line_layer.hpp>
+#include <mbgl/style/layers/raster_layer.hpp>
+#include <mbgl/style/layers/symbol_layer.hpp>
+
#include <mbgl/style/style.hpp>
#include <mbgl/style/image.hpp>
+#include <mbgl/style/light.hpp>
#include <mbgl/map/map_observer.hpp>
#include <mbgl/util/premultiply.hpp>
@@ -22,10 +34,14 @@ namespace node_mbgl {
struct NodeMap::RenderOptions {
double zoom = 0;
double bearing = 0;
+ mbgl::style::Light light;
double pitch = 0;
double latitude = 0;
double longitude = 0;
mbgl::Size size = { 512, 512 };
+ bool axonometric = false;
+ double xSkew = 0;
+ double ySkew = 1;
std::vector<std::string> classes;
mbgl::MapDebugOptions debugOptions = mbgl::MapDebugOptions::NoDebug;
};
@@ -65,6 +81,10 @@ void NodeMap::Init(v8::Local<v8::Object> target) {
Nan::SetPrototypeMethod(tpl, "setZoom", SetZoom);
Nan::SetPrototypeMethod(tpl, "setBearing", SetBearing);
Nan::SetPrototypeMethod(tpl, "setPitch", SetPitch);
+ Nan::SetPrototypeMethod(tpl, "setLight", SetLight);
+ Nan::SetPrototypeMethod(tpl, "setAxonometric", SetAxonometric);
+ Nan::SetPrototypeMethod(tpl, "setXSkew", SetXSkew);
+ Nan::SetPrototypeMethod(tpl, "setYSkew", SetYSkew);
Nan::SetPrototypeMethod(tpl, "dumpDebugLogs", DumpDebugLogs);
Nan::SetPrototypeMethod(tpl, "queryRenderedFeatures", QueryRenderedFeatures);
@@ -251,6 +271,29 @@ NodeMap::RenderOptions NodeMap::ParseOptions(v8::Local<v8::Object> obj) {
options.pitch = Nan::Get(obj, Nan::New("pitch").ToLocalChecked()).ToLocalChecked()->NumberValue();
}
+ if (Nan::Has(obj, Nan::New("light").ToLocalChecked()).FromJust()) {
+ auto lightObj = Nan::Get(obj, Nan::New("light").ToLocalChecked()).ToLocalChecked();
+ mbgl::style::conversion::Error conversionError;
+ if (auto light = mbgl::style::conversion::convert<mbgl::style::Light>(lightObj, conversionError)) {
+ options.light = *light;
+ } else {
+ throw conversionError;
+ }
+ }
+
+ if (Nan::Has(obj, Nan::New("axonometric").ToLocalChecked()).FromJust()) {
+ options.axonometric = Nan::Get(obj, Nan::New("axonometric").ToLocalChecked()).ToLocalChecked()->BooleanValue();
+ }
+
+ if (Nan::Has(obj, Nan::New("skew").ToLocalChecked()).FromJust()) {
+ auto skewObj = Nan::Get(obj, Nan::New("skew").ToLocalChecked()).ToLocalChecked();
+ if (skewObj->IsArray()) {
+ auto skew = skewObj.As<v8::Array>();
+ if (skew->Length() > 0) { options.xSkew = Nan::Get(skew, 0).ToLocalChecked()->NumberValue(); }
+ if (skew->Length() > 1) { options.ySkew = Nan::Get(skew, 1).ToLocalChecked()->NumberValue(); }
+ }
+ }
+
if (Nan::Has(obj, Nan::New("center").ToLocalChecked()).FromJust()) {
auto centerObj = Nan::Get(obj, Nan::New("center").ToLocalChecked()).ToLocalChecked();
if (centerObj->IsArray()) {
@@ -345,14 +388,14 @@ void NodeMap::Render(const Nan::FunctionCallbackInfo<v8::Value>& info) {
return Nan::ThrowError("Map is currently rendering an image");
}
- auto options = ParseOptions(Nan::To<v8::Object>(info[0]).ToLocalChecked());
-
- assert(!nodeMap->callback);
- assert(!nodeMap->image.data);
- nodeMap->callback = std::make_unique<Nan::Callback>(info[1].As<v8::Function>());
-
try {
+ auto options = ParseOptions(Nan::To<v8::Object>(info[0]).ToLocalChecked());
+ assert(!nodeMap->callback);
+ assert(!nodeMap->image.data);
+ nodeMap->callback = std::make_unique<Nan::Callback>(info[1].As<v8::Function>());
nodeMap->startRender(std::move(options));
+ } catch (mbgl::style::conversion::Error& err) {
+ return Nan::ThrowTypeError(err.message.c_str());
} catch (mbgl::util::Exception &ex) {
return Nan::ThrowError(ex.what());
}
@@ -370,6 +413,18 @@ void NodeMap::startRender(NodeMap::RenderOptions options) {
camera.angle = -options.bearing * mbgl::util::DEG2RAD;
camera.pitch = options.pitch * mbgl::util::DEG2RAD;
+ if (map->getAxonometric() != options.axonometric) {
+ map->setAxonometric(options.axonometric);
+ }
+
+ if (map->getXSkew() != options.xSkew) {
+ map->setXSkew(options.xSkew);
+ }
+
+ if (map->getYSkew() != options.ySkew) {
+ map->setYSkew(options.ySkew);
+ }
+
map->renderStill(camera, options.debugOptions, [this](const std::exception_ptr eptr) {
if (eptr) {
error = std::move(eptr);
@@ -477,6 +532,7 @@ void NodeMap::release() {
});
map.reset();
+ frontend.reset();
}
/**
@@ -508,7 +564,7 @@ void NodeMap::cancel() {
frontend = std::make_unique<mbgl::HeadlessFrontend>(mbgl::Size{ 256, 256 }, pixelRatio, *this, threadpool);
map = std::make_unique<mbgl::Map>(*frontend, mapObserver, frontend->getSize(), pixelRatio,
- *this, threadpool, mbgl::MapMode::Still);
+ *this, threadpool, mode);
// FIXME: Reload the style after recreating the map. We need to find
// a better way of canceling an ongoing rendering on the core level
@@ -709,7 +765,7 @@ void NodeMap::SetLayoutProperty(const Nan::FunctionCallbackInfo<v8::Value>& info
return Nan::ThrowTypeError("Second argument must be a string");
}
- mbgl::optional<Error> error = setLayoutProperty(*layer, *Nan::Utf8String(info[1]), info[2]);
+ mbgl::optional<Error> error = setLayoutProperty(*layer, *Nan::Utf8String(info[1]), Convertible(info[2]));
if (error) {
return Nan::ThrowTypeError(error->message.c_str());
}
@@ -741,7 +797,7 @@ void NodeMap::SetPaintProperty(const Nan::FunctionCallbackInfo<v8::Value>& info)
return Nan::ThrowTypeError("Second argument must be a string");
}
- mbgl::optional<Error> error = setPaintProperty(*layer, *Nan::Utf8String(info[1]), info[2]);
+ mbgl::optional<Error> error = setPaintProperty(*layer, *Nan::Utf8String(info[1]), Convertible(info[2]));
if (error) {
return Nan::ThrowTypeError(error->message.c_str());
}
@@ -760,6 +816,10 @@ struct SetFilterVisitor {
Nan::ThrowTypeError("layer doesn't support filters");
}
+ void operator()(mbgl::style::HillshadeLayer&) {
+ Nan::ThrowTypeError("layer doesn't support filters");
+ }
+
void operator()(mbgl::style::BackgroundLayer&) {
Nan::ThrowTypeError("layer doesn't support filters");
}
@@ -879,6 +939,82 @@ void NodeMap::SetPitch(const Nan::FunctionCallbackInfo<v8::Value>& info) {
info.GetReturnValue().SetUndefined();
}
+void NodeMap::SetLight(const Nan::FunctionCallbackInfo<v8::Value>& info) {
+ using namespace mbgl::style;
+ using namespace mbgl::style::conversion;
+
+ auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder());
+ if (!nodeMap->map) return Nan::ThrowError(releasedMessage());
+
+ if (info.Length() <= 0 || !info[0]->IsObject()) {
+ return Nan::ThrowTypeError("First argument must be an object");
+ }
+
+ try {
+ Error conversionError;
+ if (auto light = convert<mbgl::style::Light>(info[0], conversionError)) {
+ nodeMap->map->getStyle().setLight(std::make_unique<Light>(*light));
+ } else {
+ return Nan::ThrowTypeError(conversionError.message.c_str());
+ }
+ } catch (const std::exception &ex) {
+ return Nan::ThrowError(ex.what());
+ }
+
+ info.GetReturnValue().SetUndefined();
+}
+
+void NodeMap::SetAxonometric(const Nan::FunctionCallbackInfo<v8::Value>& info) {
+ auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder());
+ if (!nodeMap->map) return Nan::ThrowError(releasedMessage());
+
+ if (info.Length() <= 0 || !info[0]->IsBoolean()) {
+ return Nan::ThrowTypeError("First argument must be a boolean");
+ }
+
+ try {
+ nodeMap->map->setAxonometric(info[0]->BooleanValue());
+ } catch (const std::exception &ex) {
+ return Nan::ThrowError(ex.what());
+ }
+
+ info.GetReturnValue().SetUndefined();
+}
+
+void NodeMap::SetXSkew(const Nan::FunctionCallbackInfo<v8::Value>& info) {
+ auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder());
+ if (!nodeMap->map) return Nan::ThrowError(releasedMessage());
+
+ if (info.Length() <= 0 || !info[0]->IsNumber()) {
+ return Nan::ThrowTypeError("First argument must be a number");
+ }
+
+ try {
+ nodeMap->map->setXSkew(info[0]->NumberValue());
+ } catch (const std::exception &ex) {
+ return Nan::ThrowError(ex.what());
+ }
+
+ info.GetReturnValue().SetUndefined();
+}
+
+void NodeMap::SetYSkew(const Nan::FunctionCallbackInfo<v8::Value>& info) {
+ auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder());
+ if (!nodeMap->map) return Nan::ThrowError(releasedMessage());
+
+ if (info.Length() <= 0 || !info[0]->IsNumber()) {
+ return Nan::ThrowTypeError("First argument must be a number");
+ }
+
+ try {
+ nodeMap->map->setYSkew(info[0]->NumberValue());
+ } catch (const std::exception &ex) {
+ return Nan::ThrowError(ex.what());
+ }
+
+ info.GetReturnValue().SetUndefined();
+}
+
void NodeMap::DumpDebugLogs(const Nan::FunctionCallbackInfo<v8::Value>& info) {
auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder());
if (!nodeMap->map) return Nan::ThrowError(releasedMessage());
@@ -983,6 +1119,15 @@ NodeMap::NodeMap(v8::Local<v8::Object> options)
->NumberValue()
: 1.0;
}())
+ , mode([&] {
+ Nan::HandleScope scope;
+ if (Nan::Has(options, Nan::New("mode").ToLocalChecked()).FromJust() &&
+ std::string(*v8::String::Utf8Value(Nan::Get(options, Nan::New("mode").ToLocalChecked()).ToLocalChecked()->ToString())) == "tile") {
+ return mbgl::MapMode::Tile;
+ } else {
+ return mbgl::MapMode::Static;
+ }
+ }())
, mapObserver(NodeMapObserver())
, frontend(std::make_unique<mbgl::HeadlessFrontend>(mbgl::Size { 256, 256 }, pixelRatio, *this, threadpool))
, map(std::make_unique<mbgl::Map>(*frontend,
@@ -991,7 +1136,7 @@ NodeMap::NodeMap(v8::Local<v8::Object> options)
pixelRatio,
*this,
threadpool,
- mbgl::MapMode::Still)),
+ mode)),
async(new uv_async_t) {
async->data = this;
diff --git a/platform/node/src/node_map.hpp b/platform/node/src/node_map.hpp
index c8e33bb347..efa6f6cee0 100644
--- a/platform/node/src/node_map.hpp
+++ b/platform/node/src/node_map.hpp
@@ -57,6 +57,10 @@ public:
static void SetZoom(const Nan::FunctionCallbackInfo<v8::Value>&);
static void SetBearing(const Nan::FunctionCallbackInfo<v8::Value>&);
static void SetPitch(const Nan::FunctionCallbackInfo<v8::Value>&);
+ static void SetLight(const Nan::FunctionCallbackInfo<v8::Value>&);
+ static void SetAxonometric(const Nan::FunctionCallbackInfo<v8::Value>&);
+ static void SetXSkew(const Nan::FunctionCallbackInfo<v8::Value>&);
+ static void SetYSkew(const Nan::FunctionCallbackInfo<v8::Value>&);
static void DumpDebugLogs(const Nan::FunctionCallbackInfo<v8::Value>&);
static void QueryRenderedFeatures(const Nan::FunctionCallbackInfo<v8::Value>&);
@@ -71,6 +75,7 @@ public:
std::unique_ptr<mbgl::AsyncRequest> request(const mbgl::Resource&, mbgl::FileSource::Callback);
const float pixelRatio;
+ mbgl::MapMode mode;
NodeThreadPool threadpool;
NodeMapObserver mapObserver;
std::unique_ptr<mbgl::HeadlessFrontend> frontend;
diff --git a/platform/node/src/node_mapbox_gl_native.cpp b/platform/node/src/node_mapbox_gl_native.cpp
index cdcc982220..96e96e4298 100644
--- a/platform/node/src/node_mapbox_gl_native.cpp
+++ b/platform/node/src/node_mapbox_gl_native.cpp
@@ -10,6 +10,7 @@
#include "node_map.hpp"
#include "node_logging.hpp"
#include "node_request.hpp"
+#include "node_expression.hpp"
void RegisterModule(v8::Local<v8::Object> target, v8::Local<v8::Object> module) {
// This has the effect of:
@@ -20,6 +21,7 @@ void RegisterModule(v8::Local<v8::Object> target, v8::Local<v8::Object> module)
node_mbgl::NodeMap::Init(target);
node_mbgl::NodeRequest::Init();
+ node_mbgl::NodeExpression::Init(target);
// Exports Resource constants.
v8::Local<v8::Object> resource = Nan::New<v8::Object>();
diff --git a/platform/node/test/expression.test.js b/platform/node/test/expression.test.js
new file mode 100644
index 0000000000..ffd1c68ff2
--- /dev/null
+++ b/platform/node/test/expression.test.js
@@ -0,0 +1,85 @@
+'use strict';
+
+var suite = require('../../../mapbox-gl-js/test/integration').expression;
+var mbgl = require('../index');
+var ignores = require('./ignores.json');
+
+var tests;
+
+if (process.argv[1] === __filename && process.argv.length > 2) {
+ tests = process.argv.slice(2);
+}
+
+function getExpectedType(spec) {
+ if (spec.type === 'array') {
+ const itemType = getExpectedType({ type: spec.value });
+ const array = {
+ kind: 'array',
+ itemType: itemType || { kind: 'value' },
+ };
+ if (typeof spec.length === 'number') {
+ array.N = spec.length;
+ }
+ return array;
+ }
+
+ if (spec.type === 'enum') {
+ return { kind: 'string' };
+ }
+
+ return typeof spec.type === 'string' ? {kind: spec.type} : null;
+}
+
+suite.run('native', {ignores: ignores, tests: tests}, (fixture) => {
+ const compiled = {};
+ const recompiled = {};
+ const result = {
+ compiled,
+ recompiled
+ };
+
+ const spec = fixture.propertySpec || {};
+ const expression = mbgl.Expression.parse(fixture.expression, getExpectedType(spec));
+
+ const evaluateExpression = (expression, compilationResult) => {
+ if (expression instanceof mbgl.Expression) {
+ compilationResult.result = 'success';
+ compilationResult.isFeatureConstant = expression.isFeatureConstant();
+ compilationResult.isZoomConstant = expression.isZoomConstant();
+ compilationResult.type = expression.getType();
+
+ const evaluate = fixture.inputs || [];
+ const evaluateResults = [];
+ for (const input of evaluate) {
+ const feature = Object.assign({
+ type: 'Feature',
+ properties: {},
+ geometry: { type: 'Point', coordinates: [0, 0] }
+ }, input[1])
+
+ const output = expression.evaluate(input[0], feature);
+ evaluateResults.push(output);
+ }
+
+ if (fixture.inputs) {
+ return evaluateResults;
+ }
+ } else {
+ compilationResult.result = 'error';
+ compilationResult.errors = expression;
+ }
+ }
+
+ result.outputs = evaluateExpression(expression, compiled);
+ if (expression instanceof mbgl.Expression) {
+ result.serialized = expression.serialize();
+ const recompiledExpression = mbgl.Expression.parse(result.serialized, getExpectedType(spec));
+ result.roundTripOutputs = evaluateExpression(recompiledExpression, recompiled);
+ // Type is allowed to change through serialization
+ // (eg "array" -> "array<number, 3>")
+ // Override the round-tripped type here so that the equality check passes
+ recompiled.type = compiled.type;
+ }
+
+ return result;
+});
diff --git a/platform/node/test/ignores.json b/platform/node/test/ignores.json
index 1084a5c923..50e2aa1d61 100644
--- a/platform/node/test/ignores.json
+++ b/platform/node/test/ignores.json
@@ -2,58 +2,90 @@
"query-tests/geometry/multilinestring": "needs investigation",
"query-tests/geometry/multipolygon": "needs investigation",
"query-tests/geometry/polygon": "needs investigation",
- "query-tests/regressions/mapbox-gl-js#3534": "https://github.com/mapbox/mapbox-gl-native/issues/8193",
- "query-tests/regressions/mapbox-gl-js#4417": "https://github.com/mapbox/mapbox-gl-native/issues/8007",
- "query-tests/symbol-features-in/pitched-screen": "https://github.com/mapbox/mapbox-gl-native/issues/6817",
- "query-tests/symbol-features-in/tilted-inside": "https://github.com/mapbox/mapbox-gl-native/issues/5056",
- "query-tests/symbol-features-in/tilted-outside": "https://github.com/mapbox/mapbox-gl-native/issues/9435",
+ "query-tests/symbol/panned-after-insert": "https://github.com/mapbox/mapbox-gl-native/issues/10408",
+ "query-tests/symbol/rotated-after-insert": "https://github.com/mapbox/mapbox-gl-native/issues/10408",
"query-tests/world-wrapping/box": "skip - needs issue",
"query-tests/world-wrapping/point": "skip - needs issue",
- "render-tests/debug/collision-overscaled": "https://github.com/mapbox/mapbox-gl-native/issues/3841",
- "render-tests/debug/collision-pitched-wrapped": "https://github.com/mapbox/mapbox-gl-native/issues/3841",
- "render-tests/debug/collision-pitched": "https://github.com/mapbox/mapbox-gl-native/issues/3841",
+ "render-tests/background-color/transition": "https://github.com/mapbox/mapbox-gl-native/issues/10619",
"render-tests/debug/collision": "https://github.com/mapbox/mapbox-gl-native/issues/3841",
- "render-tests/debug/tile-overscaled": "https://github.com/mapbox/mapbox-gl-native/issues/3841",
+ "render-tests/debug/collision-lines": "https://github.com/mapbox/mapbox-gl-native/issues/10412",
+ "render-tests/debug/collision-lines-overscaled": "https://github.com/mapbox/mapbox-gl-native/issues/10412",
+ "render-tests/debug/collision-lines-pitched": "https://github.com/mapbox/mapbox-gl-native/issues/10412",
+ "render-tests/debug/collision-pitched-wrapped": "https://github.com/mapbox/mapbox-gl-native/issues/10412",
"render-tests/debug/tile": "https://github.com/mapbox/mapbox-gl-native/issues/3841",
+ "render-tests/debug/tile-overscaled": "https://github.com/mapbox/mapbox-gl-native/issues/3841",
"render-tests/extent/1024-circle": "needs investigation",
- "render-tests/extent/1024-symbol": "needs investigation",
"render-tests/fill-extrusion-pattern/@2x": "https://github.com/mapbox/mapbox-gl-js/issues/3327",
- "render-tests/fill-extrusion-pattern/function-2": "https://github.com/mapbox/mapbox-gl-js/issues/3327",
"render-tests/fill-extrusion-pattern/function": "https://github.com/mapbox/mapbox-gl-js/issues/3327",
+ "render-tests/fill-extrusion-pattern/function-2": "https://github.com/mapbox/mapbox-gl-js/issues/3327",
"render-tests/fill-extrusion-pattern/literal": "https://github.com/mapbox/mapbox-gl-js/issues/3327",
"render-tests/fill-extrusion-pattern/missing": "https://github.com/mapbox/mapbox-gl-js/issues/3327",
"render-tests/fill-extrusion-pattern/opacity": "https://github.com/mapbox/mapbox-gl-js/issues/3327",
"render-tests/geojson/inline-linestring-fill": "current behavior is arbitrary",
"render-tests/geojson/inline-polygon-symbol": "behavior needs reconciliation with gl-js",
- "render-tests/icon-size/composite-function-high-base-plain": "https://github.com/mapbox/mapbox-gl-native/issues/8654",
- "render-tests/icon-size/composite-function-high-base-sdf": "https://github.com/mapbox/mapbox-gl-native/issues/8654",
- "render-tests/icon-text-fit/both-padding": "https://github.com/mapbox/mapbox-gl-native/issues/5602",
- "render-tests/icon-text-fit/both": "https://github.com/mapbox/mapbox-gl-native/issues/5602",
- "render-tests/icon-text-fit/height": "https://github.com/mapbox/mapbox-gl-native/issues/5602",
- "render-tests/icon-text-fit/width-padding": "https://github.com/mapbox/mapbox-gl-native/issues/5602",
- "render-tests/icon-text-fit/width": "https://github.com/mapbox/mapbox-gl-native/issues/5602",
- "render-tests/line-width/property-function": "https://github.com/mapbox/mapbox-gl-js/issues/3682#issuecomment-264348200",
- "render-tests/line-join/property-function": "https://github.com/mapbox/mapbox-gl-js/pull/5020",
- "render-tests/line-join/property-function-dasharray": "https://github.com/mapbox/mapbox-gl-js/pull/5020",
- "render-tests/line-opacity/step-curve": "https://github.com/mapbox/mapbox-gl-native/pull/9439",
+ "render-tests/mixed-zoom/z10-z11": "https://github.com/mapbox/mapbox-gl-native/issues/10397",
+ "render-tests/raster-masking/overlapping-zoom": "https://github.com/mapbox/mapbox-gl-native/issues/10195",
"render-tests/regressions/mapbox-gl-js#2305": "https://github.com/mapbox/mapbox-gl-native/issues/6927",
+ "render-tests/regressions/mapbox-gl-js#2467": "https://github.com/mapbox/mapbox-gl-native/issues/10619",
+ "render-tests/regressions/mapbox-gl-js#2762": "https://github.com/mapbox/mapbox-gl-native/issues/10619",
+ "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",
"render-tests/runtime-styling/set-style-paint-property-fill-flat-to-extrude": "https://github.com/mapbox/mapbox-gl-native/issues/6745",
- "render-tests/symbol-placement/line": "needs issue",
- "render-tests/text-font/camera-function": "https://github.com/mapbox/mapbox-gl-native/pull/9439",
- "render-tests/text-keep-upright/line-placement-true-offset": "https://github.com/mapbox/mapbox-gl-native/issues/9271",
+ "render-tests/symbol-placement/line-overscaled": "https://github.com/mapbox/mapbox-gl-js/issues/5654",
+ "render-tests/symbol-visibility/visible": "https://github.com/mapbox/mapbox-gl-native/issues/10409",
"render-tests/text-pitch-alignment/auto-text-rotation-alignment-map": "https://github.com/mapbox/mapbox-gl-native/issues/9732",
- "render-tests/text-pitch-alignment/auto-text-rotation-alignment-viewport": "https://github.com/mapbox/mapbox-gl-native/issues/9732",
"render-tests/text-pitch-alignment/map-text-rotation-alignment-map": "https://github.com/mapbox/mapbox-gl-native/issues/9732",
- "render-tests/text-pitch-alignment/map-text-rotation-alignment-viewport": "https://github.com/mapbox/mapbox-gl-native/issues/9732",
- "render-tests/text-pitch-alignment/viewport-overzoomed": "https://github.com/mapbox/mapbox-gl-js/issues/5095",
- "render-tests/text-pitch-alignment/viewport-overzoomed-single-glyph": "https://github.com/mapbox/mapbox-gl-js/issues/5095",
"render-tests/text-pitch-alignment/viewport-text-rotation-alignment-map": "https://github.com/mapbox/mapbox-gl-native/issues/9732",
- "render-tests/text-pitch-alignment/viewport-text-rotation-alignment-viewport": "https://github.com/mapbox/mapbox-gl-native/issues/9732",
"render-tests/text-pitch-scaling/line-half": "https://github.com/mapbox/mapbox-gl-native/issues/9732",
- "render-tests/text-size/composite-expression": "https://github.com/mapbox/mapbox-gl-native/pull/9439",
- "render-tests/video/default": "skip - https://github.com/mapbox/mapbox-gl-native/issues/601"
+ "render-tests/video/default": "skip - https://github.com/mapbox/mapbox-gl-native/issues/601",
+ "render-tests/background-color/colorSpace-hcl": "needs issue",
+ "render-tests/combinations/background-opaque--heatmap-translucent": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/combinations/background-translucent--heatmap-translucent": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/combinations/circle-translucent--heatmap-translucent": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/combinations/fill-extrusion-translucent--heatmap-translucent": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/combinations/fill-opaque--heatmap-translucent": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/combinations/fill-translucent--heatmap-translucent": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/combinations/hillshade-translucent--heatmap-translucent": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/combinations/heatmap-translucent--background-opaque": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/combinations/heatmap-translucent--background-translucent": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/combinations/heatmap-translucent--circle-translucent": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/combinations/heatmap-translucent--fill-extrusion-translucent": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/combinations/heatmap-translucent--fill-opaque": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/combinations/heatmap-translucent--fill-translucent": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/combinations/heatmap-translucent--heatmap-translucent": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/combinations/heatmap-translucent--hillshade-translucent": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/combinations/heatmap-translucent--line-translucent": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/combinations/heatmap-translucent--raster-translucent": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/combinations/heatmap-translucent--symbol-translucent": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/combinations/line-translucent--heatmap-translucent": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/combinations/raster-translucent--heatmap-translucent": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/combinations/symbol-translucent--heatmap-translucent": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/combinations/background-opaque--hillshade-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642",
+ "render-tests/combinations/background-translucent--hillshade-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642",
+ "render-tests/combinations/circle-translucent--hillshade-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642",
+ "render-tests/combinations/fill-extrusion-translucent--hillshade-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642",
+ "render-tests/combinations/background-opaque--fill-extrusion-translucent": "needs investigation",
+ "render-tests/combinations/background-translucent--fill-extrusion-translucent": "needs investigation",
+ "render-tests/combinations/circle-translucent--fill-extrusion-translucent": "needs investigation",
+ "render-tests/combinations/fill-extrusion-translucent--background-translucent": "needs investigation",
+ "render-tests/combinations/fill-extrusion-translucent--circle-translucent": "needs investigation",
+ "render-tests/combinations/fill-extrusion-translucent--hillshade-translucent": "needs investigation",
+ "render-tests/combinations/fill-extrusion-translucent--fill-extrusion-translucent": "needs investigation",
+ "render-tests/combinations/fill-extrusion-translucent--fill-translucent": "needs investigation",
+ "render-tests/combinations/fill-extrusion-translucent--line-translucent": "needs investigation",
+ "render-tests/combinations/fill-extrusion-translucent--symbol-translucent": "needs investigation",
+ "render-tests/combinations/hillshade-translucent--fill-extrusion-translucent": "needs investigation",
+ "render-tests/combinations/fill-opaque--fill-extrusion-translucent": "needs investigation",
+ "render-tests/combinations/fill-translucent--fill-extrusion-translucent": "needs investigation",
+ "render-tests/combinations/line-translucent--fill-extrusion-translucent": "needs investigation",
+ "render-tests/combinations/raster-translucent--fill-extrusion-translucent": "needs investigation",
+ "render-tests/combinations/symbol-translucent--fill-extrusion-translucent": "needs investigation"
}
diff --git a/platform/node/test/js/map.test.js b/platform/node/test/js/map.test.js
index 39665e41ef..81c15f9c1b 100644
--- a/platform/node/test/js/map.test.js
+++ b/platform/node/test/js/map.test.js
@@ -121,6 +121,10 @@ test('Map', function(t) {
'setZoom',
'setBearing',
'setPitch',
+ 'setLight',
+ 'setAxonometric',
+ 'setXSkew',
+ 'setYSkew',
'dumpDebugLogs',
'queryRenderedFeatures'
]);
diff --git a/platform/node/test/suite_implementation.js b/platform/node/test/suite_implementation.js
index 323f429bed..c09e8f50bf 100644
--- a/platform/node/test/suite_implementation.js
+++ b/platform/node/test/suite_implementation.js
@@ -14,21 +14,25 @@ mbgl.on('message', function(msg) {
var maps = new Map();
module.exports = function (style, options, callback) {
+ var tileMode = options.mapMode === 'tile';
if (options.recycleMap) {
- if (maps.has(options.pixelRatio)) {
- var map = maps.get(options.pixelRatio);
+ var key = options.pixelRatio + '/' + tileMode;
+ if (maps.has(key)) {
+ var map = maps.get(key);
map.request = mapRequest;
} else {
- maps.set(options.pixelRatio, new mbgl.Map({
+ maps.set(key, new mbgl.Map({
ratio: options.pixelRatio,
- request: mapRequest
+ request: mapRequest,
+ mode: options.mapMode
}));
- var map = maps.get(options.pixelRatio);
+ var map = maps.get(key);
}
} else {
var map = new mbgl.Map({
ratio: options.pixelRatio,
- request: mapRequest
+ request: mapRequest,
+ mode: options.mapMode
});
}
@@ -49,6 +53,7 @@ module.exports = function (style, options, callback) {
options.zoom = style.zoom || 0;
options.bearing = style.bearing || 0;
options.pitch = style.pitch || 0;
+ options.light = style.light || {};
map.load(style, { defaultStyleCamera: true });
@@ -90,6 +95,12 @@ module.exports = function (style, options, callback) {
applyOperations(operations.slice(1), callback);
});
+ } else if (operation[0] === 'sleep') {
+ // Prefer "wait", which renders until the map is loaded
+ // Use "sleep" when you need to test something that sidesteps the "loaded" logic
+ setTimeout(() => {
+ applyOperations(operations.slice(1), callback);
+ }, operation[1]);
} else if (operation[0] === 'addImage' || operation[0] === 'updateImage') {
var img = PNG.sync.read(fs.readFileSync(path.join(__dirname, '../../../mapbox-gl-js/test/integration', operation[2])));
const testOpts = (operation.length > 3) ? operation[3] : {};
@@ -119,6 +130,8 @@ module.exports = function (style, options, callback) {
options.bearing = operation[1];
} else if (operation[0] === 'setPitch') {
options.pitch = operation[1];
+ } else if (operation[0] === 'setLight') {
+ options.light = operation[1];
}
map[operation[0]].apply(map, operation.slice(1));
diff --git a/platform/qt/README.md b/platform/qt/README.md
index 0123e3bda0..4d2d887828 100644
--- a/platform/qt/README.md
+++ b/platform/qt/README.md
@@ -1,41 +1,84 @@
# Mapbox Qt SDK
-[![Travis](https://travis-ci.org/mapbox/mapbox-gl-native.svg?branch=master)](https://travis-ci.org/mapbox/mapbox-gl-native/builds)
-[![Bitrise](https://www.bitrise.io/app/96cfbc97e0245c22.svg?token=GxsqIOGPXhn0F23sSVSsYA&branch=master)](https://www.bitrise.io/app/96cfbc97e0245c22)
+[![Circle CI build status](https://circleci.com/gh/mapbox/mapbox-gl-native.svg?style=shield)](https://circleci.com/gh/mapbox/workflows/mapbox-gl-native/tree/master) [![AppVeyor CI build status](https://ci.appveyor.com/api/projects/status/3q12kbcooc6df8uc?svg=true)](https://ci.appveyor.com/project/Mapbox/mapbox-gl-native)
-### Developing
+## Developing
-This is the foundation for the Mapbox GL plugin available since Qt 5.9. Use the Qt bugtracker
-for bugs related to the plugin and this GitHub repository for bugs related to Mapbox GL Native.
+This is the foundation for the [Mapbox GL plugin](https://doc.qt.io/qt-5/location-plugin-mapboxgl.html)
+available in the Qt SDK since Qt 5.9. Use the [Qt bugtracker](https://bugreports.qt.io) for bugs related
+to the plugin and this GitHub repository for bugs related to Mapbox GL Native and the Qt bindings.
+
+You should build this repository if you want to develop/contribute using the low level Qt C++ bindings or
+want to contribute to the Mapbox GL Native projectusing the Qt port for debugging.
+
+See the Mapbox Qt landing page for more details: https://www.mapbox.com/qt/
+
+### Build dependencies
#### Linux
-For Linux (Ubuntu) desktop, together with these [build
+For Linux (tested on Ubuntu) desktop, together with these [build
instructions](https://github.com/mapbox/mapbox-gl-native/tree/master/platform/linux#build),
you also need:
- sudo apt-get install qt5-default
+```
+$ sudo apt-get install qt5-default
+```
#### macOS
-For macOS desktop, you can install Qt 5 via [Homebrew](http://brew.sh):
+For macOS desktop, you can install Qt 5 via [Homebrew](https://brew.sh):
- brew install qt5
+```
+$ brew install qt5
+```
Since Homebrew doesn't add Qt to the path, you'll have to do that manually before running any Make target:
- export PATH=/usr/local/opt/qt5/bin:$PATH
+```
+export PATH=/usr/local/opt/qt5/bin:$PATH
+```
+
+### Windows
+
+The Windows build will assume you have installed and on the default path:
+
+- Microsoft Visual Studio 2015
+- [CMake 3.10.1+](https://cmake.org/download/)
+- [LLVM 5.0.0+](https://releases.llvm.org/download.html)
+- [Qt 5+](https://www.qt.io/download) with "msvc2015" support.
-We provide the following build targets for our Qt SDK:
+At runtime, you will also need installed:
+
+- [OpenSSL 1.0.2+](https://slproweb.com/products/Win32OpenSSL.html)
+- DirectX
### Build instructions
-#### Qt library
+Public API headers
+can be found in the [platform/qt/include](https://github.com/mapbox/mapbox-gl-native/tree/master/platform/qt/include) directory.
-```make qt-lib``` to build the `qmapboxgl` shared library. Public API headers
-can be found in [platform/qt/include](https://github.com/mapbox/mapbox-gl-native/tree/master/platform/qt/include) folder.
+#### Linux and macOS
#### QMapboxGL example application
-```make qt-app``` or ```make run-qt-app``` to run the application at the end of
-build.
+```
+$ make qt-lib # Will build libqmapboxgl.so
+$ make qt-app # Will build the test app and libqmapboxgl.so if not built yet
+$ make run-qt-app # Will build and run the test app
+```
+
+#### Windows
+
+The Windows build bot will publish MSVC 2015 compatible binaries and headers on every build
+at the [artifacts](https://ci.appveyor.com/project/Mapbox/mapbox-gl-native/build/artifacts) tab.
+These binaries can be downloaded and used right away.
+
+In case of building from the sources:
+
+```
+$ mkdir build
+$ cd build
+$ cmake -G "Visual Studio 14 2015 Win64" -T LLVM-vs2014 -DMBGL_PLATFORM=qt -DWITH_QT_DECODERS=ON -DWITH_QT_I18N=ON -DWITH_NODEJS=OFF ..
+$ cmake --build . --config Release --target qmapboxgl -- /m
+```
diff --git a/platform/qt/app/mapwindow.cpp b/platform/qt/app/mapwindow.cpp
index c4efbfa318..f6d5473192 100644
--- a/platform/qt/app/mapwindow.cpp
+++ b/platform/qt/app/mapwindow.cpp
@@ -252,10 +252,18 @@ void MapWindow::keyPressEvent(QKeyEvent *ev)
break;
case Qt::Key_2: {
if (m_lineAnnotationId.isNull()) {
- QMapbox::Coordinate topLeft = m_map->coordinateForPixel({ 0, 0 });
- QMapbox::Coordinate bottomRight = m_map->coordinateForPixel({ qreal(size().width()), qreal(size().height()) });
- QMapbox::CoordinatesCollections lineGeometry { { { topLeft, bottomRight } } };
- QMapbox::ShapeAnnotationGeometry annotationGeometry { QMapbox::ShapeAnnotationGeometry::LineStringType, lineGeometry };
+ QMapbox::Coordinates coordinates;
+ coordinates.push_back(m_map->coordinateForPixel({ 0, 0 }));
+ coordinates.push_back(m_map->coordinateForPixel({ qreal(size().width()), qreal(size().height()) }));
+
+ QMapbox::CoordinatesCollection collection;
+ collection.push_back(coordinates);
+
+ QMapbox::CoordinatesCollections lineGeometry;
+ lineGeometry.push_back(collection);
+
+ QMapbox::ShapeAnnotationGeometry annotationGeometry(QMapbox::ShapeAnnotationGeometry::LineStringType, lineGeometry);
+
QMapbox::LineAnnotation line;
line.geometry = annotationGeometry;
line.opacity = 0.5f;
@@ -270,12 +278,20 @@ void MapWindow::keyPressEvent(QKeyEvent *ev)
break;
case Qt::Key_3: {
if (m_fillAnnotationId.isNull()) {
- QMapbox::Coordinate topLeft = m_map->coordinateForPixel({ 0, 0 });
- QMapbox::Coordinate topRight = m_map->coordinateForPixel({ 0, qreal(size().height()) });
- QMapbox::Coordinate bottomLeft = m_map->coordinateForPixel({ qreal(size().width()), 0 });
- QMapbox::Coordinate bottomRight = m_map->coordinateForPixel({ qreal(size().width()), qreal(size().height()) });
- QMapbox::CoordinatesCollections fillGeometry { { { bottomLeft, bottomRight, topRight, topLeft, bottomLeft } } };
- QMapbox::ShapeAnnotationGeometry annotationGeometry { QMapbox::ShapeAnnotationGeometry::PolygonType, fillGeometry };
+ QMapbox::Coordinates coordinates;
+ coordinates.push_back(m_map->coordinateForPixel({ qreal(size().width()), 0 }));
+ coordinates.push_back(m_map->coordinateForPixel({ qreal(size().width()), qreal(size().height()) }));
+ coordinates.push_back(m_map->coordinateForPixel({ 0, qreal(size().height()) }));
+ coordinates.push_back(m_map->coordinateForPixel({ 0, 0 }));
+
+ QMapbox::CoordinatesCollection collection;
+ collection.push_back(coordinates);
+
+ QMapbox::CoordinatesCollections fillGeometry;
+ fillGeometry.push_back(collection);
+
+ QMapbox::ShapeAnnotationGeometry annotationGeometry(QMapbox::ShapeAnnotationGeometry::PolygonType, fillGeometry);
+
QMapbox::FillAnnotation fill;
fill.geometry = annotationGeometry;
fill.opacity = 0.5f;
@@ -293,8 +309,16 @@ void MapWindow::keyPressEvent(QKeyEvent *ev)
m_map->removeLayer("circleLayer");
m_map->removeSource("circleSource");
} else {
- QMapbox::CoordinatesCollections point { { { m_map->coordinate() } } };
- QMapbox::Feature feature { QMapbox::Feature::PointType, point, {}, {} };
+ QMapbox::Coordinates coordinates;
+ coordinates.push_back(m_map->coordinate());
+
+ QMapbox::CoordinatesCollection collection;
+ collection.push_back(coordinates);
+
+ QMapbox::CoordinatesCollections point;
+ point.push_back(collection);
+
+ QMapbox::Feature feature(QMapbox::Feature::PointType, point, {}, {});
QVariantMap circleSource;
circleSource["type"] = "geojson";
@@ -418,10 +442,9 @@ void MapWindow::initializeGL()
void MapWindow::paintGL()
{
m_frameDraws++;
- m_map->resize(size(), size() * pixelRatio());
+ m_map->resize(size());
#if QT_VERSION >= 0x050400
- // When we're using QOpenGLWidget, we need to tell Mapbox GL about the framebuffer we're using.
- m_map->setFramebufferObject(defaultFramebufferObject());
+ m_map->setFramebufferObject(defaultFramebufferObject(), size() * pixelRatio());
#endif
m_map->render();
}
diff --git a/platform/qt/config.cmake b/platform/qt/config.cmake
index 57e586d7c3..5f04367b25 100644
--- a/platform/qt/config.cmake
+++ b/platform/qt/config.cmake
@@ -1,7 +1,7 @@
-include(platform/qt/qt.cmake)
+mason_use(optional VERSION f27e7908 HEADER_ONLY)
+mason_use(tao_tuple VERSION 28626e99 HEADER_ONLY)
-mason_use(sqlite VERSION 3.14.2)
-mason_use(gtest VERSION 1.8.0${MASON_CXXABI_SUFFIX})
+include(platform/qt/qt.cmake)
if(NOT WITH_QT_DECODERS)
mason_use(libjpeg-turbo VERSION 1.5.0)
@@ -20,6 +20,7 @@ macro(mbgl_platform_core)
target_include_directories(mbgl-core
PUBLIC platform/default
+ PRIVATE platform/qt
PRIVATE platform/qt/include
)
@@ -50,6 +51,10 @@ macro(mbgl_platform_core)
target_sources(mbgl-core PRIVATE platform/default/local_glyph_rasterizer.cpp)
+ if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
+ target_add_mason_package(mbgl-core PRIVATE optional)
+ target_add_mason_package(mbgl-core PRIVATE tao_tuple)
+ endif()
endmacro()
@@ -61,28 +66,40 @@ macro(mbgl_filesource)
target_link_libraries(mbgl-filesource
${MBGL_QT_FILESOURCE_LIBRARIES}
)
+
+ if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
+ target_add_mason_package(mbgl-filesource PRIVATE optional)
+ target_add_mason_package(mbgl-filesource PRIVATE tao_tuple)
+ endif()
endmacro()
+# FIXME: For now tests are disabled on Windows until we
+# get the node.js dependencies working.
+if (NOT CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
+ mason_use(gtest VERSION 1.8.0${MASON_CXXABI_SUFFIX})
-macro(mbgl_platform_test)
- target_sources(mbgl-test
- PRIVATE platform/qt/test/main.cpp
- PRIVATE platform/qt/test/qmapboxgl.test.cpp
- PRIVATE platform/qt/test/qmapboxgl.test.cpp
- )
+ macro(mbgl_platform_test)
+ target_sources(mbgl-test
+ PRIVATE platform/qt/test/main.cpp
+ PRIVATE platform/qt/test/qmapboxgl.test.cpp
+ PRIVATE platform/qt/test/qmapboxgl.test.cpp
+ )
- set_source_files_properties(
- platform/qt/test/main.cpp
- PROPERTIES COMPILE_FLAGS -DWORK_DIRECTORY="${CMAKE_SOURCE_DIR}"
- )
+ target_include_directories(mbgl-test
+ PRIVATE platform/qt
+ )
- target_add_mason_package(mbgl-test PRIVATE sqlite)
+ set_source_files_properties(
+ platform/qt/test/main.cpp
+ PROPERTIES COMPILE_FLAGS -DWORK_DIRECTORY="${CMAKE_SOURCE_DIR}"
+ )
- target_link_libraries(mbgl-test
- PRIVATE qmapboxgl
- PRIVATE mbgl-filesource
- )
-endmacro()
+ target_link_libraries(mbgl-test
+ PRIVATE qmapboxgl
+ PRIVATE mbgl-filesource
+ )
+ endmacro()
+endif()
target_add_mason_package(qmapboxgl PRIVATE geojson)
target_add_mason_package(qmapboxgl PRIVATE rapidjson)
diff --git a/platform/qt/config.qdocconf b/platform/qt/config.qdocconf
index b8ade81bb2..d6f0edbd32 100644
--- a/platform/qt/config.qdocconf
+++ b/platform/qt/config.qdocconf
@@ -4,7 +4,7 @@ include($QT_INSTALL_DOCS/global/compat.qdocconf)
include($QT_INSTALL_DOCS/global/fileextensions.qdocconf)
include($QT_INSTALL_DOCS/global/qt-html-templates-online.qdocconf)
-Cpp.ignoretokens = Q_DECL_EXPORT
+Cpp.ignoretokens = Q_MAPBOXGL_EXPORT
project = QMapboxGL
description = Mapbox Qt SDK
diff --git a/platform/qt/include/QQuickMapboxGL b/platform/qt/include/QQuickMapboxGL
deleted file mode 100644
index db109a1d3a..0000000000
--- a/platform/qt/include/QQuickMapboxGL
+++ /dev/null
@@ -1 +0,0 @@
-#include "qquickmapboxgl.hpp"
diff --git a/platform/qt/include/QQuickMapboxGLMapParameter b/platform/qt/include/QQuickMapboxGLMapParameter
deleted file mode 100644
index 603fb2bd51..0000000000
--- a/platform/qt/include/QQuickMapboxGLMapParameter
+++ /dev/null
@@ -1 +0,0 @@
-#include "qquickmapboxglmapparameter.hpp"
diff --git a/platform/qt/include/qmapbox.hpp b/platform/qt/include/qmapbox.hpp
index d138f4057b..5a5198108c 100644
--- a/platform/qt/include/qmapbox.hpp
+++ b/platform/qt/include/qmapbox.hpp
@@ -9,6 +9,12 @@
// This header follows the Qt coding style: https://wiki.qt.io/Qt_Coding_Style
+#if defined(QT_BUILD_MAPBOXGL_LIB)
+ #define Q_MAPBOXGL_EXPORT Q_DECL_EXPORT
+#else
+ #define Q_MAPBOXGL_EXPORT Q_DECL_IMPORT
+#endif
+
namespace QMapbox {
typedef QPair<double, double> Coordinate;
@@ -20,13 +26,14 @@ typedef QList<Coordinates> CoordinatesCollection;
typedef QList<CoordinatesCollection> CoordinatesCollections;
-struct Q_DECL_EXPORT Feature {
+struct Q_MAPBOXGL_EXPORT Feature {
enum Type {
PointType = 1,
LineStringType,
PolygonType
};
+ /*! Class constructor. */
Feature(Type type_ = PointType, const CoordinatesCollections& geometry_ = CoordinatesCollections(),
const QVariantMap& properties_ = QVariantMap(), const QVariant& id_ = QVariant())
: type(type_), geometry(geometry_), properties(properties_), id(id_) {}
@@ -37,7 +44,7 @@ struct Q_DECL_EXPORT Feature {
QVariant id;
};
-struct Q_DECL_EXPORT ShapeAnnotationGeometry {
+struct Q_MAPBOXGL_EXPORT ShapeAnnotationGeometry {
enum Type {
LineStringType = 1,
PolygonType,
@@ -45,6 +52,7 @@ struct Q_DECL_EXPORT ShapeAnnotationGeometry {
MultiPolygonType
};
+ /*! Class constructor. */
ShapeAnnotationGeometry(Type type_ = LineStringType, const CoordinatesCollections& geometry_ = CoordinatesCollections())
: type(type_), geometry(geometry_) {}
@@ -52,12 +60,13 @@ struct Q_DECL_EXPORT ShapeAnnotationGeometry {
CoordinatesCollections geometry;
};
-struct Q_DECL_EXPORT SymbolAnnotation {
+struct Q_MAPBOXGL_EXPORT SymbolAnnotation {
Coordinate geometry;
QString icon;
};
-struct Q_DECL_EXPORT LineAnnotation {
+struct Q_MAPBOXGL_EXPORT LineAnnotation {
+ /*! Class constructor. */
LineAnnotation(const ShapeAnnotationGeometry& geometry_ = ShapeAnnotationGeometry(), float opacity_ = 1.0f,
float width_ = 1.0f, const QColor& color_ = Qt::black)
: geometry(geometry_), opacity(opacity_), width(width_), color(color_) {}
@@ -68,7 +77,8 @@ struct Q_DECL_EXPORT LineAnnotation {
QColor color;
};
-struct Q_DECL_EXPORT FillAnnotation {
+struct Q_MAPBOXGL_EXPORT FillAnnotation {
+ /*! Class constructor. */
FillAnnotation(const ShapeAnnotationGeometry& geometry_ = ShapeAnnotationGeometry(), float opacity_ = 1.0f,
const QColor& color_ = Qt::black, const QVariant& outlineColor_ = QVariant())
: geometry(geometry_), opacity(opacity_), color(color_), outlineColor(outlineColor_) {}
@@ -88,13 +98,13 @@ enum NetworkMode {
Offline,
};
-Q_DECL_EXPORT QList<QPair<QString, QString> >& defaultStyles();
+Q_MAPBOXGL_EXPORT QList<QPair<QString, QString> >& defaultStyles();
-Q_DECL_EXPORT NetworkMode networkMode();
-Q_DECL_EXPORT void setNetworkMode(NetworkMode);
+Q_MAPBOXGL_EXPORT NetworkMode networkMode();
+Q_MAPBOXGL_EXPORT void setNetworkMode(NetworkMode);
// This struct is a 1:1 copy of mbgl::CustomLayerRenderParameters.
-struct Q_DECL_EXPORT CustomLayerRenderParameters {
+struct Q_MAPBOXGL_EXPORT CustomLayerRenderParameters {
double width;
double height;
double latitude;
diff --git a/platform/qt/include/qmapboxgl.hpp b/platform/qt/include/qmapboxgl.hpp
index e2fb283989..bc18eaba59 100644
--- a/platform/qt/include/qmapboxgl.hpp
+++ b/platform/qt/include/qmapboxgl.hpp
@@ -10,11 +10,13 @@
#include <QString>
#include <QStringList>
+#include <functional>
+
class QMapboxGLPrivate;
// This header follows the Qt coding style: https://wiki.qt.io/Qt_Coding_Style
-class Q_DECL_EXPORT QMapboxGLSettings
+class Q_MAPBOXGL_EXPORT QMapboxGLSettings
{
public:
QMapboxGLSettings();
@@ -24,6 +26,11 @@ public:
SharedGLContext
};
+ enum MapMode {
+ Continuous = 0,
+ Static
+ };
+
enum ConstrainMode {
NoConstrain = 0,
ConstrainHeightOnly,
@@ -38,6 +45,9 @@ public:
GLContextMode contextMode() const;
void setContextMode(GLContextMode);
+ MapMode mapMode() const;
+ void setMapMode(MapMode);
+
ConstrainMode constrainMode() const;
void setConstrainMode(ConstrainMode);
@@ -59,8 +69,12 @@ public:
QString apiBaseUrl() const;
void setApiBaseUrl(const QString &);
+ std::function<std::string(const std::string &&)> resourceTransform() const;
+ void setResourceTransform(const std::function<std::string(const std::string &&)> &);
+
private:
GLContextMode m_contextMode;
+ MapMode m_mapMode;
ConstrainMode m_constrainMode;
ViewportMode m_viewportMode;
@@ -69,9 +83,10 @@ private:
QString m_assetPath;
QString m_accessToken;
QString m_apiBaseUrl;
+ std::function<std::string(const std::string &&)> m_resourceTransform;
};
-struct Q_DECL_EXPORT QMapboxGLCameraOptions {
+struct Q_MAPBOXGL_EXPORT QMapboxGLCameraOptions {
QVariant center; // Coordinate
QVariant anchor; // QPointF
QVariant zoom; // double
@@ -79,7 +94,7 @@ struct Q_DECL_EXPORT QMapboxGLCameraOptions {
QVariant pitch; // double
};
-class Q_DECL_EXPORT QMapboxGL : public QObject
+class Q_MAPBOXGL_EXPORT QMapboxGL : public QObject
{
Q_OBJECT
Q_PROPERTY(double latitude READ latitude WRITE setLatitude)
@@ -185,8 +200,7 @@ public:
void scaleBy(double scale, const QPointF &center = QPointF());
void rotateBy(const QPointF &first, const QPointF &second);
- void resize(const QSize &size, const QSize &framebufferSize);
- void setFramebufferObject(quint32 fbo);
+ void resize(const QSize &size);
double metersPerPixelAtLatitude(double latitude, double zoom) const;
QMapbox::ProjectedMeters projectedMetersForCoordinate(const QMapbox::Coordinate &) const;
@@ -220,15 +234,27 @@ public:
void setFilter(const QString &layer, const QVariant &filter);
+ // When rendering on a different thread,
+ // should be called on the render thread.
+ void createRenderer();
+ void destroyRenderer();
+ void setFramebufferObject(quint32 fbo, const QSize &size);
+
public slots:
void render();
void connectionEstablished();
+ // Commit changes, load all the resources
+ // and renders the map when completed.
+ void startStaticRender();
+
signals:
void needsRendering();
void mapChanged(QMapboxGL::MapChange);
void copyrightsChanged(const QString &copyrightsHtml);
+ void staticRenderFinished(const QString &error);
+
private:
Q_DISABLE_COPY(QMapboxGL)
diff --git a/platform/qt/mbgl/gl/gl_impl.hpp b/platform/qt/mbgl/gl/gl_impl.hpp
new file mode 100644
index 0000000000..b08efe1eec
--- /dev/null
+++ b/platform/qt/mbgl/gl/gl_impl.hpp
@@ -0,0 +1,178 @@
+#pragma once
+
+#include <QtGlobal>
+
+// Qt4
+#if QT_VERSION < 0x050000
+ #if __APPLE__
+ #include "TargetConditionals.h"
+ #include <OpenGL/OpenGL.h>
+ #include <OpenGL/gl.h>
+ #include <OpenGL/glext.h>
+ #elif MBGL_USE_GLES2
+ #define GL_GLEXT_PROTOTYPES
+ #include <GLES2/gl2.h>
+ #include <GLES2/gl2ext.h>
+ #else
+ #define GL_GLEXT_PROTOTYPES
+ #include <GL/gl.h>
+ #include <GL/glext.h>
+ #endif
+
+// Qt5
+#else
+ #include <QOpenGLContext>
+ #include <QOpenGLFunctions>
+
+ #ifndef GL_RGBA8_OES
+ #define GL_RGBA8_OES GL_RGBA8
+ #endif
+
+ #ifndef GL_DEPTH24_STENCIL8_OES
+ #define GL_DEPTH24_STENCIL8_OES GL_DEPTH24_STENCIL8
+ #endif
+
+ #define glActiveTexture(...) QOpenGLContext::currentContext()->functions()->glActiveTexture(__VA_ARGS__)
+ #define glAttachShader(...) QOpenGLContext::currentContext()->functions()->glAttachShader(__VA_ARGS__)
+ #define glBindAttribLocation(...) QOpenGLContext::currentContext()->functions()->glBindAttribLocation(__VA_ARGS__)
+ #define glBindBuffer(...) QOpenGLContext::currentContext()->functions()->glBindBuffer(__VA_ARGS__)
+ #define glBindFramebuffer(...) QOpenGLContext::currentContext()->functions()->glBindFramebuffer(__VA_ARGS__)
+ #define glBindRenderbuffer(...) QOpenGLContext::currentContext()->functions()->glBindRenderbuffer(__VA_ARGS__)
+ #define glBindTexture(...) QOpenGLContext::currentContext()->functions()->glBindTexture(__VA_ARGS__)
+ #define glBlendColor(...) QOpenGLContext::currentContext()->functions()->glBlendColor(__VA_ARGS__)
+ #define glBlendEquation(...) QOpenGLContext::currentContext()->functions()->glBlendEquation(__VA_ARGS__)
+ #define glBlendEquationSeparate(...) QOpenGLContext::currentContext()->functions()->glBlendEquationSeparate(__VA_ARGS__)
+ #define glBlendFunc(...) QOpenGLContext::currentContext()->functions()->glBlendFunc(__VA_ARGS__)
+ #define glBlendFuncSeparate(...) QOpenGLContext::currentContext()->functions()->glBlendFuncSeparate(__VA_ARGS__)
+ #define glBufferData(...) QOpenGLContext::currentContext()->functions()->glBufferData(__VA_ARGS__)
+ #define glBufferSubData(...) QOpenGLContext::currentContext()->functions()->glBufferSubData(__VA_ARGS__)
+ #define glCheckFramebufferStatus(...) QOpenGLContext::currentContext()->functions()->glCheckFramebufferStatus(__VA_ARGS__)
+ #define glClear(...) QOpenGLContext::currentContext()->functions()->glClear(__VA_ARGS__)
+ #define glClearColor(...) QOpenGLContext::currentContext()->functions()->glClearColor(__VA_ARGS__)
+ #define glClearDepthf(...) QOpenGLContext::currentContext()->functions()->glClearDepthf(__VA_ARGS__)
+ #define glClearStencil(...) QOpenGLContext::currentContext()->functions()->glClearStencil(__VA_ARGS__)
+ #define glColorMask(...) QOpenGLContext::currentContext()->functions()->glColorMask(__VA_ARGS__)
+ #define glCompileShader(...) QOpenGLContext::currentContext()->functions()->glCompileShader(__VA_ARGS__)
+ #define glCompressedTexImage2D(...) QOpenGLContext::currentContext()->functions()->glCompressedTexImage2D(__VA_ARGS__)
+ #define glCompressedTexSubImage2D(...) QOpenGLContext::currentContext()->functions()->glCompressedTexSubImage2D(__VA_ARGS__)
+ #define glCopyTexImage2D(...) QOpenGLContext::currentContext()->functions()->glCopyTexImage2D(__VA_ARGS__)
+ #define glCopyTexSubImage2D(...) QOpenGLContext::currentContext()->functions()->glCopyTexSubImage2D(__VA_ARGS__)
+ #define glCreateProgram(...) QOpenGLContext::currentContext()->functions()->glCreateProgram(__VA_ARGS__)
+ #define glCreateShader(...) QOpenGLContext::currentContext()->functions()->glCreateShader(__VA_ARGS__)
+ #define glCullFace(...) QOpenGLContext::currentContext()->functions()->glCullFace(__VA_ARGS__)
+ #define glDeleteBuffers(...) QOpenGLContext::currentContext()->functions()->glDeleteBuffers(__VA_ARGS__)
+ #define glDeleteFramebuffers(...) QOpenGLContext::currentContext()->functions()->glDeleteFramebuffers(__VA_ARGS__)
+ #define glDeleteProgram(...) QOpenGLContext::currentContext()->functions()->glDeleteProgram(__VA_ARGS__)
+ #define glDeleteRenderbuffers(...) QOpenGLContext::currentContext()->functions()->glDeleteRenderbuffers(__VA_ARGS__)
+ #define glDeleteShader(...) QOpenGLContext::currentContext()->functions()->glDeleteShader(__VA_ARGS__)
+ #define glDeleteTextures(...) QOpenGLContext::currentContext()->functions()->glDeleteTextures(__VA_ARGS__)
+ #define glDepthFunc(...) QOpenGLContext::currentContext()->functions()->glDepthFunc(__VA_ARGS__)
+ #define glDepthMask(...) QOpenGLContext::currentContext()->functions()->glDepthMask(__VA_ARGS__)
+ #define glDepthRangef(...) QOpenGLContext::currentContext()->functions()->glDepthRangef(__VA_ARGS__)
+ #define glDetachShader(...) QOpenGLContext::currentContext()->functions()->glDetachShader(__VA_ARGS__)
+ #define glDisable(...) QOpenGLContext::currentContext()->functions()->glDisable(__VA_ARGS__)
+ #define glDisableVertexAttribArray(...) QOpenGLContext::currentContext()->functions()->glDisableVertexAttribArray(__VA_ARGS__)
+ #define glDrawArrays(...) QOpenGLContext::currentContext()->functions()->glDrawArrays(__VA_ARGS__)
+ #define glDrawElements(...) QOpenGLContext::currentContext()->functions()->glDrawElements(__VA_ARGS__)
+ #define glEnable(...) QOpenGLContext::currentContext()->functions()->glEnable(__VA_ARGS__)
+ #define glEnableVertexAttribArray(...) QOpenGLContext::currentContext()->functions()->glEnableVertexAttribArray(__VA_ARGS__)
+ #define glFinish(...) QOpenGLContext::currentContext()->functions()->glFinish(__VA_ARGS__)
+ #define glFlush(...) QOpenGLContext::currentContext()->functions()->glFlush(__VA_ARGS__)
+ #define glFramebufferRenderbuffer(...) QOpenGLContext::currentContext()->functions()->glFramebufferRenderbuffer(__VA_ARGS__)
+ #define glFramebufferTexture2D(...) QOpenGLContext::currentContext()->functions()->glFramebufferTexture2D(__VA_ARGS__)
+ #define glFrontFace(...) QOpenGLContext::currentContext()->functions()->glFrontFace(__VA_ARGS__)
+ #define glGenBuffers(...) QOpenGLContext::currentContext()->functions()->glGenBuffers(__VA_ARGS__)
+ #define glGenerateMipmap(...) QOpenGLContext::currentContext()->functions()->glGenerateMipmap(__VA_ARGS__)
+ #define glGenFramebuffers(...) QOpenGLContext::currentContext()->functions()->glGenFramebuffers(__VA_ARGS__)
+ #define glGenRenderbuffers(...) QOpenGLContext::currentContext()->functions()->glGenRenderbuffers(__VA_ARGS__)
+ #define glGenTextures(...) QOpenGLContext::currentContext()->functions()->glGenTextures(__VA_ARGS__)
+ #define glGetActiveAttrib(...) QOpenGLContext::currentContext()->functions()->glGetActiveAttrib(__VA_ARGS__)
+ #define glGetActiveUniform(...) QOpenGLContext::currentContext()->functions()->glGetActiveUniform(__VA_ARGS__)
+ #define glGetAttachedShaders(...) QOpenGLContext::currentContext()->functions()->glGetAttachedShaders(__VA_ARGS__)
+ #define glGetAttribLocation(...) QOpenGLContext::currentContext()->functions()->glGetAttribLocation(__VA_ARGS__)
+ #define glGetBooleanv(...) QOpenGLContext::currentContext()->functions()->glGetBooleanv(__VA_ARGS__)
+ #define glGetBufferParameteriv(...) QOpenGLContext::currentContext()->functions()->glGetBufferParameteriv(__VA_ARGS__)
+ #define glGetError(...) QOpenGLContext::currentContext()->functions()->glGetError(__VA_ARGS__)
+ #define glGetFloatv(...) QOpenGLContext::currentContext()->functions()->glGetFloatv(__VA_ARGS__)
+ #define glGetFramebufferAttachmentParameteriv(...) QOpenGLContext::currentContext()->functions()->glGetFramebufferAttachmentParameteriv(__VA_ARGS__)
+ #define glGetIntegerv(...) QOpenGLContext::currentContext()->functions()->glGetIntegerv(__VA_ARGS__)
+ #define glGetProgramInfoLog(...) QOpenGLContext::currentContext()->functions()->glGetProgramInfoLog(__VA_ARGS__)
+ #define glGetProgramiv(...) QOpenGLContext::currentContext()->functions()->glGetProgramiv(__VA_ARGS__)
+ #define glGetRenderbufferParameteriv(...) QOpenGLContext::currentContext()->functions()->glGetRenderbufferParameteriv(__VA_ARGS__)
+ #define glGetShaderInfoLog(...) QOpenGLContext::currentContext()->functions()->glGetShaderInfoLog(__VA_ARGS__)
+ #define glGetShaderiv(...) QOpenGLContext::currentContext()->functions()->glGetShaderiv(__VA_ARGS__)
+ #define glGetShaderPrecisionFormat(...) QOpenGLContext::currentContext()->functions()->glGetShaderPrecisionFormat(__VA_ARGS__)
+ #define glGetShaderSource(...) QOpenGLContext::currentContext()->functions()->glGetShaderSource(__VA_ARGS__)
+ #define glGetString(...) QOpenGLContext::currentContext()->functions()->glGetString(__VA_ARGS__)
+ #define glGetTexParameterfv(...) QOpenGLContext::currentContext()->functions()->glGetTexParameterfv(__VA_ARGS__)
+ #define glGetTexParameteriv(...) QOpenGLContext::currentContext()->functions()->glGetTexParameteriv(__VA_ARGS__)
+ #define glGetUniformfv(...) QOpenGLContext::currentContext()->functions()->glGetUniformfv(__VA_ARGS__)
+ #define glGetUniformiv(...) QOpenGLContext::currentContext()->functions()->glGetUniformiv(__VA_ARGS__)
+ #define glGetUniformLocation(...) QOpenGLContext::currentContext()->functions()->glGetUniformLocation(__VA_ARGS__)
+ #define glGetVertexAttribfv(...) QOpenGLContext::currentContext()->functions()->glGetVertexAttribfv(__VA_ARGS__)
+ #define glGetVertexAttribiv(...) QOpenGLContext::currentContext()->functions()->glGetVertexAttribiv(__VA_ARGS__)
+ #define glGetVertexAttribPointerv(...) QOpenGLContext::currentContext()->functions()->glGetVertexAttribPointerv(__VA_ARGS__)
+ #define glHint(...) QOpenGLContext::currentContext()->functions()->glHint(__VA_ARGS__)
+ #define glIsBuffer(...) QOpenGLContext::currentContext()->functions()->glIsBuffer(__VA_ARGS__)
+ #define glIsEnabled(...) QOpenGLContext::currentContext()->functions()->glIsEnabled(__VA_ARGS__)
+ #define glIsFramebuffer(...) QOpenGLContext::currentContext()->functions()->glIsFramebuffer(__VA_ARGS__)
+ #define glIsProgram(...) QOpenGLContext::currentContext()->functions()->glIsProgram(__VA_ARGS__)
+ #define glIsRenderbuffer(...) QOpenGLContext::currentContext()->functions()->glIsRenderbuffer(__VA_ARGS__)
+ #define glIsShader(...) QOpenGLContext::currentContext()->functions()->glIsShader(__VA_ARGS__)
+ #define glIsTexture(...) QOpenGLContext::currentContext()->functions()->glIsTexture(__VA_ARGS__)
+ #define glLineWidth(...) QOpenGLContext::currentContext()->functions()->glLineWidth(__VA_ARGS__)
+ #define glLinkProgram(...) QOpenGLContext::currentContext()->functions()->glLinkProgram(__VA_ARGS__)
+ #define glPixelStorei(...) QOpenGLContext::currentContext()->functions()->glPixelStorei(__VA_ARGS__)
+ #define glPolygonOffset(...) QOpenGLContext::currentContext()->functions()->glPolygonOffset(__VA_ARGS__)
+ #define glReadPixels(...) QOpenGLContext::currentContext()->functions()->glReadPixels(__VA_ARGS__)
+ #define glReleaseShaderCompiler(...) QOpenGLContext::currentContext()->functions()->glReleaseShaderCompiler(__VA_ARGS__)
+ #define glRenderbufferStorage(...) QOpenGLContext::currentContext()->functions()->glRenderbufferStorage(__VA_ARGS__)
+ #define glSampleCoverage(...) QOpenGLContext::currentContext()->functions()->glSampleCoverage(__VA_ARGS__)
+ #define glScissor(...) QOpenGLContext::currentContext()->functions()->glScissor(__VA_ARGS__)
+ #define glShaderBinary(...) QOpenGLContext::currentContext()->functions()->glShaderBinary(__VA_ARGS__)
+ #define glShaderSource(...) QOpenGLContext::currentContext()->functions()->glShaderSource(__VA_ARGS__)
+ #define glStencilFunc(...) QOpenGLContext::currentContext()->functions()->glStencilFunc(__VA_ARGS__)
+ #define glStencilFuncSeparate(...) QOpenGLContext::currentContext()->functions()->glStencilFuncSeparate(__VA_ARGS__)
+ #define glStencilMask(...) QOpenGLContext::currentContext()->functions()->glStencilMask(__VA_ARGS__)
+ #define glStencilMaskSeparate(...) QOpenGLContext::currentContext()->functions()->glStencilMaskSeparate(__VA_ARGS__)
+ #define glStencilOp(...) QOpenGLContext::currentContext()->functions()->glStencilOp(__VA_ARGS__)
+ #define glStencilOpSeparate(...) QOpenGLContext::currentContext()->functions()->glStencilOpSeparate(__VA_ARGS__)
+ #define glTexImage2D(...) QOpenGLContext::currentContext()->functions()->glTexImage2D(__VA_ARGS__)
+ #define glTexLevelParameteriv(...) QOpenGLContext::currentContext()->functions()->glTexLevelParameteriv(__VA_ARGS__)
+ #define glTexParameterf(...) QOpenGLContext::currentContext()->functions()->glTexParameterf(__VA_ARGS__)
+ #define glTexParameterfv(...) QOpenGLContext::currentContext()->functions()->glTexParameterfv(__VA_ARGS__)
+ #define glTexParameteri(...) QOpenGLContext::currentContext()->functions()->glTexParameteri(__VA_ARGS__)
+ #define glTexParameteriv(...) QOpenGLContext::currentContext()->functions()->glTexParameteriv(__VA_ARGS__)
+ #define glTexSubImage2D(...) QOpenGLContext::currentContext()->functions()->glTexSubImage2D(__VA_ARGS__)
+ #define glUniform1f(...) QOpenGLContext::currentContext()->functions()->glUniform1f(__VA_ARGS__)
+ #define glUniform1fv(...) QOpenGLContext::currentContext()->functions()->glUniform1fv(__VA_ARGS__)
+ #define glUniform1i(...) QOpenGLContext::currentContext()->functions()->glUniform1i(__VA_ARGS__)
+ #define glUniform1iv(...) QOpenGLContext::currentContext()->functions()->glUniform1iv(__VA_ARGS__)
+ #define glUniform2f(...) QOpenGLContext::currentContext()->functions()->glUniform2f(__VA_ARGS__)
+ #define glUniform2fv(...) QOpenGLContext::currentContext()->functions()->glUniform2fv(__VA_ARGS__)
+ #define glUniform2i(...) QOpenGLContext::currentContext()->functions()->glUniform2i(__VA_ARGS__)
+ #define glUniform2iv(...) QOpenGLContext::currentContext()->functions()->glUniform2iv(__VA_ARGS__)
+ #define glUniform3f(...) QOpenGLContext::currentContext()->functions()->glUniform3f(__VA_ARGS__)
+ #define glUniform3fv(...) QOpenGLContext::currentContext()->functions()->glUniform3fv(__VA_ARGS__)
+ #define glUniform3i(...) QOpenGLContext::currentContext()->functions()->glUniform3i(__VA_ARGS__)
+ #define glUniform3iv(...) QOpenGLContext::currentContext()->functions()->glUniform3iv(__VA_ARGS__)
+ #define glUniform4f(...) QOpenGLContext::currentContext()->functions()->glUniform4f(__VA_ARGS__)
+ #define glUniform4fv(...) QOpenGLContext::currentContext()->functions()->glUniform4fv(__VA_ARGS__)
+ #define glUniform4i(...) QOpenGLContext::currentContext()->functions()->glUniform4i(__VA_ARGS__)
+ #define glUniform4iv(...) QOpenGLContext::currentContext()->functions()->glUniform4iv(__VA_ARGS__)
+ #define glUniformMatrix2fv(...) QOpenGLContext::currentContext()->functions()->glUniformMatrix2fv(__VA_ARGS__)
+ #define glUniformMatrix3fv(...) QOpenGLContext::currentContext()->functions()->glUniformMatrix3fv(__VA_ARGS__)
+ #define glUniformMatrix4fv(...) QOpenGLContext::currentContext()->functions()->glUniformMatrix4fv(__VA_ARGS__)
+ #define glUseProgram(...) QOpenGLContext::currentContext()->functions()->glUseProgram(__VA_ARGS__)
+ #define glValidateProgram(...) QOpenGLContext::currentContext()->functions()->glValidateProgram(__VA_ARGS__)
+ #define glVertexAttrib1f(...) QOpenGLContext::currentContext()->functions()->glVertexAttrib1f(__VA_ARGS__)
+ #define glVertexAttrib1fv(...) QOpenGLContext::currentContext()->functions()->glVertexAttrib1fv(__VA_ARGS__)
+ #define glVertexAttrib2f(...) QOpenGLContext::currentContext()->functions()->glVertexAttrib2f(__VA_ARGS__)
+ #define glVertexAttrib2fv(...) QOpenGLContext::currentContext()->functions()->glVertexAttrib2fv(__VA_ARGS__)
+ #define glVertexAttrib3f(...) QOpenGLContext::currentContext()->functions()->glVertexAttrib3f(__VA_ARGS__)
+ #define glVertexAttrib3fv(...) QOpenGLContext::currentContext()->functions()->glVertexAttrib3fv(__VA_ARGS__)
+ #define glVertexAttrib4f(...) QOpenGLContext::currentContext()->functions()->glVertexAttrib4f(__VA_ARGS__)
+ #define glVertexAttrib4fv(...) QOpenGLContext::currentContext()->functions()->glVertexAttrib4fv(__VA_ARGS__)
+ #define glVertexAttribPointer(...) QOpenGLContext::currentContext()->functions()->glVertexAttribPointer(__VA_ARGS__)
+ #define glViewport(...) QOpenGLContext::currentContext()->functions()->glViewport(__VA_ARGS__)
+#endif
diff --git a/platform/qt/ninja.exe b/platform/qt/ninja.exe
new file mode 100755
index 0000000000..f86ef07382
--- /dev/null
+++ b/platform/qt/ninja.exe
Binary files differ
diff --git a/platform/qt/qt.cmake b/platform/qt/qt.cmake
index 2346d7d820..d1d597bda6 100644
--- a/platform/qt/qt.cmake
+++ b/platform/qt/qt.cmake
@@ -5,8 +5,7 @@ option(WITH_QT_DECODERS "Use builtin Qt image decoders" OFF)
option(WITH_QT_I18N "Use builtin Qt i18n support" OFF)
option(WITH_QT_4 "Use Qt4 instead of Qt5" OFF)
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -D__QT__")
-set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden -D__QT__")
+add_definitions("-D__QT__")
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
@@ -17,13 +16,8 @@ set(MBGL_QT_CORE_FILES
PRIVATE platform/default/mbgl/gl/headless_frontend.hpp
PRIVATE platform/default/mbgl/gl/headless_backend.cpp
PRIVATE platform/default/mbgl/gl/headless_backend.hpp
- PRIVATE platform/default/mbgl/gl/headless_display.cpp
- PRIVATE platform/default/mbgl/gl/headless_display.hpp
PRIVATE platform/qt/src/headless_backend_qt.cpp
- # Misc
- PRIVATE platform/default/logging_stderr.cpp
-
# Thread pool
PRIVATE platform/default/mbgl/util/shared_thread_pool.cpp
PRIVATE platform/default/mbgl/util/shared_thread_pool.hpp
@@ -36,6 +30,7 @@ set(MBGL_QT_CORE_FILES
# Platform integration
PRIVATE platform/qt/src/async_task.cpp
PRIVATE platform/qt/src/async_task_impl.hpp
+ PRIVATE platform/qt/src/qt_logging.cpp
PRIVATE platform/qt/src/qt_image.cpp
PRIVATE platform/qt/src/run_loop.cpp
PRIVATE platform/qt/src/run_loop_impl.hpp
@@ -60,11 +55,18 @@ set(MBGL_QT_FILESOURCE_FILES
add_library(qmapboxgl SHARED
platform/qt/include/qmapbox.hpp
platform/qt/include/qmapboxgl.hpp
+ platform/qt/src/qt_conversion.hpp
+ platform/qt/src/qt_geojson.cpp
+ platform/qt/src/qt_geojson.hpp
platform/qt/src/qmapbox.cpp
platform/qt/src/qmapboxgl.cpp
platform/qt/src/qmapboxgl_p.hpp
- platform/qt/src/qmapboxgl_renderer_frontend_p.hpp
- platform/qt/src/qmapboxgl_renderer_frontend_p.cpp
+ platform/qt/src/qmapboxgl_map_observer.cpp
+ platform/qt/src/qmapboxgl_map_observer.hpp
+ platform/qt/src/qmapboxgl_map_renderer.cpp
+ platform/qt/src/qmapboxgl_map_renderer.hpp
+ platform/qt/src/qmapboxgl_renderer_backend.hpp
+ platform/qt/src/qmapboxgl_renderer_backend.cpp
platform/default/mbgl/util/default_styles.hpp
)
@@ -72,6 +74,10 @@ target_include_directories(qmapboxgl
PUBLIC platform/qt/include
)
+target_compile_definitions(qmapboxgl
+ PRIVATE "-DQT_BUILD_MAPBOXGL_LIB"
+)
+
target_link_libraries(qmapboxgl
PRIVATE mbgl-core
PRIVATE mbgl-filesource
@@ -103,12 +109,6 @@ endif()
xcode_create_scheme(TARGET mbgl-qt)
-if(WITH_QT_4)
- include(platform/qt/qt4.cmake)
-else()
- include(platform/qt/qt5.cmake)
-endif()
-
# OS specific configurations
if (MASON_PLATFORM STREQUAL "osx" OR MASON_PLATFORM STREQUAL "ios")
list(APPEND MBGL_QT_CORE_FILES
@@ -116,19 +116,42 @@ if (MASON_PLATFORM STREQUAL "osx" OR MASON_PLATFORM STREQUAL "ios")
)
list(APPEND MBGL_QT_CORE_LIBRARIES
PRIVATE "-framework Foundation"
- PRIVATE "-framework OpenGL"
)
+ if(WITH_QT_4)
+ list(APPEND MBGL_QT_CORE_LIBRARIES
+ PRIVATE "-framework OpenGL"
+ )
+ endif()
elseif (CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux")
list(APPEND MBGL_QT_CORE_FILES
PRIVATE platform/default/thread.cpp
)
- list(APPEND MBGL_QT_CORE_LIBRARIES
- PRIVATE -lGL
- )
+ if(WITH_QT_4)
+ list(APPEND MBGL_QT_CORE_LIBRARIES
+ PRIVATE "-lGL"
+ )
+ endif()
elseif (CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
+ add_definitions("-DQT_COMPILING_QIMAGE_COMPAT_CPP")
+ add_definitions("-DRAPIDJSON_HAS_CXX11_RVALUE_REFS")
+ add_definitions("-D_USE_MATH_DEFINES")
+ add_definitions("-D_WINDOWS")
+
+ add_definitions("-Wno-deprecated-declarations")
+ add_definitions("-Wno-macro-redefined")
+ add_definitions("-Wno-microsoft-exception-spec")
+ add_definitions("-Wno-unknown-argument")
+ add_definitions("-Wno-unknown-warning-option")
+ add_definitions("-Wno-unused-command-line-argument")
+ add_definitions("-Wno-unused-local-typedef")
+ add_definitions("-Wno-unused-private-field")
+
list(APPEND MBGL_QT_CORE_FILES
PRIVATE platform/qt/src/thread.cpp
)
+
+ target_add_mason_package(qmapboxgl PRIVATE optional)
+ target_add_mason_package(qmapboxgl PRIVATE tao_tuple)
endif()
add_custom_command(
diff --git a/platform/qt/qt5.cmake b/platform/qt/qt5.cmake
index c4af774ba3..d083c45073 100644
--- a/platform/qt/qt5.cmake
+++ b/platform/qt/qt5.cmake
@@ -5,6 +5,11 @@ find_package(Qt5OpenGL REQUIRED)
find_package(Qt5Widgets REQUIRED)
find_package(Qt5Sql REQUIRED)
+# Qt5 always build OpenGL ES2 which is the compatibility
+# mode. Qt5 will take care of translating the desktop
+# version of OpenGL to ES2.
+add_definitions("-DMBGL_USE_GLES2")
+
set(MBGL_QT_CORE_LIBRARIES
PUBLIC Qt5::Core
PUBLIC Qt5::Gui
diff --git a/platform/qt/resources/common.qrc b/platform/qt/resources/common.qrc
index 598059d55e..24f663df77 100644
--- a/platform/qt/resources/common.qrc
+++ b/platform/qt/resources/common.qrc
@@ -1,6 +1,6 @@
<RCC>
<qresource prefix="/">
- <file alias="icon.png">../../../common/mb-icon-blue-square.png</file>
+ <file alias="icon.png">../../../misc/mb-icon-blue-square.png</file>
<file alias="default_marker.svg">../../../platform/default/resources/default_marker.svg</file>
<file>source1.geojson</file>
<file>source2.geojson</file>
diff --git a/platform/qt/src/headless_backend_qt.cpp b/platform/qt/src/headless_backend_qt.cpp
index 5f95b2f96a..ad3fa42290 100644
--- a/platform/qt/src/headless_backend_qt.cpp
+++ b/platform/qt/src/headless_backend_qt.cpp
@@ -12,19 +12,11 @@
namespace mbgl {
-struct QtImpl : public HeadlessBackend::Impl {
- void activateContext() final {
- widget.makeCurrent();
- }
+class QtBackendImpl : public HeadlessBackend::Impl {
+public:
+ ~QtBackendImpl() final = default;
- void deactivateContext() final {
- widget.doneCurrent();
- }
-
- QGLWidget widget;
-};
-
-gl::ProcAddress HeadlessBackend::initializeExtension(const char* name) {
+ gl::ProcAddress getExtensionFunctionPointer(const char* name) final {
#if QT_VERSION >= 0x050000
QOpenGLContext* thisContext = QOpenGLContext::currentContext();
return thisContext->getProcAddress(name);
@@ -32,15 +24,23 @@ gl::ProcAddress HeadlessBackend::initializeExtension(const char* name) {
const QGLContext* thisContext = QGLContext::currentContext();
return reinterpret_cast<gl::ProcAddress>(thisContext->getProcAddress(name));
#endif
-}
+ }
+
+ void activateContext() final {
+ widget.makeCurrent();
+ }
-bool HeadlessBackend::hasDisplay() {
- return true;
+ void deactivateContext() final {
+ widget.doneCurrent();
+ }
+
+private:
+ QGLWidget widget;
};
-void HeadlessBackend::createContext() {
- assert(!hasContext());
- impl.reset(new QtImpl);
+void HeadlessBackend::createImpl() {
+ assert(!impl);
+ impl = std::make_unique<QtBackendImpl>();
}
} // namespace mbgl
diff --git a/platform/qt/src/http_file_source.cpp b/platform/qt/src/http_file_source.cpp
index 573b707c27..6e70693241 100644
--- a/platform/qt/src/http_file_source.cpp
+++ b/platform/qt/src/http_file_source.cpp
@@ -80,9 +80,10 @@ void HTTPFileSource::Impl::onReplyFinished()
return;
}
+ QByteArray data = reply->readAll();
QVector<HTTPRequest*>& requestsVector = it.value().second;
for (auto req : requestsVector) {
- req->handleNetworkReply(reply);
+ req->handleNetworkReply(reply, data);
}
m_pending.erase(it);
diff --git a/platform/qt/src/http_request.cpp b/platform/qt/src/http_request.cpp
index 386a2d9ef4..ea3f388bd5 100644
--- a/platform/qt/src/http_request.cpp
+++ b/platform/qt/src/http_request.cpp
@@ -52,7 +52,7 @@ QNetworkRequest HTTPRequest::networkRequest() const
return req;
}
-void HTTPRequest::handleNetworkReply(QNetworkReply *reply)
+void HTTPRequest::handleNetworkReply(QNetworkReply *reply, const QByteArray& data)
{
m_handled = true;
@@ -98,11 +98,10 @@ void HTTPRequest::handleNetworkReply(QNetworkReply *reply)
switch(responseCode) {
case 200: {
- QByteArray bytes = reply->readAll();
- if (bytes.isEmpty()) {
+ if (data.isEmpty()) {
response.data = std::make_shared<std::string>();
} else {
- response.data = std::make_shared<std::string>(bytes.constData(), bytes.size());
+ response.data = std::make_shared<std::string>(data.constData(), data.size());
}
break;
}
diff --git a/platform/qt/src/http_request.hpp b/platform/qt/src/http_request.hpp
index 959f97759a..b4d476d586 100644
--- a/platform/qt/src/http_request.hpp
+++ b/platform/qt/src/http_request.hpp
@@ -20,7 +20,7 @@ public:
QUrl requestUrl() const;
QNetworkRequest networkRequest() const;
- void handleNetworkReply(QNetworkReply *);
+ void handleNetworkReply(QNetworkReply *, const QByteArray& data);
private:
HTTPFileSource::Impl* m_context;
diff --git a/platform/qt/src/qmapbox.cpp b/platform/qt/src/qmapbox.cpp
index aad32a35dc..ec76ebfe53 100644
--- a/platform/qt/src/qmapbox.cpp
+++ b/platform/qt/src/qmapbox.cpp
@@ -206,7 +206,7 @@ namespace QMapbox {
Returns the current QMapbox::NetworkMode.
*/
-Q_DECL_EXPORT NetworkMode networkMode()
+NetworkMode networkMode()
{
return static_cast<NetworkMode>(mbgl::NetworkStatus::Get());
}
@@ -219,7 +219,7 @@ Q_DECL_EXPORT NetworkMode networkMode()
File source requests uses the available network when \a mode is set to \b
Online, otherwise scoped to the local cache.
*/
-Q_DECL_EXPORT void setNetworkMode(NetworkMode mode)
+void setNetworkMode(NetworkMode mode)
{
mbgl::NetworkStatus::Set(static_cast<mbgl::NetworkStatus::Status>(mode));
}
@@ -230,7 +230,7 @@ Q_DECL_EXPORT void setNetworkMode(NetworkMode mode)
Returns a list containing a pair of string objects, representing the style
URL and name, respectively.
*/
-Q_DECL_EXPORT QList<QPair<QString, QString> >& defaultStyles()
+QList<QPair<QString, QString> >& defaultStyles()
{
static QList<QPair<QString, QString>> styles;
diff --git a/platform/qt/src/qmapboxgl.cpp b/platform/qt/src/qmapboxgl.cpp
index e79c6af56a..414b65255c 100644
--- a/platform/qt/src/qmapboxgl.cpp
+++ b/platform/qt/src/qmapboxgl.cpp
@@ -1,6 +1,8 @@
#include "qmapboxgl.hpp"
#include "qmapboxgl_p.hpp"
-#include "qmapboxgl_renderer_frontend_p.hpp"
+
+#include "qmapboxgl_map_observer.hpp"
+#include "qmapboxgl_renderer_observer.hpp"
#include "qt_conversion.hpp"
#include "qt_geojson.hpp"
@@ -14,7 +16,17 @@
#include <mbgl/style/conversion.hpp>
#include <mbgl/style/conversion/layer.hpp>
#include <mbgl/style/conversion/source.hpp>
+#include <mbgl/style/conversion/filter.hpp>
+#include <mbgl/style/conversion/geojson.hpp>
+#include <mbgl/style/filter.hpp>
#include <mbgl/style/layers/custom_layer.hpp>
+#include <mbgl/style/layers/background_layer.hpp>
+#include <mbgl/style/layers/circle_layer.hpp>
+#include <mbgl/style/layers/fill_layer.hpp>
+#include <mbgl/style/layers/fill_extrusion_layer.hpp>
+#include <mbgl/style/layers/line_layer.hpp>
+#include <mbgl/style/layers/raster_layer.hpp>
+#include <mbgl/style/layers/symbol_layer.hpp>
#include <mbgl/style/sources/geojson_source.hpp>
#include <mbgl/style/transition_options.hpp>
#include <mbgl/style/image.hpp>
@@ -29,14 +41,12 @@
#include <mbgl/util/run_loop.hpp>
#include <mbgl/util/shared_thread_pool.hpp>
#include <mbgl/util/traits.hpp>
+#include <mbgl/actor/scheduler.hpp>
#if QT_VERSION >= 0x050000
#include <QGuiApplication>
-#include <QWindow>
-#include <QOpenGLContext>
#else
#include <QCoreApplication>
-#include <QGLContext>
#endif
#include <QDebug>
@@ -54,6 +64,10 @@ using namespace QMapbox;
static_assert(mbgl::underlying_type(QMapboxGLSettings::UniqueGLContext) == mbgl::underlying_type(mbgl::GLContextMode::Unique), "error");
static_assert(mbgl::underlying_type(QMapboxGLSettings::SharedGLContext) == mbgl::underlying_type(mbgl::GLContextMode::Shared), "error");
+// mbgl::MapMode
+static_assert(mbgl::underlying_type(QMapboxGLSettings::Continuous) == mbgl::underlying_type(mbgl::MapMode::Continuous), "error");
+static_assert(mbgl::underlying_type(QMapboxGLSettings::Static) == mbgl::underlying_type(mbgl::MapMode::Static), "error");
+
// mbgl::ConstrainMode
static_assert(mbgl::underlying_type(QMapboxGLSettings::NoConstrain) == mbgl::underlying_type(mbgl::ConstrainMode::None), "error");
static_assert(mbgl::underlying_type(QMapboxGLSettings::ConstrainHeightOnly) == mbgl::underlying_type(mbgl::ConstrainMode::HeightOnly), "error");
@@ -100,8 +114,13 @@ std::unique_ptr<mbgl::style::Image> toStyleImage(const QString &id, const QImage
.rgbSwapped()
.convertToFormat(QImage::Format_ARGB32_Premultiplied);
+#if QT_VERSION >= 0x051000
+ auto img = std::make_unique<uint8_t[]>(swapped.sizeInBytes());
+ memcpy(img.get(), swapped.constBits(), swapped.sizeInBytes());
+#else
auto img = std::make_unique<uint8_t[]>(swapped.byteCount());
memcpy(img.get(), swapped.constBits(), swapped.byteCount());
+#endif
return std::make_unique<mbgl::style::Image>(
id.toStdString(),
@@ -139,14 +158,37 @@ std::unique_ptr<mbgl::style::Image> toStyleImage(const QString &id, const QImage
reset before each rendering. Use this mode if the intention is to only draw a
fullscreen map.
- \value SharedGLContext The OpenGL context is shared and the state will be restored
- before rendering. This mode is safer when OpenGL calls are performed prior of after
- we call QMapboxGL::render for rendering a map.
+ \value SharedGLContext The OpenGL context is shared and the state will be
+ marked dirty - which invalidates any previously assumed GL state. The
+ embedder is responsible for clearing up the viewport prior to calling
+ QMapboxGL::render. The embedder is also responsible for resetting its own
+ GL state after QMapboxGL::render has finished, if needed.
\sa contextMode()
*/
/*!
+ \enum QMapboxGLSettings::MapMode
+
+ This enum sets the map rendering mode
+
+ \value Continuous The map will render as data arrives from the network and
+ react immediately to state changes.
+
+ This is the default mode and the preferred when the map is intended to be
+ interactive.
+
+ \value Static The map will no longer react to state changes and will only
+ be rendered when QMapboxGL::startStaticRender is called. After all the
+ resources are loaded, the QMapboxGL::staticRenderFinished signal is emitted.
+
+ This mode is useful for taking a snapshot of the finished rendering result
+ of the map into a QImage.
+
+ \sa mapMode()
+*/
+
+/*!
\enum QMapboxGLSettings::ConstrainMode
This enum determines if the map wraps.
@@ -182,6 +224,7 @@ std::unique_ptr<mbgl::style::Image> toStyleImage(const QString &id, const QImage
*/
QMapboxGLSettings::QMapboxGLSettings()
: m_contextMode(QMapboxGLSettings::SharedGLContext)
+ , m_mapMode(QMapboxGLSettings::Continuous)
, m_constrainMode(QMapboxGLSettings::ConstrainHeightOnly)
, m_viewportMode(QMapboxGLSettings::DefaultViewport)
, m_cacheMaximumSize(mbgl::util::DEFAULT_MAX_CACHE_SIZE)
@@ -212,6 +255,31 @@ void QMapboxGLSettings::setContextMode(GLContextMode mode)
}
/*!
+ Returns the map mode. Static mode will emit a signal for
+ rendering a map only when the map is fully loaded.
+ Animations like style transitions and labels fading won't
+ be seen.
+
+ The Continuous mode will emit the signal for every new
+ change on the map and it is usually what you expect for
+ a interactive map.
+
+ By default, it is set to QMapboxGLSettings::Continuous.
+*/
+QMapboxGLSettings::MapMode QMapboxGLSettings::mapMode() const
+{
+ return m_mapMode;
+}
+
+/*!
+ Sets the map \a mode.
+*/
+void QMapboxGLSettings::setMapMode(MapMode mode)
+{
+ m_mapMode = mode;
+}
+
+/*!
Returns the constrain mode. This is used to limit the map to wrap
around the globe horizontally.
@@ -362,6 +430,27 @@ void QMapboxGLSettings::setApiBaseUrl(const QString& url)
}
/*!
+ Returns resource transformation callback used to transform requested URLs.
+*/
+std::function<std::string(const std::string &&)> QMapboxGLSettings::resourceTransform() const
+{
+ return m_resourceTransform;
+}
+
+/*!
+ Sets the resource \a transform callback.
+
+ When given, resource transformation callback will be used to transform the
+ requested resource URLs before they are requested from internet. This can be
+ used add or remove custom parameters, or reroute certain requests to other
+ servers or endpoints.
+*/
+void QMapboxGLSettings::setResourceTransform(const std::function<std::string(const std::string &&)> &transform)
+{
+ m_resourceTransform = transform;
+}
+
+/*!
\class QMapboxGL
\brief The QMapboxGL class is a Qt wrapper for the Mapbox GL Native engine.
@@ -850,7 +939,7 @@ void QMapboxGL::removeAnnotation(QMapbox::AnnotationID id)
}
/*!
- Sets a layout \a property \a value to an existing \a layer. The \a property_ string can be any
+ Sets a layout \a property_ \a value to an existing \a layer. The \a property_ string can be any
as defined by the \l {https://www.mapbox.com/mapbox-gl-style-spec/} {Mapbox style specification}
for layout properties.
@@ -901,7 +990,7 @@ void QMapboxGL::setLayoutProperty(const QString& layer, const QString& property_
}
/*!
- Sets a paint \a property_ \a value to an existing \a layer. The \a property string can be any
+ Sets a paint \a property_ \a value to an existing \a layer. The \a property_ string can be any
as defined by the \l {https://www.mapbox.com/mapbox-gl-style-spec/} {Mapbox style specification}
for paint properties.
@@ -1009,32 +1098,17 @@ void QMapboxGL::rotateBy(const QPointF &first, const QPointF &second)
}
/*!
- Resize the map to \a size and scale to fit at \a framebufferSize. For
- high DPI screens, the size will be smaller than the \a framebufferSize.
-
- This fallowing example will double the pixel density of the map for
- a given \c size:
-
- \code
- map->resize(size / 2, size);
- \endcode
+ Resize the map to \a size_ and scale to fit at the framebuffer. For
+ high DPI screens, the size will be smaller than the framebuffer.
*/
-void QMapboxGL::resize(const QSize& size, const QSize& framebufferSize)
+void QMapboxGL::resize(const QSize& size_)
{
- if (d_ptr->size == size && d_ptr->fbSize == framebufferSize) return;
+ auto size = sanitizedSize(size_);
- d_ptr->size = size;
- d_ptr->fbSize = framebufferSize;
-
- d_ptr->mapObj->setSize(sanitizedSize(size));
-}
+ if (d_ptr->mapObj->getSize() == size)
+ return;
-/*!
- If Mapbox GL needs to rebind the default \a fbo, it will use the
- ID supplied here.
-*/
-void QMapboxGL::setFramebufferObject(quint32 fbo) {
- d_ptr->fbObject = fbo;
+ d_ptr->mapObj->setSize(size);
}
/*!
@@ -1429,6 +1503,51 @@ void QMapboxGL::setFilter(const QString& layer, const QVariant& filter)
}
/*!
+ Creates the infrastructure needed for rendering the map. It
+ should be called before any call to render().
+
+ Must be called on the render thread.
+*/
+void QMapboxGL::createRenderer()
+{
+ d_ptr->createRenderer();
+}
+
+/*!
+ Destroys the infrastructure needed for rendering the map,
+ releasing resources.
+
+ Must be called on the render thread.
+*/
+void QMapboxGL::destroyRenderer()
+{
+ d_ptr->destroyRenderer();
+}
+
+/*!
+ Start a static rendering of the current state of the map. This
+ should only be called when the map is initialized in static mode.
+
+ \sa QMapboxGLSettings::MapMode
+*/
+void QMapboxGL::startStaticRender()
+{
+ d_ptr->mapObj->renderStill([this](std::exception_ptr err) {
+ QString what;
+
+ try {
+ if (err) {
+ std::rethrow_exception(err);
+ }
+ } catch(const std::exception& e) {
+ what = e.what();
+ }
+
+ emit staticRenderFinished(what);
+ });
+}
+
+/*!
Renders the map using OpenGL draw calls. It will make sure to bind the
framebuffer object before drawing; otherwise a valid OpenGL context is
expected with an appropriate OpenGL viewport state set for the size of
@@ -1436,28 +1555,33 @@ void QMapboxGL::setFilter(const QString& layer, const QVariant& filter)
This function should be called only after the signal needsRendering() is
emitted at least once.
+
+ Must be called on the render thread.
*/
void QMapboxGL::render()
{
-#if defined(__APPLE__) && QT_VERSION < 0x050000
- // FIXME Qt 4.x provides an incomplete FBO at start.
- // See https://bugreports.qt.io/browse/QTBUG-36802 for details.
- if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
- return;
- }
-#endif
-
- d_ptr->dirty = false;
d_ptr->render();
}
/*!
+ If Mapbox GL needs to rebind the default \a fbo, it will use the
+ ID supplied here. \a size is the size of the framebuffer, which
+ on high DPI screens is usually bigger than the map size.
+
+ Must be called on the render thread.
+*/
+void QMapboxGL::setFramebufferObject(quint32 fbo, const QSize& size)
+{
+ d_ptr->setFramebufferObject(fbo, size);
+}
+
+/*!
Informs the map that the network connection has been established, causing
all network requests that previously timed out to be retried immediately.
*/
void QMapboxGL::connectionEstablished()
{
- d_ptr->connectionEstablished();
+ mbgl::NetworkStatus::Reachable();
}
/*!
@@ -1471,6 +1595,16 @@ void QMapboxGL::connectionEstablished()
*/
/*!
+ \fn void QMapboxGL::staticRenderFinished(const QString &error)
+
+ This signal is emitted when a static map is fully drawn. Usually the next
+ step is to extract the map from a framebuffer into a container like a
+ QImage. \a error is set to a message when an error occurs.
+
+ \sa startStaticRender()
+*/
+
+/*!
\fn void QMapboxGL::mapChanged(QMapboxGL::MapChange change)
This signal is emitted when the state of the map has changed. This signal
@@ -1487,172 +1621,140 @@ void QMapboxGL::connectionEstablished()
\a copyrightsHtml is a string with a HTML snippet.
*/
-class QMapboxGLRendererFrontend;
-
-QMapboxGLPrivate::QMapboxGLPrivate(QMapboxGL *q, const QMapboxGLSettings &settings, const QSize &size_, qreal pixelRatio)
+QMapboxGLPrivate::QMapboxGLPrivate(QMapboxGL *q, const QMapboxGLSettings &settings, const QSize &size, qreal pixelRatio_)
: QObject(q)
- , size(size_)
- , q_ptr(q)
- , fileSourceObj(sharedDefaultFileSource(
+ , m_fileSourceObj(sharedDefaultFileSource(
settings.cacheDatabasePath().toStdString(),
settings.assetPath().toStdString(),
settings.cacheDatabaseMaximumSize()))
- , threadPool(mbgl::sharedThreadPool())
-{
- // Setup and connect the renderer frontend
- frontend = std::make_unique<QMapboxGLRendererFrontend>(
- std::make_unique<mbgl::Renderer>(*this, pixelRatio, *fileSourceObj, *threadPool,
- static_cast<mbgl::GLContextMode>(settings.contextMode())),
- *this);
- connect(frontend.get(), SIGNAL(updated()), this, SLOT(invalidate()));
+ , m_threadPool(mbgl::sharedThreadPool())
+ , m_mode(settings.contextMode())
+ , m_pixelRatio(pixelRatio_)
+{
+ // Setup the FileSource
+ m_fileSourceObj->setAccessToken(settings.accessToken().toStdString());
+ m_fileSourceObj->setAPIBaseURL(settings.apiBaseUrl().toStdString());
+
+ if (settings.resourceTransform()) {
+ m_resourceTransform = std::make_unique<mbgl::Actor<mbgl::ResourceTransform>>(*mbgl::Scheduler::GetCurrent(),
+ [callback = settings.resourceTransform()] (mbgl::Resource::Kind, const std::string &&url_) -> std::string {
+ return callback(std::move(url_));
+ });
+ m_fileSourceObj->setResourceTransform(m_resourceTransform->self());
+ }
- mapObj = std::make_unique<mbgl::Map>(
- *frontend,
- *this, sanitizedSize(size),
- pixelRatio, *fileSourceObj, *threadPool,
- mbgl::MapMode::Continuous,
- static_cast<mbgl::ConstrainMode>(settings.constrainMode()),
- static_cast<mbgl::ViewportMode>(settings.viewportMode()));
+ // Setup MapObserver
+ m_mapObserver = std::make_unique<QMapboxGLMapObserver>(this);
qRegisterMetaType<QMapboxGL::MapChange>("QMapboxGL::MapChange");
- fileSourceObj->setAccessToken(settings.accessToken().toStdString());
- fileSourceObj->setAPIBaseURL(settings.apiBaseUrl().toStdString());
+ connect(m_mapObserver.get(), SIGNAL(mapChanged(QMapboxGL::MapChange)), q, SIGNAL(mapChanged(QMapboxGL::MapChange)));
+ connect(m_mapObserver.get(), SIGNAL(copyrightsChanged(QString)), q, SIGNAL(copyrightsChanged(QString)));
- connect(this, SIGNAL(needsRendering()), q_ptr, SIGNAL(needsRendering()), Qt::QueuedConnection);
- connect(this, SIGNAL(mapChanged(QMapboxGL::MapChange)), q_ptr, SIGNAL(mapChanged(QMapboxGL::MapChange)), Qt::QueuedConnection);
- connect(this, SIGNAL(copyrightsChanged(QString)), q_ptr, SIGNAL(copyrightsChanged(QString)), Qt::QueuedConnection);
+ // Setup the Map object
+ mapObj = std::make_unique<mbgl::Map>(
+ *this, // RendererFrontend
+ *m_mapObserver,
+ sanitizedSize(size),
+ m_pixelRatio, *m_fileSourceObj, *m_threadPool,
+ static_cast<mbgl::MapMode>(settings.mapMode()),
+ static_cast<mbgl::ConstrainMode>(settings.constrainMode()),
+ static_cast<mbgl::ViewportMode>(settings.viewportMode()));
+
+ // Needs to be Queued to give time to discard redundant draw calls via the `renderQueued` flag.
+ connect(this, SIGNAL(needsRendering()), q, SIGNAL(needsRendering()), Qt::QueuedConnection);
}
QMapboxGLPrivate::~QMapboxGLPrivate()
{
}
-mbgl::Size QMapboxGLPrivate::getFramebufferSize() const {
- return sanitizedSize(fbSize);
-}
+void QMapboxGLPrivate::update(std::shared_ptr<mbgl::UpdateParameters> parameters)
+{
+ std::lock_guard<std::recursive_mutex> lock(m_mapRendererMutex);
-void QMapboxGLPrivate::updateAssumedState() {
- assumeFramebufferBinding(fbObject);
- assumeViewport(0, 0, getFramebufferSize());
-}
+ if (!m_mapRenderer) {
+ return;
+ }
-void QMapboxGLPrivate::bind() {
- setFramebufferBinding(fbObject);
- setViewport(0, 0, getFramebufferSize());
-}
+ m_mapRenderer->updateParameters(std::move(parameters));
-void QMapboxGLPrivate::invalidate()
-{
- if (!dirty) {
- emit needsRendering();
- dirty = true;
- }
+ requestRendering();
}
-void QMapboxGLPrivate::render()
+void QMapboxGLPrivate::setObserver(mbgl::RendererObserver &observer)
{
- frontend->render();
-}
+ m_rendererObserver = std::make_shared<QMapboxGLRendererObserver>(
+ *mbgl::util::RunLoop::Get(), observer);
-void QMapboxGLPrivate::onCameraWillChange(mbgl::MapObserver::CameraChangeMode mode)
-{
- if (mode == mbgl::MapObserver::CameraChangeMode::Immediate) {
- emit mapChanged(QMapboxGL::MapChangeRegionWillChange);
- } else {
- emit mapChanged(QMapboxGL::MapChangeRegionWillChangeAnimated);
+ std::lock_guard<std::recursive_mutex> lock(m_mapRendererMutex);
+
+ if (m_mapRenderer) {
+ m_mapRenderer->setObserver(m_rendererObserver);
}
}
-void QMapboxGLPrivate::onCameraIsChanging()
+void QMapboxGLPrivate::createRenderer()
{
- emit mapChanged(QMapboxGL::MapChangeRegionIsChanging);
-}
+ std::lock_guard<std::recursive_mutex> lock(m_mapRendererMutex);
-void QMapboxGLPrivate::onCameraDidChange(mbgl::MapObserver::CameraChangeMode mode)
-{
- if (mode == mbgl::MapObserver::CameraChangeMode::Immediate) {
- emit mapChanged(QMapboxGL::MapChangeRegionDidChange);
- } else {
- emit mapChanged(QMapboxGL::MapChangeRegionDidChangeAnimated);
+ if (m_mapRenderer) {
+ return;
}
-}
-void QMapboxGLPrivate::onWillStartLoadingMap()
-{
- emit mapChanged(QMapboxGL::MapChangeWillStartLoadingMap);
-}
+ m_mapRenderer = std::make_unique<QMapboxGLMapRenderer>(
+ m_pixelRatio,
+ *m_fileSourceObj,
+ *m_threadPool,
+ m_mode
+ );
-void QMapboxGLPrivate::onDidFinishLoadingMap()
-{
- emit mapChanged(QMapboxGL::MapChangeDidFinishLoadingMap);
-}
+ connect(m_mapRenderer.get(), SIGNAL(needsRendering()), this, SLOT(requestRendering()));
-void QMapboxGLPrivate::onDidFailLoadingMap(std::exception_ptr)
-{
- emit mapChanged(QMapboxGL::MapChangeDidFailLoadingMap);
+ m_mapRenderer->setObserver(m_rendererObserver);
}
-void QMapboxGLPrivate::onWillStartRenderingFrame()
+void QMapboxGLPrivate::destroyRenderer()
{
- emit mapChanged(QMapboxGL::MapChangeWillStartRenderingFrame);
-}
+ std::lock_guard<std::recursive_mutex> lock(m_mapRendererMutex);
-void QMapboxGLPrivate::onDidFinishRenderingFrame(mbgl::MapObserver::RenderMode mode)
-{
- if (mode == mbgl::MapObserver::RenderMode::Partial) {
- emit mapChanged(QMapboxGL::MapChangeDidFinishRenderingFrame);
- } else {
- emit mapChanged(QMapboxGL::MapChangeDidFinishRenderingFrameFullyRendered);
- }
+ m_mapRenderer.reset();
}
-void QMapboxGLPrivate::onWillStartRenderingMap()
+void QMapboxGLPrivate::render()
{
- emit mapChanged(QMapboxGL::MapChangeWillStartLoadingMap);
-}
+ std::lock_guard<std::recursive_mutex> lock(m_mapRendererMutex);
-void QMapboxGLPrivate::onDidFinishRenderingMap(mbgl::MapObserver::RenderMode mode)
-{
- if (mode == mbgl::MapObserver::RenderMode::Partial) {
- emit mapChanged(QMapboxGL::MapChangeDidFinishRenderingMap);
- } else {
- emit mapChanged(QMapboxGL::MapChangeDidFinishRenderingMapFullyRendered);
+ if (!m_mapRenderer) {
+ createRenderer();
}
-}
-void QMapboxGLPrivate::onDidFinishLoadingStyle()
-{
- emit mapChanged(QMapboxGL::MapChangeDidFinishLoadingStyle);
+#if defined(__APPLE__) && QT_VERSION < 0x050000
+ // FIXME Qt 4.x provides an incomplete FBO at start.
+ // See https://bugreports.qt.io/browse/QTBUG-36802 for details.
+ if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
+ return;
+ }
+#endif
+
+ m_renderQueued.clear();
+ m_mapRenderer->render();
}
-void QMapboxGLPrivate::onSourceChanged(mbgl::style::Source&)
+void QMapboxGLPrivate::setFramebufferObject(quint32 fbo, const QSize& size)
{
- std::string attribution;
- for (const auto& source : mapObj->getStyle().getSources()) {
- // Avoid duplicates by using the most complete attribution HTML snippet.
- if (source->getAttribution() && (attribution.size() < source->getAttribution()->size()))
- attribution = *source->getAttribution();
+ std::lock_guard<std::recursive_mutex> lock(m_mapRendererMutex);
+
+ if (!m_mapRenderer) {
+ createRenderer();
}
- emit copyrightsChanged(QString::fromStdString(attribution));
- emit mapChanged(QMapboxGL::MapChangeSourceDidChange);
-}
-/*!
- Initializes an OpenGL extension function such as Vertex Array Objects (VAOs),
- required by Mapbox GL Native engine.
-*/
-mbgl::gl::ProcAddress QMapboxGLPrivate::initializeExtension(const char* name) {
-#if QT_VERSION >= 0x050000
- QOpenGLContext* thisContext = QOpenGLContext::currentContext();
- return thisContext->getProcAddress(name);
-#else
- const QGLContext* thisContext = QGLContext::currentContext();
- return reinterpret_cast<mbgl::gl::ProcAddress>(thisContext->getProcAddress(name));
-#endif
+ m_mapRenderer->updateFramebuffer(fbo, sanitizedSize(size));
}
-void QMapboxGLPrivate::connectionEstablished()
+void QMapboxGLPrivate::requestRendering()
{
- mbgl::NetworkStatus::Reachable();
+ if (!m_renderQueued.test_and_set()) {
+ emit needsRendering();
+ }
}
diff --git a/platform/qt/src/qmapboxgl_map_observer.cpp b/platform/qt/src/qmapboxgl_map_observer.cpp
new file mode 100644
index 0000000000..e180ed8fda
--- /dev/null
+++ b/platform/qt/src/qmapboxgl_map_observer.cpp
@@ -0,0 +1,95 @@
+#include "qmapboxgl_map_observer.hpp"
+
+#include "qmapboxgl_p.hpp"
+
+QMapboxGLMapObserver::QMapboxGLMapObserver(QMapboxGLPrivate *d)
+ : d_ptr(d)
+{
+}
+
+QMapboxGLMapObserver::~QMapboxGLMapObserver()
+{
+}
+
+void QMapboxGLMapObserver::onCameraWillChange(mbgl::MapObserver::CameraChangeMode mode)
+{
+ if (mode == mbgl::MapObserver::CameraChangeMode::Immediate) {
+ emit mapChanged(QMapboxGL::MapChangeRegionWillChange);
+ } else {
+ emit mapChanged(QMapboxGL::MapChangeRegionWillChangeAnimated);
+ }
+}
+
+void QMapboxGLMapObserver::onCameraIsChanging()
+{
+ emit mapChanged(QMapboxGL::MapChangeRegionIsChanging);
+}
+
+void QMapboxGLMapObserver::onCameraDidChange(mbgl::MapObserver::CameraChangeMode mode)
+{
+ if (mode == mbgl::MapObserver::CameraChangeMode::Immediate) {
+ emit mapChanged(QMapboxGL::MapChangeRegionDidChange);
+ } else {
+ emit mapChanged(QMapboxGL::MapChangeRegionDidChangeAnimated);
+ }
+}
+
+void QMapboxGLMapObserver::onWillStartLoadingMap()
+{
+ emit mapChanged(QMapboxGL::MapChangeWillStartLoadingMap);
+}
+
+void QMapboxGLMapObserver::onDidFinishLoadingMap()
+{
+ emit mapChanged(QMapboxGL::MapChangeDidFinishLoadingMap);
+}
+
+void QMapboxGLMapObserver::onDidFailLoadingMap(std::exception_ptr)
+{
+ emit mapChanged(QMapboxGL::MapChangeDidFailLoadingMap);
+}
+
+void QMapboxGLMapObserver::onWillStartRenderingFrame()
+{
+ emit mapChanged(QMapboxGL::MapChangeWillStartRenderingFrame);
+}
+
+void QMapboxGLMapObserver::onDidFinishRenderingFrame(mbgl::MapObserver::RenderMode mode)
+{
+ if (mode == mbgl::MapObserver::RenderMode::Partial) {
+ emit mapChanged(QMapboxGL::MapChangeDidFinishRenderingFrame);
+ } else {
+ emit mapChanged(QMapboxGL::MapChangeDidFinishRenderingFrameFullyRendered);
+ }
+}
+
+void QMapboxGLMapObserver::onWillStartRenderingMap()
+{
+ emit mapChanged(QMapboxGL::MapChangeWillStartLoadingMap);
+}
+
+void QMapboxGLMapObserver::onDidFinishRenderingMap(mbgl::MapObserver::RenderMode mode)
+{
+ if (mode == mbgl::MapObserver::RenderMode::Partial) {
+ emit mapChanged(QMapboxGL::MapChangeDidFinishRenderingMap);
+ } else {
+ emit mapChanged(QMapboxGL::MapChangeDidFinishRenderingMapFullyRendered);
+ }
+}
+
+void QMapboxGLMapObserver::onDidFinishLoadingStyle()
+{
+ emit mapChanged(QMapboxGL::MapChangeDidFinishLoadingStyle);
+}
+
+void QMapboxGLMapObserver::onSourceChanged(mbgl::style::Source&)
+{
+ std::string attribution;
+ for (const auto& source : d_ptr->mapObj->getStyle().getSources()) {
+ // Avoid duplicates by using the most complete attribution HTML snippet.
+ if (source->getAttribution() && (attribution.size() < source->getAttribution()->size()))
+ attribution = *source->getAttribution();
+ }
+ emit copyrightsChanged(QString::fromStdString(attribution));
+ emit mapChanged(QMapboxGL::MapChangeSourceDidChange);
+}
diff --git a/platform/qt/src/qmapboxgl_map_observer.hpp b/platform/qt/src/qmapboxgl_map_observer.hpp
new file mode 100644
index 0000000000..c9d0581a90
--- /dev/null
+++ b/platform/qt/src/qmapboxgl_map_observer.hpp
@@ -0,0 +1,45 @@
+#pragma once
+
+#include "qmapboxgl.hpp"
+
+#include <mbgl/map/map_observer.hpp>
+#include <mbgl/style/style.hpp>
+
+#include <QObject>
+
+#include <exception>
+#include <memory>
+
+class QMapboxGLPrivate;
+
+class QMapboxGLMapObserver : public QObject, public mbgl::MapObserver
+{
+ Q_OBJECT
+
+public:
+ explicit QMapboxGLMapObserver(QMapboxGLPrivate *);
+ virtual ~QMapboxGLMapObserver();
+
+ // mbgl::MapObserver implementation.
+ void onCameraWillChange(mbgl::MapObserver::CameraChangeMode) final;
+ void onCameraIsChanging() final;
+ void onCameraDidChange(mbgl::MapObserver::CameraChangeMode) final;
+ void onWillStartLoadingMap() final;
+ void onDidFinishLoadingMap() final;
+ void onDidFailLoadingMap(std::exception_ptr) final;
+ void onWillStartRenderingFrame() final;
+ void onDidFinishRenderingFrame(mbgl::MapObserver::RenderMode) final;
+ void onWillStartRenderingMap() final;
+ void onDidFinishRenderingMap(mbgl::MapObserver::RenderMode) final;
+ void onDidFinishLoadingStyle() final;
+ void onSourceChanged(mbgl::style::Source&) final;
+
+signals:
+ void mapChanged(QMapboxGL::MapChange);
+ void copyrightsChanged(const QString &copyrightsHtml);
+
+private:
+ Q_DISABLE_COPY(QMapboxGLMapObserver)
+
+ QMapboxGLPrivate *d_ptr;
+};
diff --git a/platform/qt/src/qmapboxgl_map_renderer.cpp b/platform/qt/src/qmapboxgl_map_renderer.cpp
new file mode 100644
index 0000000000..7a9d1f6f78
--- /dev/null
+++ b/platform/qt/src/qmapboxgl_map_renderer.cpp
@@ -0,0 +1,86 @@
+#include "qmapboxgl_map_renderer.hpp"
+
+#include <QtGlobal>
+
+QMapboxGLMapRenderer::QMapboxGLMapRenderer(qreal pixelRatio,
+ mbgl::DefaultFileSource &fs, mbgl::ThreadPool &tp, QMapboxGLSettings::GLContextMode mode)
+ : m_renderer(std::make_unique<mbgl::Renderer>(m_backend, pixelRatio, fs, tp, static_cast<mbgl::GLContextMode>(mode)))
+ , m_threadWithScheduler(Scheduler::GetCurrent() != nullptr)
+{
+}
+
+QMapboxGLMapRenderer::~QMapboxGLMapRenderer()
+{
+ MBGL_VERIFY_THREAD(tid);
+}
+
+void QMapboxGLMapRenderer::schedule(std::weak_ptr<mbgl::Mailbox> mailbox)
+{
+ std::lock_guard<std::mutex> lock(m_taskQueueMutex);
+ m_taskQueue.push(mailbox);
+
+ // Need to force the main thread to wake
+ // up this thread and process the events.
+ emit needsRendering();
+}
+
+void QMapboxGLMapRenderer::updateParameters(std::shared_ptr<mbgl::UpdateParameters> newParameters)
+{
+ std::lock_guard<std::mutex> lock(m_updateMutex);
+ m_updateParameters = std::move(newParameters);
+}
+
+void QMapboxGLMapRenderer::updateFramebuffer(quint32 fbo, const mbgl::Size &size)
+{
+ MBGL_VERIFY_THREAD(tid);
+
+ m_backend.updateFramebuffer(fbo, size);
+}
+
+void QMapboxGLMapRenderer::render()
+{
+ MBGL_VERIFY_THREAD(tid);
+
+ std::shared_ptr<mbgl::UpdateParameters> params;
+ {
+ // Lock on the parameters
+ std::lock_guard<std::mutex> lock(m_updateMutex);
+
+ if (!m_updateParameters) {
+ return;
+ }
+
+ // Hold on to the update parameters during render
+ params = m_updateParameters;
+ }
+
+ // The OpenGL implementation automatically enables the OpenGL context for us.
+ mbgl::BackendScope scope(m_backend, mbgl::BackendScope::ScopeType::Implicit);
+
+ // If we don't have a Scheduler on this thread, which
+ // is usually the case for render threads, use this
+ // object as scheduler.
+ if (!m_threadWithScheduler) {
+ Scheduler::SetCurrent(this);
+ }
+
+ m_renderer->render(*params);
+
+ if (!m_threadWithScheduler) {
+ std::queue<std::weak_ptr<mbgl::Mailbox>> taskQueue;
+ {
+ std::unique_lock<std::mutex> lock(m_taskQueueMutex);
+ std::swap(taskQueue, m_taskQueue);
+ }
+
+ while (!taskQueue.empty()) {
+ mbgl::Mailbox::maybeReceive(taskQueue.front());
+ taskQueue.pop();
+ }
+ }
+}
+
+void QMapboxGLMapRenderer::setObserver(std::shared_ptr<mbgl::RendererObserver> observer)
+{
+ m_renderer->setObserver(observer.get());
+}
diff --git a/platform/qt/src/qmapboxgl_map_renderer.hpp b/platform/qt/src/qmapboxgl_map_renderer.hpp
new file mode 100644
index 0000000000..adba11de51
--- /dev/null
+++ b/platform/qt/src/qmapboxgl_map_renderer.hpp
@@ -0,0 +1,63 @@
+#pragma once
+
+#include "qmapboxgl.hpp"
+#include "qmapboxgl_renderer_backend.hpp"
+
+#include <mbgl/renderer/renderer.hpp>
+#include <mbgl/renderer/renderer_backend.hpp>
+#include <mbgl/renderer/renderer_observer.hpp>
+#include <mbgl/storage/default_file_source.hpp>
+#include <mbgl/util/shared_thread_pool.hpp>
+#include <mbgl/util/util.hpp>
+
+#include <QtGlobal>
+
+#include <memory>
+#include <mutex>
+#include <queue>
+
+namespace mbgl {
+class Renderer;
+class UpdateParameters;
+} // namespace mbgl
+
+class QMapboxGLRendererBackend;
+
+class QMapboxGLMapRenderer : public QObject, public mbgl::Scheduler
+{
+ Q_OBJECT
+
+public:
+ QMapboxGLMapRenderer(qreal pixelRatio, mbgl::DefaultFileSource &,
+ mbgl::ThreadPool &, QMapboxGLSettings::GLContextMode);
+ virtual ~QMapboxGLMapRenderer();
+
+ // mbgl::Scheduler implementation.
+ void schedule(std::weak_ptr<mbgl::Mailbox> scheduled) final;
+
+ void render();
+ void updateFramebuffer(quint32 fbo, const mbgl::Size &size);
+ void setObserver(std::shared_ptr<mbgl::RendererObserver>);
+
+ // Thread-safe, called by the Frontend
+ void updateParameters(std::shared_ptr<mbgl::UpdateParameters>);
+
+signals:
+ void needsRendering();
+
+private:
+ MBGL_STORE_THREAD(tid)
+
+ Q_DISABLE_COPY(QMapboxGLMapRenderer)
+
+ std::mutex m_updateMutex;
+ std::shared_ptr<mbgl::UpdateParameters> m_updateParameters;
+
+ QMapboxGLRendererBackend m_backend;
+ std::unique_ptr<mbgl::Renderer> m_renderer;
+
+ std::mutex m_taskQueueMutex;
+ std::queue<std::weak_ptr<mbgl::Mailbox>> m_taskQueue;
+
+ bool m_threadWithScheduler;
+};
diff --git a/platform/qt/src/qmapboxgl_p.hpp b/platform/qt/src/qmapboxgl_p.hpp
index eb86870c10..51c7cb8fc4 100644
--- a/platform/qt/src/qmapboxgl_p.hpp
+++ b/platform/qt/src/qmapboxgl_p.hpp
@@ -1,18 +1,24 @@
#pragma once
#include "qmapboxgl.hpp"
-#include "qmapboxgl_renderer_frontend_p.hpp"
+#include "qmapboxgl_map_observer.hpp"
+#include "qmapboxgl_map_renderer.hpp"
+#include <mbgl/actor/actor.hpp>
#include <mbgl/map/map.hpp>
-#include <mbgl/renderer/renderer_backend.hpp>
-#include <mbgl/util/default_thread_pool.hpp>
+#include <mbgl/renderer/renderer_frontend.hpp>
#include <mbgl/storage/default_file_source.hpp>
+#include <mbgl/storage/resource_transform.hpp>
+#include <mbgl/util/default_thread_pool.hpp>
#include <mbgl/util/geo.hpp>
#include <QObject>
#include <QSize>
-class QMapboxGLPrivate : public QObject, public mbgl::RendererBackend, public mbgl::MapObserver
+#include <atomic>
+#include <memory>
+
+class QMapboxGLPrivate : public QObject, public mbgl::RendererFrontend
{
Q_OBJECT
@@ -20,52 +26,40 @@ public:
explicit QMapboxGLPrivate(QMapboxGL *, const QMapboxGLSettings &, const QSize &size, qreal pixelRatio);
virtual ~QMapboxGLPrivate();
+ // mbgl::RendererFrontend implementation.
+ void reset() final {}
+ void setObserver(mbgl::RendererObserver &) final;
+ void update(std::shared_ptr<mbgl::UpdateParameters>) final;
- // mbgl::RendererBackend implementation.
- void bind() final;
- mbgl::Size getFramebufferSize() const final;
- void updateAssumedState() final;
- void activate() final {}
- void deactivate() final {}
-
- // mbgl::MapObserver implementation.
- void onCameraWillChange(mbgl::MapObserver::CameraChangeMode) final;
- void onCameraIsChanging() final;
- void onCameraDidChange(mbgl::MapObserver::CameraChangeMode) final;
- void onWillStartLoadingMap() final;
- void onDidFinishLoadingMap() final;
- void onDidFailLoadingMap(std::exception_ptr) final;
- void onWillStartRenderingFrame() final;
- void onDidFinishRenderingFrame(mbgl::MapObserver::RenderMode) final;
- void onWillStartRenderingMap() final;
- void onDidFinishRenderingMap(mbgl::MapObserver::RenderMode) final;
- void onDidFinishLoadingStyle() final;
- void onSourceChanged(mbgl::style::Source&) final;
+ // These need to be called on the same thread.
+ void createRenderer();
+ void destroyRenderer();
+ void render();
+ void setFramebufferObject(quint32 fbo, const QSize& size);
mbgl::EdgeInsets margins;
- QSize size { 0, 0 };
- QSize fbSize { 0, 0 };
- quint32 fbObject = 0;
-
- QMapboxGL *q_ptr { nullptr };
-
- std::shared_ptr<mbgl::DefaultFileSource> fileSourceObj;
- std::shared_ptr<mbgl::ThreadPool> threadPool;
- std::unique_ptr<QMapboxGLRendererFrontend> frontend;
std::unique_ptr<mbgl::Map> mapObj;
- bool dirty { false };
-
-private:
- mbgl::gl::ProcAddress initializeExtension(const char*) override;
-
public slots:
- void connectionEstablished();
- void invalidate();
- void render();
+ void requestRendering();
signals:
void needsRendering();
- void mapChanged(QMapboxGL::MapChange);
- void copyrightsChanged(const QString &copyrightsHtml);
+
+private:
+ Q_DISABLE_COPY(QMapboxGLPrivate)
+
+ std::recursive_mutex m_mapRendererMutex;
+ std::shared_ptr<mbgl::RendererObserver> m_rendererObserver;
+
+ std::unique_ptr<QMapboxGLMapObserver> m_mapObserver;
+ std::shared_ptr<mbgl::DefaultFileSource> m_fileSourceObj;
+ std::shared_ptr<mbgl::ThreadPool> m_threadPool;
+ std::unique_ptr<QMapboxGLMapRenderer> m_mapRenderer;
+ std::unique_ptr<mbgl::Actor<mbgl::ResourceTransform>> m_resourceTransform;
+
+ QMapboxGLSettings::GLContextMode m_mode;
+ qreal m_pixelRatio;
+
+ std::atomic_flag m_renderQueued = ATOMIC_FLAG_INIT;
};
diff --git a/platform/qt/src/qmapboxgl_renderer_backend.cpp b/platform/qt/src/qmapboxgl_renderer_backend.cpp
new file mode 100644
index 0000000000..917741f5ce
--- /dev/null
+++ b/platform/qt/src/qmapboxgl_renderer_backend.cpp
@@ -0,0 +1,49 @@
+#include "qmapboxgl_renderer_backend.hpp"
+
+#include <QtGlobal>
+
+#if QT_VERSION >= 0x050000
+#include <QOpenGLContext>
+#else
+#include <QGLContext>
+#endif
+
+void QMapboxGLRendererBackend::updateAssumedState()
+{
+ assumeFramebufferBinding(ImplicitFramebufferBinding);
+ assumeViewport(0, 0, m_size);
+}
+
+void QMapboxGLRendererBackend::bind()
+{
+ assert(mbgl::BackendScope::exists());
+
+ setFramebufferBinding(m_fbo);
+ setViewport(0, 0, m_size);
+}
+
+mbgl::Size QMapboxGLRendererBackend::getFramebufferSize() const
+{
+ return m_size;
+}
+
+void QMapboxGLRendererBackend::updateFramebuffer(quint32 fbo, const mbgl::Size &size)
+{
+ m_fbo = fbo;
+ m_size = size;
+}
+
+/*!
+ Initializes an OpenGL extension function such as Vertex Array Objects (VAOs),
+ required by Mapbox GL Native engine.
+*/
+mbgl::gl::ProcAddress QMapboxGLRendererBackend::getExtensionFunctionPointer(const char* name)
+{
+#if QT_VERSION >= 0x050000
+ QOpenGLContext* thisContext = QOpenGLContext::currentContext();
+ return thisContext->getProcAddress(name);
+#else
+ const QGLContext* thisContext = QGLContext::currentContext();
+ return reinterpret_cast<mbgl::gl::ProcAddress>(thisContext->getProcAddress(name));
+#endif
+}
diff --git a/platform/qt/src/qmapboxgl_renderer_backend.hpp b/platform/qt/src/qmapboxgl_renderer_backend.hpp
new file mode 100644
index 0000000000..de66b035fc
--- /dev/null
+++ b/platform/qt/src/qmapboxgl_renderer_backend.hpp
@@ -0,0 +1,34 @@
+#pragma once
+
+#include "qmapboxgl.hpp"
+
+#include <mbgl/renderer/renderer_backend.hpp>
+#include <mbgl/storage/default_file_source.hpp>
+#include <mbgl/util/shared_thread_pool.hpp>
+
+class QMapboxGLRendererBackend : public mbgl::RendererBackend
+{
+public:
+ QMapboxGLRendererBackend() = default;
+ virtual ~QMapboxGLRendererBackend() = default;
+
+ // mbgl::RendererBackend implementation
+ void updateAssumedState() final;
+ void bind() final;
+ mbgl::Size getFramebufferSize() const final;
+
+ void updateFramebuffer(quint32 fbo, const mbgl::Size &);
+
+protected:
+ mbgl::gl::ProcAddress getExtensionFunctionPointer(const char*) final;
+
+ // No-op, implicit mode.
+ void activate() final {}
+ void deactivate() final {}
+
+private:
+ quint32 m_fbo = 0;
+ mbgl::Size m_size = { 0, 0 };
+
+ Q_DISABLE_COPY(QMapboxGLRendererBackend)
+};
diff --git a/platform/qt/src/qmapboxgl_renderer_frontend_p.cpp b/platform/qt/src/qmapboxgl_renderer_frontend_p.cpp
deleted file mode 100644
index ea60851eb4..0000000000
--- a/platform/qt/src/qmapboxgl_renderer_frontend_p.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-#include "qmapboxgl_renderer_frontend_p.hpp"
-
-#include <mbgl/renderer/backend_scope.hpp>
-#include <mbgl/renderer/renderer.hpp>
-
-QMapboxGLRendererFrontend::QMapboxGLRendererFrontend(std::unique_ptr<mbgl::Renderer> renderer_, mbgl::RendererBackend& backend_)
- : renderer(std::move(renderer_))
- , backend(backend_) {
-}
-
-QMapboxGLRendererFrontend::~QMapboxGLRendererFrontend() = default;
-
-void QMapboxGLRendererFrontend::reset() {
- if (renderer) {
- renderer.reset();
- }
-}
-
-void QMapboxGLRendererFrontend::update(std::shared_ptr<mbgl::UpdateParameters> updateParameters_) {
- updateParameters = updateParameters_;
- emit updated();
-}
-
-void QMapboxGLRendererFrontend::setObserver(mbgl::RendererObserver& observer_) {
- if (!renderer) return;
-
- renderer->setObserver(&observer_);
-}
-
-void QMapboxGLRendererFrontend::render() {
- if (!renderer || !updateParameters) return;
-
- // The OpenGL implementation automatically enables the OpenGL context for us.
- mbgl::BackendScope scope { backend, mbgl::BackendScope::ScopeType::Implicit };
-
- renderer->render(*updateParameters);
-}
diff --git a/platform/qt/src/qmapboxgl_renderer_frontend_p.hpp b/platform/qt/src/qmapboxgl_renderer_frontend_p.hpp
deleted file mode 100644
index c5e2bacc34..0000000000
--- a/platform/qt/src/qmapboxgl_renderer_frontend_p.hpp
+++ /dev/null
@@ -1,35 +0,0 @@
-#pragma once
-
-#include <mbgl/renderer/renderer_backend.hpp>
-#include <mbgl/renderer/renderer_frontend.hpp>
-
-#include <QObject>
-
-namespace mbgl {
- class Renderer;
-} // namespace mbgl
-
-class QMapboxGLRendererFrontend : public QObject, public mbgl::RendererFrontend
-{
- Q_OBJECT
-
-public:
- explicit QMapboxGLRendererFrontend(std::unique_ptr<mbgl::Renderer>, mbgl::RendererBackend&);
- ~QMapboxGLRendererFrontend() override;
-
- void reset() override;
- void setObserver(mbgl::RendererObserver&) override;
-
- void update(std::shared_ptr<mbgl::UpdateParameters>) override;
-
-public slots:
- void render();
-
-signals:
- void updated();
-
-private:
- std::unique_ptr<mbgl::Renderer> renderer;
- mbgl::RendererBackend& backend;
- std::shared_ptr<mbgl::UpdateParameters> updateParameters;
-};
diff --git a/platform/qt/src/qmapboxgl_renderer_observer.hpp b/platform/qt/src/qmapboxgl_renderer_observer.hpp
new file mode 100644
index 0000000000..ee340113ff
--- /dev/null
+++ b/platform/qt/src/qmapboxgl_renderer_observer.hpp
@@ -0,0 +1,51 @@
+#pragma once
+
+#include <mbgl/actor/actor.hpp>
+#include <mbgl/actor/actor_ref.hpp>
+#include <mbgl/actor/mailbox.hpp>
+#include <mbgl/renderer/renderer_observer.hpp>
+#include <mbgl/util/run_loop.hpp>
+
+#include <memory>
+
+// Forwards RendererObserver signals to the given
+// Delegate RendererObserver on the given RunLoop
+class QMapboxGLRendererObserver : public mbgl::RendererObserver {
+public:
+ QMapboxGLRendererObserver(mbgl::util::RunLoop& mapRunLoop, mbgl::RendererObserver& delegate_)
+ : mailbox(std::make_shared<mbgl::Mailbox>(mapRunLoop))
+ , delegate(delegate_, mailbox) {
+ }
+
+ ~QMapboxGLRendererObserver() {
+ mailbox->close();
+ }
+
+ void onInvalidate() final {
+ delegate.invoke(&mbgl::RendererObserver::onInvalidate);
+ }
+
+ void onResourceError(std::exception_ptr err) final {
+ delegate.invoke(&mbgl::RendererObserver::onResourceError, err);
+ }
+
+ void onWillStartRenderingMap() final {
+ delegate.invoke(&mbgl::RendererObserver::onWillStartRenderingMap);
+ }
+
+ void onWillStartRenderingFrame() final {
+ delegate.invoke(&mbgl::RendererObserver::onWillStartRenderingFrame);
+ }
+
+ void onDidFinishRenderingFrame(RenderMode mode, bool repaintNeeded) final {
+ delegate.invoke(&mbgl::RendererObserver::onDidFinishRenderingFrame, mode, repaintNeeded);
+ }
+
+ void onDidFinishRenderingMap() final {
+ delegate.invoke(&mbgl::RendererObserver::onDidFinishRenderingMap);
+ }
+
+private:
+ std::shared_ptr<mbgl::Mailbox> mailbox;
+ mbgl::ActorRef<mbgl::RendererObserver> delegate;
+};
diff --git a/platform/qt/src/qt_conversion.hpp b/platform/qt/src/qt_conversion.hpp
index 40d7e5b928..19b0cb54fc 100644
--- a/platform/qt/src/qt_conversion.hpp
+++ b/platform/qt/src/qt_conversion.hpp
@@ -1,120 +1,145 @@
#pragma once
#include <mbgl/style/conversion.hpp>
-#include <mbgl/util/feature.hpp>
+#include <mbgl/style/conversion/geojson.hpp>
#include <mbgl/util/optional.hpp>
-#include <QMapbox>
-
-#include <QColor>
#include <QVariant>
+#include <QColor>
+#include <QMapbox>
+#include "qt_geojson.hpp"
namespace mbgl {
namespace style {
namespace conversion {
-inline bool isUndefined(const QVariant& value) {
- return value.isNull() || !value.isValid();
-}
+template <>
+class ConversionTraits<QVariant> {
+public:
+ static bool isUndefined(const QVariant& value) {
+ return value.isNull() || !value.isValid();
+ }
-inline bool isArray(const QVariant& value) {
- return value.canConvert(QVariant::List);
-}
+ static bool isArray(const QVariant& value) {
+ return value.canConvert(QVariant::List);
+ }
-inline std::size_t arrayLength(const QVariant& value) {
- return value.toList().size();
-}
+ static std::size_t arrayLength(const QVariant& value) {
+ return value.toList().size();
+ }
-inline QVariant arrayMember(const QVariant& value, std::size_t i) {
- return value.toList()[i];
-}
+ static QVariant arrayMember(const QVariant& value, std::size_t i) {
+ return value.toList()[i];
+ }
-inline bool isObject(const QVariant& value) {
- return value.canConvert(QVariant::Map)
- || value.type() == QVariant::ByteArray
-#if QT_VERSION >= 0x050000
- || QString(value.typeName()) == QStringLiteral("QMapbox::Feature");
-#else
- || QString(value.typeName()) == QString("QMapbox::Feature");
-#endif
-}
+ static bool isObject(const QVariant& value) {
+ return value.canConvert(QVariant::Map)
+ || value.type() == QVariant::ByteArray
+ #if QT_VERSION >= 0x050000
+ || QString(value.typeName()) == QStringLiteral("QMapbox::Feature");
+ #else
+ || QString(value.typeName()) == QString("QMapbox::Feature");
+ #endif
+ }
-inline optional<QVariant> objectMember(const QVariant& value, const char* key) {
- auto map = value.toMap();
- auto iter = map.constFind(key);
+ static optional<QVariant> objectMember(const QVariant& value, const char* key) {
+ auto map = value.toMap();
+ auto iter = map.constFind(key);
- if (iter != map.constEnd()) {
- return iter.value();
- } else {
- return {};
+ if (iter != map.constEnd()) {
+ return iter.value();
+ } else {
+ return {};
+ }
}
-}
-using EachMemberFn = std::function<optional<Error>(const std::string&, const QVariant&)>;
+ template <class Fn>
+ static optional<Error> eachMember(const QVariant& value, Fn&& fn) {
+ auto map = value.toMap();
+ auto iter = map.constBegin();
-optional<Error> eachMember(const QVariant& value, EachMemberFn&& fn) {
- auto map = value.toMap();
- auto iter = map.constBegin();
+ while (iter != map.constEnd()) {
+ optional<Error> result = fn(iter.key().toStdString(), QVariant(iter.value()));
+ if (result) {
+ return result;
+ }
- while (iter != map.constEnd()) {
- optional<Error> result = fn(iter.key().toStdString(), iter.value());
- if (result) {
- return result;
+ ++iter;
}
- ++iter;
+ return {};
}
- return {};
-}
+ static optional<bool> toBool(const QVariant& value) {
+ if (value.type() == QVariant::Bool) {
+ return value.toBool();
+ } else {
+ return {};
+ }
+ }
-inline optional<bool> toBool(const QVariant& value) {
- if (value.type() == QVariant::Bool) {
- return value.toBool();
- } else {
- return {};
+ static optional<float> toNumber(const QVariant& value) {
+ if (value.type() == QVariant::Int || value.type() == QVariant::Double) {
+ return value.toFloat();
+ } else {
+ return {};
+ }
}
-}
-inline optional<float> toNumber(const QVariant& value) {
- if (value.type() == QVariant::Int || value.type() == QVariant::Double) {
- return value.toFloat();
- } else {
- return {};
+ static optional<double> toDouble(const QVariant& value) {
+ if (value.type() == QVariant::Int || value.type() == QVariant::Double) {
+ return value.toDouble();
+ } else {
+ return {};
+ }
}
-}
-inline optional<double> toDouble(const QVariant& value) {
- if (value.type() == QVariant::Int || value.type() == QVariant::Double) {
- return value.toDouble();
- } else {
- return {};
+
+ static optional<std::string> toString(const QVariant& value) {
+ if (value.type() == QVariant::String) {
+ return value.toString().toStdString();
+ } else if (value.type() == QVariant::Color) {
+ return value.value<QColor>().name().toStdString();
+ } else {
+ return {};
+ }
}
-}
-inline optional<std::string> toString(const QVariant& value) {
- if (value.type() == QVariant::String) {
- return value.toString().toStdString();
- } else if (value.type() == QVariant::Color) {
- return value.value<QColor>().name().toStdString();
- } else {
- return {};
+ static optional<Value> toValue(const QVariant& value) {
+ if (value.type() == QVariant::Bool) {
+ return { value.toBool() };
+ } else if (value.type() == QVariant::String) {
+ return { value.toString().toStdString() };
+ } else if (value.type() == QVariant::Color) {
+ return { value.value<QColor>().name().toStdString() };
+ } else if (value.type() == QVariant::Int) {
+ return { int64_t(value.toInt()) };
+ } else if (value.canConvert(QVariant::Double)) {
+ return { value.toDouble() };
+ } else {
+ return {};
+ }
}
-}
-inline optional<Value> toValue(const QVariant& value) {
- if (value.type() == QVariant::Bool) {
- return { value.toBool() };
- } else if (value.type() == QVariant::String) {
- return { value.toString().toStdString() };
- } else if (value.type() == QVariant::Color) {
- return { value.value<QColor>().name().toStdString() };
- } else if (value.type() == QVariant::Int) {
- return { int64_t(value.toInt()) };
- } else if (value.canConvert(QVariant::Double)) {
- return { value.toDouble() };
- } else {
- return {};
+ static optional<GeoJSON> toGeoJSON(const QVariant& value, Error& error) {
+ #if QT_VERSION >= 0x050000
+ if (value.typeName() == QStringLiteral("QMapbox::Feature")) {
+ #else
+ if (value.typeName() == QString("QMapbox::Feature")) {
+ #endif
+ return GeoJSON { asMapboxGLFeature(value.value<QMapbox::Feature>()) };
+ } else if (value.type() != QVariant::ByteArray) {
+ error = { "JSON data must be in QByteArray" };
+ return {};
+ }
+
+ QByteArray data = value.toByteArray();
+ return parseGeoJSON(std::string(data.constData(), data.size()), error);
}
+};
+
+template <class T, class...Args>
+optional<T> convert(const QVariant& value, Error& error, Args&&...args) {
+ return convert<T>(Convertible(value), error, std::forward<Args>(args)...);
}
} // namespace conversion
diff --git a/platform/qt/src/qt_geojson.cpp b/platform/qt/src/qt_geojson.cpp
new file mode 100644
index 0000000000..80377de64d
--- /dev/null
+++ b/platform/qt/src/qt_geojson.cpp
@@ -0,0 +1,166 @@
+#include "qt_geojson.hpp"
+#include <mapbox/geojson.hpp>
+#include <mbgl/util/geometry.hpp>
+#include <mbgl/util/feature.hpp>
+
+namespace QMapbox {
+
+mbgl::Point<double> asMapboxGLPoint(const QMapbox::Coordinate &coordinate) {
+ return mbgl::Point<double> { coordinate.second, coordinate.first };
+}
+
+mbgl::MultiPoint<double> asMapboxGLMultiPoint(const QMapbox::Coordinates &multiPoint) {
+ mbgl::MultiPoint<double> mbglMultiPoint;
+ mbglMultiPoint.reserve(multiPoint.size());
+ for (const auto &point: multiPoint) {
+ mbglMultiPoint.emplace_back(asMapboxGLPoint(point));
+ }
+ return mbglMultiPoint;
+};
+
+mbgl::LineString<double> asMapboxGLLineString(const QMapbox::Coordinates &lineString) {
+ mbgl::LineString<double> mbglLineString;
+ mbglLineString.reserve(lineString.size());
+ for (const auto &coordinate : lineString) {
+ mbglLineString.emplace_back(asMapboxGLPoint(coordinate));
+ }
+ return mbglLineString;
+};
+
+mbgl::MultiLineString<double> asMapboxGLMultiLineString(const QMapbox::CoordinatesCollection &multiLineString) {
+ mbgl::MultiLineString<double> mbglMultiLineString;
+ mbglMultiLineString.reserve(multiLineString.size());
+ for (const auto &lineString : multiLineString) {
+ mbglMultiLineString.emplace_back(std::forward<mbgl::LineString<double>>(asMapboxGLLineString(lineString)));
+ }
+ return mbglMultiLineString;
+};
+
+mbgl::Polygon<double> asMapboxGLPolygon(const QMapbox::CoordinatesCollection &polygon) {
+ mbgl::Polygon<double> mbglPolygon;
+ mbglPolygon.reserve(polygon.size());
+ for (const auto &linearRing : polygon) {
+ mbgl::LinearRing<double> mbglLinearRing;
+ mbglLinearRing.reserve(linearRing.size());
+ for (const auto &coordinate: linearRing) {
+ mbglLinearRing.emplace_back(asMapboxGLPoint(coordinate));
+ }
+ mbglPolygon.emplace_back(std::move(mbglLinearRing));
+ }
+ return mbglPolygon;
+};
+
+mbgl::MultiPolygon<double> asMapboxGLMultiPolygon(const QMapbox::CoordinatesCollections &multiPolygon) {
+ mbgl::MultiPolygon<double> mbglMultiPolygon;
+ mbglMultiPolygon.reserve(multiPolygon.size());
+ for (const auto &polygon : multiPolygon) {
+ mbglMultiPolygon.emplace_back(std::forward<mbgl::Polygon<double>>(asMapboxGLPolygon(polygon)));
+ }
+ return mbglMultiPolygon;
+};
+
+mbgl::Value asMapboxGLPropertyValue(const QVariant &value) {
+ auto valueList = [](const QVariantList &list) {
+ std::vector<mbgl::Value> mbglList;
+ mbglList.reserve(list.size());
+ for (const auto& listValue : list) {
+ mbglList.emplace_back(asMapboxGLPropertyValue(listValue));
+ }
+ return mbglList;
+ };
+
+ auto valueMap = [](const QVariantMap &map) {
+ std::unordered_map<std::string, mbgl::Value> mbglMap;
+ mbglMap.reserve(map.size());
+ auto it = map.constBegin();
+ while (it != map.constEnd()) {
+ mbglMap.emplace(std::make_pair(it.key().toStdString(), asMapboxGLPropertyValue(it.value())));
+ ++it;
+ }
+ return mbglMap;
+ };
+
+ switch (value.type()) {
+#if QT_VERSION >= 0x050000
+ case QMetaType::UnknownType:
+#else
+ case QVariant::Invalid:
+#endif
+ return mbgl::NullValue {};
+ case QMetaType::Bool:
+ return { value.toBool() };
+ case QMetaType::ULongLong:
+ return { uint64_t(value.toULongLong()) };
+ case QMetaType::LongLong:
+ return { int64_t(value.toLongLong()) };
+ case QMetaType::Double:
+ return { value.toDouble() };
+ case QMetaType::QString:
+ return { value.toString().toStdString() };
+ case QMetaType::QVariantList:
+ return valueList(value.toList());
+ case QMetaType::QVariantMap:
+ return valueMap(value.toMap());
+ default:
+ qWarning() << "Unsupported feature property value:" << value;
+ return {};
+ }
+}
+
+mbgl::FeatureIdentifier asMapboxGLFeatureIdentifier(const QVariant &id) {
+ switch (id.type()) {
+#if QT_VERSION >= 0x050000
+ case QMetaType::UnknownType:
+#else
+ case QVariant::Invalid:
+#endif
+ return {};
+ case QMetaType::ULongLong:
+ return { uint64_t(id.toULongLong()) };
+ case QMetaType::LongLong:
+ return { int64_t(id.toLongLong()) };
+ case QMetaType::Double:
+ return { id.toDouble() };
+ case QMetaType::QString:
+ return { id.toString().toStdString() };
+ default:
+ qWarning() << "Unsupported feature identifier:" << id;
+ return {};
+ }
+}
+
+mbgl::Feature asMapboxGLFeature(const QMapbox::Feature &feature) {
+ mbgl::PropertyMap properties;
+ properties.reserve(feature.properties.size());
+ auto it = feature.properties.constBegin();
+ while (it != feature.properties.constEnd()) {
+ properties.emplace(std::make_pair(it.key().toStdString(), asMapboxGLPropertyValue(it.value())));
+ }
+
+ mbgl::FeatureIdentifier id = asMapboxGLFeatureIdentifier(feature.id);
+
+ if (feature.type == QMapbox::Feature::PointType) {
+ const QMapbox::Coordinates &points = feature.geometry.first().first();
+ if (points.size() == 1) {
+ return { asMapboxGLPoint(points.first()), std::move(properties), std::move(id) };
+ } else {
+ return { asMapboxGLMultiPoint(points), std::move(properties), std::move(id) };
+ }
+ } else if (feature.type == QMapbox::Feature::LineStringType) {
+ const QMapbox::CoordinatesCollection &lineStrings = feature.geometry.first();
+ if (lineStrings.size() == 1) {
+ return { asMapboxGLLineString(lineStrings.first()), std::move(properties), std::move(id) };
+ } else {
+ return { asMapboxGLMultiLineString(lineStrings), std::move(properties), std::move(id) };
+ }
+ } else { // PolygonType
+ const QMapbox::CoordinatesCollections &polygons = feature.geometry;
+ if (polygons.size() == 1) {
+ return { asMapboxGLPolygon(polygons.first()), std::move(properties), std::move(id) };
+ } else {
+ return { asMapboxGLMultiPolygon(polygons), std::move(properties), std::move(id) };
+ }
+ }
+};
+
+} // namespace QMapbox
diff --git a/platform/qt/src/qt_geojson.hpp b/platform/qt/src/qt_geojson.hpp
index a6958b7edc..a9c10272ab 100644
--- a/platform/qt/src/qt_geojson.hpp
+++ b/platform/qt/src/qt_geojson.hpp
@@ -1,7 +1,8 @@
#pragma once
#include <mapbox/geojson.hpp>
-#include <mbgl/style/conversion/geojson.hpp>
+#include <mbgl/util/geometry.hpp>
+#include <mbgl/util/feature.hpp>
#include <QMapbox>
@@ -13,187 +14,14 @@
namespace QMapbox {
-mbgl::Point<double> asMapboxGLPoint(const QMapbox::Coordinate &coordinate) {
- return mbgl::Point<double> { coordinate.second, coordinate.first };
-}
-
-mbgl::MultiPoint<double> asMapboxGLMultiPoint(const QMapbox::Coordinates &multiPoint) {
- mbgl::MultiPoint<double> mbglMultiPoint;
- mbglMultiPoint.reserve(multiPoint.size());
- for (const auto &point: multiPoint) {
- mbglMultiPoint.emplace_back(asMapboxGLPoint(point));
- }
- return mbglMultiPoint;
-};
-
-mbgl::LineString<double> asMapboxGLLineString(const QMapbox::Coordinates &lineString) {
- mbgl::LineString<double> mbglLineString;
- mbglLineString.reserve(lineString.size());
- for (const auto &coordinate : lineString) {
- mbglLineString.emplace_back(asMapboxGLPoint(coordinate));
- }
- return mbglLineString;
-};
-
-mbgl::MultiLineString<double> asMapboxGLMultiLineString(const QMapbox::CoordinatesCollection &multiLineString) {
- mbgl::MultiLineString<double> mbglMultiLineString;
- mbglMultiLineString.reserve(multiLineString.size());
- for (const auto &lineString : multiLineString) {
- mbglMultiLineString.emplace_back(std::forward<mbgl::LineString<double>>(asMapboxGLLineString(lineString)));
- }
- return mbglMultiLineString;
-};
-
-mbgl::Polygon<double> asMapboxGLPolygon(const QMapbox::CoordinatesCollection &polygon) {
- mbgl::Polygon<double> mbglPolygon;
- mbglPolygon.reserve(polygon.size());
- for (const auto &linearRing : polygon) {
- mbgl::LinearRing<double> mbglLinearRing;
- mbglLinearRing.reserve(linearRing.size());
- for (const auto &coordinate: linearRing) {
- mbglLinearRing.emplace_back(asMapboxGLPoint(coordinate));
- }
- mbglPolygon.emplace_back(std::move(mbglLinearRing));
- }
- return mbglPolygon;
-};
-
-mbgl::MultiPolygon<double> asMapboxGLMultiPolygon(const QMapbox::CoordinatesCollections &multiPolygon) {
- mbgl::MultiPolygon<double> mbglMultiPolygon;
- mbglMultiPolygon.reserve(multiPolygon.size());
- for (const auto &polygon : multiPolygon) {
- mbglMultiPolygon.emplace_back(std::forward<mbgl::Polygon<double>>(asMapboxGLPolygon(polygon)));
- }
- return mbglMultiPolygon;
-};
-
-mbgl::Value asMapboxGLPropertyValue(const QVariant &value) {
- auto valueList = [](const QVariantList &list) {
- std::vector<mbgl::Value> mbglList;
- mbglList.reserve(list.size());
- for (const auto& listValue : list) {
- mbglList.emplace_back(asMapboxGLPropertyValue(listValue));
- }
- return mbglList;
- };
-
- auto valueMap = [](const QVariantMap &map) {
- std::unordered_map<std::string, mbgl::Value> mbglMap;
- mbglMap.reserve(map.size());
- auto it = map.constBegin();
- while (it != map.constEnd()) {
- mbglMap.emplace(std::make_pair(it.key().toStdString(), asMapboxGLPropertyValue(it.value())));
- ++it;
- }
- return mbglMap;
- };
-
- switch (value.type()) {
-#if QT_VERSION >= 0x050000
- case QMetaType::UnknownType:
-#else
- case QVariant::Invalid:
-#endif
- return mbgl::NullValue {};
- case QMetaType::Bool:
- return { value.toBool() };
- case QMetaType::ULongLong:
- return { uint64_t(value.toULongLong()) };
- case QMetaType::LongLong:
- return { int64_t(value.toLongLong()) };
- case QMetaType::Double:
- return { value.toDouble() };
- case QMetaType::QString:
- return { value.toString().toStdString() };
- case QMetaType::QVariantList:
- return valueList(value.toList());
- case QMetaType::QVariantMap:
- return valueMap(value.toMap());
- default:
- qWarning() << "Unsupported feature property value:" << value;
- return {};
- }
-}
-
-mbgl::FeatureIdentifier asMapboxGLFeatureIdentifier(const QVariant &id) {
- switch (id.type()) {
-#if QT_VERSION >= 0x050000
- case QMetaType::UnknownType:
-#else
- case QVariant::Invalid:
-#endif
- return {};
- case QMetaType::ULongLong:
- return { uint64_t(id.toULongLong()) };
- case QMetaType::LongLong:
- return { int64_t(id.toLongLong()) };
- case QMetaType::Double:
- return { id.toDouble() };
- case QMetaType::QString:
- return { id.toString().toStdString() };
- default:
- qWarning() << "Unsupported feature identifier:" << id;
- return {};
- }
-}
-
-mbgl::Feature asMapboxGLFeature(const QMapbox::Feature &feature) {
- mbgl::PropertyMap properties;
- properties.reserve(feature.properties.size());
- auto it = feature.properties.constBegin();
- while (it != feature.properties.constEnd()) {
- properties.emplace(std::make_pair(it.key().toStdString(), asMapboxGLPropertyValue(it.value())));
- }
-
- mbgl::FeatureIdentifier id = asMapboxGLFeatureIdentifier(feature.id);
-
- if (feature.type == QMapbox::Feature::PointType) {
- const QMapbox::Coordinates &points = feature.geometry.first().first();
- if (points.size() == 1) {
- return { asMapboxGLPoint(points.first()), std::move(properties), std::move(id) };
- } else {
- return { asMapboxGLMultiPoint(points), std::move(properties), std::move(id) };
- }
- } else if (feature.type == QMapbox::Feature::LineStringType) {
- const QMapbox::CoordinatesCollection &lineStrings = feature.geometry.first();
- if (lineStrings.size() == 1) {
- return { asMapboxGLLineString(lineStrings.first()), std::move(properties), std::move(id) };
- } else {
- return { asMapboxGLMultiLineString(lineStrings), std::move(properties), std::move(id) };
- }
- } else { // PolygonType
- const QMapbox::CoordinatesCollections &polygons = feature.geometry;
- if (polygons.size() == 1) {
- return { asMapboxGLPolygon(polygons.first()), std::move(properties), std::move(id) };
- } else {
- return { asMapboxGLMultiPolygon(polygons), std::move(properties), std::move(id) };
- }
- }
-};
+mbgl::Point<double> asMapboxGLPoint(const QMapbox::Coordinate &coordinate);
+mbgl::MultiPoint<double> asMapboxGLMultiPoint(const QMapbox::Coordinates &multiPoint);
+mbgl::LineString<double> asMapboxGLLineString(const QMapbox::Coordinates &lineString);
+mbgl::MultiLineString<double> asMapboxGLMultiLineString(const QMapbox::CoordinatesCollection &multiLineString);
+mbgl::Polygon<double> asMapboxGLPolygon(const QMapbox::CoordinatesCollection &polygon);
+mbgl::MultiPolygon<double> asMapboxGLMultiPolygon(const QMapbox::CoordinatesCollections &multiPolygon);
+mbgl::Value asMapboxGLPropertyValue(const QVariant &value);
+mbgl::FeatureIdentifier asMapboxGLFeatureIdentifier(const QVariant &id);
+mbgl::Feature asMapboxGLFeature(const QMapbox::Feature &feature);
} // namespace QMapbox
-
-namespace mbgl {
-namespace style {
-namespace conversion {
-
-template <>
-optional<GeoJSON> Converter<GeoJSON>::operator()(const QVariant& value, Error& error) const {
-#if QT_VERSION >= 0x050000
- if (value.typeName() == QStringLiteral("QMapbox::Feature")) {
-#else
- if (value.typeName() == QString("QMapbox::Feature")) {
-#endif
- return GeoJSON { asMapboxGLFeature(value.value<QMapbox::Feature>()) };
- } else if (value.type() != QVariant::ByteArray) {
- error = { "JSON data must be in QByteArray" };
- return {};
- }
-
- QByteArray data = value.toByteArray();
- return convert<GeoJSON>(std::string(data.constData(), data.size()), error);
-}
-
-} // namespace conversion
-} // namespace style
-} // namespace mbgl
diff --git a/platform/qt/src/qt_image.cpp b/platform/qt/src/qt_image.cpp
index 403ca9cbd3..a5c92514c1 100644
--- a/platform/qt/src/qt_image.cpp
+++ b/platform/qt/src/qt_image.cpp
@@ -54,8 +54,13 @@ PremultipliedImage decodeImage(const std::string& string) {
throw std::runtime_error("Unsupported image type");
}
+#if QT_VERSION >= 0x051000
+ auto img = std::make_unique<uint8_t[]>(image.sizeInBytes());
+ memcpy(img.get(), image.constBits(), image.sizeInBytes());
+#else
auto img = std::make_unique<uint8_t[]>(image.byteCount());
memcpy(img.get(), image.constBits(), image.byteCount());
+#endif
return { { static_cast<uint32_t>(image.width()), static_cast<uint32_t>(image.height()) },
std::move(img) };
diff --git a/platform/qt/src/qt_logging.cpp b/platform/qt/src/qt_logging.cpp
new file mode 100644
index 0000000000..acbe9562d0
--- /dev/null
+++ b/platform/qt/src/qt_logging.cpp
@@ -0,0 +1,12 @@
+#include <mbgl/util/logging.hpp>
+#include <mbgl/util/enum.hpp>
+
+#include <QDebug>
+
+namespace mbgl {
+
+void Log::platformRecord(EventSeverity severity, const std::string &msg) {
+ qWarning() << "[" << Enum<EventSeverity>::toString(severity) << "] " << QString::fromStdString(msg);
+}
+
+} // namespace mbgl
diff --git a/platform/qt/src/run_loop.cpp b/platform/qt/src/run_loop.cpp
index af0c50ebb9..c25243c8e7 100644
--- a/platform/qt/src/run_loop.cpp
+++ b/platform/qt/src/run_loop.cpp
@@ -52,11 +52,8 @@ LOOP_HANDLE RunLoop::getLoopHandle() {
return nullptr;
}
-void RunLoop::push(std::shared_ptr<WorkTask> task) {
- withMutex([&] {
- queue.push(std::move(task));
- impl->async->send();
- });
+void RunLoop::wake() {
+ impl->async->send();
}
void RunLoop::run() {
diff --git a/platform/qt/src/sqlite3.cpp b/platform/qt/src/sqlite3.cpp
index 7d47ae552b..80efd6a326 100644
--- a/platform/qt/src/sqlite3.cpp
+++ b/platform/qt/src/sqlite3.cpp
@@ -6,6 +6,7 @@
#include <QStringList>
#include <QThread>
#include <QVariant>
+#include <QAtomicInt>
#include <cassert>
#include <cstring>
@@ -60,20 +61,26 @@ void checkDatabaseOpenError(const QSqlDatabase &db) {
}
}
+namespace {
+ QString incrementCounter() {
+ static QAtomicInt count = 0;
+ return QString::number(count.fetchAndAddAcquire(1));
+ }
+}
+
class DatabaseImpl {
public:
- DatabaseImpl(const char* filename, int flags) {
- static uint64_t count = 0;
- const QString connectionName = QString::number(uint64_t(QThread::currentThread())) + QString::number(count++);
-
+ DatabaseImpl(const char* filename, int flags)
+ : connectionName(QString::number(uint64_t(QThread::currentThread())) + incrementCounter())
+ {
if (!QSqlDatabase::drivers().contains("QSQLITE")) {
throw Exception { Exception::Code::CANTOPEN, "SQLite driver not found." };
}
assert(!QSqlDatabase::contains(connectionName));
- db.reset(new QSqlDatabase(QSqlDatabase::addDatabase("QSQLITE", connectionName)));
+ auto db = QSqlDatabase::addDatabase("QSQLITE", connectionName);
- QString connectOptions = db->connectOptions();
+ QString connectOptions = db.connectOptions();
if (flags & OpenFlag::ReadOnly) {
if (!connectOptions.isEmpty()) connectOptions.append(';');
connectOptions.append("QSQLITE_OPEN_READONLY");
@@ -83,26 +90,26 @@ public:
connectOptions.append("QSQLITE_ENABLE_SHARED_CACHE");
}
- db->setConnectOptions(connectOptions);
- db->setDatabaseName(QString(filename));
+ db.setConnectOptions(connectOptions);
+ db.setDatabaseName(QString(filename));
- if (!db->open()) {
- checkDatabaseOpenError(*db);
+ if (!db.open()) {
+ checkDatabaseOpenError(db);
}
}
~DatabaseImpl() {
- db->close();
- checkDatabaseError(*db);
+ auto db = QSqlDatabase::database(connectionName);
+ db.close();
+ checkDatabaseError(db);
}
- QScopedPointer<QSqlDatabase> db;
+ QString connectionName;
};
class StatementImpl {
public:
StatementImpl(const QString& sql, const QSqlDatabase& db) : query(db) {
- query.setForwardOnly(true);
if (!query.prepare(sql)) {
checkQueryError(query);
}
@@ -147,17 +154,18 @@ void Database::setBusyTimeout(std::chrono::milliseconds timeout) {
// internally to int, so we need to make sure the limits apply.
std::string timeoutStr = mbgl::util::toString(timeout.count() & INT_MAX);
- QString connectOptions = impl->db->connectOptions();
+ auto db = QSqlDatabase::database(impl->connectionName);
+ QString connectOptions = db.connectOptions();
if (connectOptions.isEmpty()) {
if (!connectOptions.isEmpty()) connectOptions.append(';');
connectOptions.append("QSQLITE_BUSY_TIMEOUT=").append(QString::fromStdString(timeoutStr));
}
- if (impl->db->isOpen()) {
- impl->db->close();
+ if (db.isOpen()) {
+ db.close();
}
- impl->db->setConnectOptions(connectOptions);
- if (!impl->db->open()) {
- checkDatabaseOpenError(*impl->db);
+ db.setConnectOptions(connectOptions);
+ if (!db.open()) {
+ checkDatabaseOpenError(db);
}
}
@@ -169,9 +177,9 @@ void Database::exec(const std::string &sql) {
if (!statement.endsWith(';')) {
statement.append(';');
}
- QSqlQuery query(*impl->db);
- query.setForwardOnly(true);
+ QSqlQuery query(QSqlDatabase::database(impl->connectionName));
query.prepare(statement);
+
if (!query.exec()) {
checkQueryError(query);
}
@@ -183,7 +191,7 @@ Statement Database::prepare(const char *query) {
}
Statement::Statement(Database *db, const char *sql)
- : impl(std::make_unique<StatementImpl>(QString(sql), *db->impl->db)) {
+ : impl(std::make_unique<StatementImpl>(QString(sql), QSqlDatabase::database(db->impl->connectionName))) {
assert(impl);
}
@@ -269,9 +277,13 @@ void Statement::bind(int offset, const char* value, std::size_t length, bool ret
throw std::range_error("value too long");
}
+ // Qt SQLite driver treats QByteArray as blob: we need to explicitly
+ // declare the variant type as string.
+ QVariant text(QVariant::Type::String);
+ text.setValue(retain ? QByteArray(value, length) : QByteArray::fromRawData(value, length));
+
// Field numbering starts at 0.
- impl->query.bindValue(offset - 1, retain ? QByteArray(value, length) :
- QByteArray::fromRawData(value, length), QSql::In);
+ impl->query.bindValue(offset - 1, std::move(text), QSql::In);
checkQueryError(impl->query);
}
@@ -281,7 +293,12 @@ void Statement::bind(int offset, const std::string& value, bool retain) {
}
void Statement::bindBlob(int offset, const void* value_, std::size_t length, bool retain) {
+ assert(impl);
const char* value = reinterpret_cast<const char*>(value_);
+ if (length > std::numeric_limits<int>::max()) {
+ // Kept for consistence with the default implementation.
+ throw std::range_error("value too long");
+ }
// Field numbering starts at 0.
impl->query.bindValue(offset - 1, retain ? QByteArray(value, length) :
@@ -296,20 +313,20 @@ void Statement::bindBlob(int offset, const std::vector<uint8_t>& value, bool ret
bool Statement::run() {
assert(impl);
- if (impl->query.isValid()) {
- return impl->query.next();
- }
- assert(!impl->query.isActive());
- impl->query.setForwardOnly(true);
- if (!impl->query.exec()) {
- checkQueryError(impl->query);
+ if (!impl->query.isValid()) {
+ if (impl->query.exec()) {
+ impl->lastInsertRowId = impl->query.lastInsertId().value<int64_t>();
+ impl->changes = impl->query.numRowsAffected();
+ } else {
+ checkQueryError(impl->query);
+ }
}
- impl->lastInsertRowId = impl->query.lastInsertId().value<int64_t>();
- impl->changes = impl->query.numRowsAffected();
+ const bool hasNext = impl->query.next();
+ if (!hasNext) impl->query.finish();
- return impl->query.next();
+ return hasNext;
}
template bool Statement::get(int);
@@ -337,7 +354,7 @@ template <> mbgl::Timestamp Statement::get(int offset) {
QVariant value = impl->query.value(offset);
checkQueryError(impl->query);
return std::chrono::time_point_cast<std::chrono::seconds>(
- std::chrono::system_clock::from_time_t(value.value<std::time_t>()));
+ std::chrono::system_clock::from_time_t(value.value<::time_t>()));
}
template <> optional<int64_t> Statement::get(int offset) {
@@ -381,7 +398,7 @@ template <> optional<mbgl::Timestamp> Statement::get(int offset) {
if (value.isNull())
return {};
return { std::chrono::time_point_cast<mbgl::Seconds>(
- std::chrono::system_clock::from_time_t(value.value<std::time_t>())) };
+ std::chrono::system_clock::from_time_t(value.value<::time_t>())) };
}
void Statement::reset() {
diff --git a/platform/qt/test/qmapboxgl.test.cpp b/platform/qt/test/qmapboxgl.test.cpp
index c6ae3ed403..2a56b346a3 100644
--- a/platform/qt/test/qmapboxgl.test.cpp
+++ b/platform/qt/test/qmapboxgl.test.cpp
@@ -7,13 +7,16 @@
// We're using QGLFramebufferObject, which is only available in Qt 5 and up.
#if QT_VERSION >= 0x050000
+#include <QOpenGLContext>
+#include <QOpenGLFunctions>
+
QMapboxGLTest::QMapboxGLTest() : size(512, 512), fbo((assert(widget.context()->isValid()), widget.makeCurrent(), size)), map(nullptr, settings, size) {
connect(&map, SIGNAL(mapChanged(QMapboxGL::MapChange)),
this, SLOT(onMapChanged(QMapboxGL::MapChange)));
connect(&map, SIGNAL(needsRendering()),
this, SLOT(onNeedsRendering()));
- map.resize(fbo.size(), fbo.size());
- map.setFramebufferObject(fbo.handle());
+ map.resize(fbo.size());
+ map.setFramebufferObject(fbo.handle(), fbo.size());
map.setCoordinateZoom(QMapbox::Coordinate(60.170448, 24.942046), 14);
}
@@ -37,7 +40,7 @@ void QMapboxGLTest::onMapChanged(QMapboxGL::MapChange change) {
void QMapboxGLTest::onNeedsRendering() {
widget.makeCurrent();
fbo.bind();
- glViewport(0, 0, fbo.width(), fbo.height());
+ QOpenGLContext::currentContext()->functions()->glViewport(0, 0, fbo.width(), fbo.height());
map.render();
}
diff --git a/scripts/check-sha256.ps1 b/scripts/check-sha256.ps1
new file mode 100644
index 0000000000..e551605ad0
--- /dev/null
+++ b/scripts/check-sha256.ps1
@@ -0,0 +1,7 @@
+param([string]$file, [string]$hash);
+
+$hashFromFile = Get-FileHash -Path $file -Algorithm SHA256
+
+if ($hashFromFile.Hash -ne $hash) {
+ Throw 'Hash mismatch!'
+}
diff --git a/scripts/generate-benchmark-files.sh b/scripts/generate-benchmark-files.sh
deleted file mode 100755
index 0fb274cf31..0000000000
--- a/scripts/generate-benchmark-files.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-set -o pipefail
-
-echo "# Do not edit. Regenerate this with ./scripts/generate-benchmark-files.sh" > cmake/benchmark-files.cmake
-echo "" >> cmake/benchmark-files.cmake
-echo "set(MBGL_BENCHMARK_FILES" >> cmake/benchmark-files.cmake
-PREFIX=
-for FILE in $(git ls-files "benchmark/*.hpp" "benchmark/*.cpp" "benchmark/*.h" "benchmark/*.c" | sort) ; do
- CURRENT_PREFIX=$(dirname ${FILE#benchmark/})
- if [ "${PREFIX}" != "${CURRENT_PREFIX}" ]; then
- if [ ! -z "${PREFIX}" ]; then echo "" >> cmake/benchmark-files.cmake ; fi
- echo " # ${CURRENT_PREFIX}" >> cmake/benchmark-files.cmake
- PREFIX="${CURRENT_PREFIX}"
- fi
- echo " ${FILE}" >> cmake/benchmark-files.cmake
-done
-echo ")" >> cmake/benchmark-files.cmake
-git diff cmake/benchmark-files.cmake
diff --git a/scripts/generate-cmake-files.js b/scripts/generate-cmake-files.js
new file mode 100755
index 0000000000..4b6a8b8672
--- /dev/null
+++ b/scripts/generate-cmake-files.js
@@ -0,0 +1,32 @@
+#!/usr/bin/env node
+
+const child_process = require('child_process');
+const fs = require('fs');
+const ejs = require('ejs');
+
+require('./style-code');
+
+function generateCMakeListFile(name, regex, patterns) {
+ const files = child_process.execSync(`git ls-files ${patterns.map((p) => '"' + p + '"').join(' ')}`).toString().trim().split('\n');
+ var groups = {};
+ for (const file of files) {
+ const match = file.match(regex);
+ const group = match[1] || name;
+ if (!groups[group]) {
+ groups[group] = [];
+ }
+ groups[group].push(file);
+ }
+
+ const fileListCmake = ejs.compile(fs.readFileSync('cmake/files.cmake.ejs', 'utf8'), {strict: true});
+ writeIfModified(`cmake/${name}-files.cmake`, fileListCmake({ name: name, groups: groups }));
+}
+
+generateCMakeListFile('core', /^(?:src|include)\/(?:mbgl\/)?(.+)\/[^\/]+$/,
+ [ 'include/*.hpp', 'include/*.h', 'src/*.hpp', 'src/*.cpp', 'src/*.h', 'src/*.c' ]);
+
+generateCMakeListFile('benchmark', /^benchmark\/(?:(?:src|include)\/)?(?:mbgl\/)?(?:(.+)\/)?[^\/]+$/,
+ [ 'benchmark/*.hpp', 'benchmark/*.cpp', 'benchmark/*.h', 'benchmark/*.c' ]);
+
+generateCMakeListFile('test', /^test\/(?:(?:src|include)\/)?(?:mbgl\/)?(?:(.+)\/)?[^\/]+$/,
+ [ 'test/*.hpp', 'test/*.cpp', 'test/*.h', 'test/*.c' ]);
diff --git a/scripts/generate-core-files.sh b/scripts/generate-core-files.sh
deleted file mode 100755
index 27754306e1..0000000000
--- a/scripts/generate-core-files.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-set -o pipefail
-
-echo "# Do not edit. Regenerate this with ./scripts/generate-core-files.sh" > cmake/core-files.cmake
-echo "" >> cmake/core-files.cmake
-echo "set(MBGL_CORE_FILES" >> cmake/core-files.cmake
-PREFIX=
-for FILE in $(git ls-files "include/*.hpp" "include/*.h" "src/*.hpp" "src/*.cpp" "src/*.h" "src/*.c" | perl -p -e "s/^((src|include)\/(mbgl\/)?(.+)\/\w+\.\w+)$/\$4#\$1/g" | sort) ; do
- CURRENT_PREFIX="${FILE%#*}"
- if [ "${PREFIX}" != "${CURRENT_PREFIX}" ]; then
- if [ ! -z "${PREFIX}" ]; then echo "" >> cmake/core-files.cmake ; fi
- echo " # ${CURRENT_PREFIX}" >> cmake/core-files.cmake
- PREFIX="${CURRENT_PREFIX}"
- fi
- echo " ${FILE#*#}" >> cmake/core-files.cmake
-done
-echo ")" >> cmake/core-files.cmake
-git diff cmake/core-files.cmake
diff --git a/scripts/generate-shaders.js b/scripts/generate-shaders.js
index cf54b1b100..b1eeffb8a0 100755
--- a/scripts/generate-shaders.js
+++ b/scripts/generate-shaders.js
@@ -3,9 +3,10 @@
require('flow-remove-types/register');
const path = require('path');
-const shaders = require('../mapbox-gl-js/src/shaders');
const outputPath = 'src/mbgl/shaders';
+var shaders = require('../mapbox-gl-js/src/shaders');
+
require('./style-code');
writeIfModified(path.join(outputPath, 'preludes.hpp'), `// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED.
diff --git a/scripts/generate-style-code.js b/scripts/generate-style-code.js
index fe9a1a906b..6ddb787f19 100644..100755
--- a/scripts/generate-style-code.js
+++ b/scripts/generate-style-code.js
@@ -1,8 +1,9 @@
+#!/usr/bin/env node
'use strict';
const fs = require('fs');
const ejs = require('ejs');
-const spec = require('../mapbox-gl-js/src/style-spec/reference/v8');
+const spec = require('./style-spec');
const colorParser = require('csscolorparser');
require('./style-code');
@@ -96,6 +97,8 @@ global.paintPropertyType = function (property, type) {
global.propertyValueType = function (property) {
if (isDataDriven(property)) {
return `DataDrivenPropertyValue<${evaluatedType(property)}>`;
+ } else if (property.name === 'heatmap-color') {
+ return `HeatmapColorPropertyValue`;
} else {
return `PropertyValue<${evaluatedType(property)}>`;
}
@@ -111,6 +114,10 @@ global.defaultValue = function (property) {
return '{}';
}
+ if (property.name === 'heatmap-color') {
+ return '{}';
+ }
+
switch (property.type) {
case 'number':
return property.default;
@@ -186,8 +193,8 @@ for (const layer of layers) {
writeIfModified(`src/mbgl/style/layers/${layerFileName}_layer_properties.cpp`, propertiesCpp(layer));
}
-const propertySettersHpp = ejs.compile(fs.readFileSync('include/mbgl/style/conversion/make_property_setters.hpp.ejs', 'utf8'), {strict: true});
-writeIfModified('include/mbgl/style/conversion/make_property_setters.hpp', propertySettersHpp({layers: layers}));
+const propertySettersHpp = ejs.compile(fs.readFileSync('src/mbgl/style/conversion/make_property_setters.hpp.ejs', 'utf8'), {strict: true});
+writeIfModified('src/mbgl/style/conversion/make_property_setters.hpp', propertySettersHpp({layers: layers}));
// Light
const lightProperties = Object.keys(spec[`light`]).reduce((memo, name) => {
diff --git a/scripts/generate-test-files.sh b/scripts/generate-test-files.sh
deleted file mode 100755
index 5f507d3edf..0000000000
--- a/scripts/generate-test-files.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-set -o pipefail
-
-echo "# Do not edit. Regenerate this with ./scripts/generate-test-files.sh" > cmake/test-files.cmake
-echo "" >> cmake/test-files.cmake
-echo "set(MBGL_TEST_FILES" >> cmake/test-files.cmake
-PREFIX=
-for FILE in $(git ls-files "test/*.hpp" "test/*.cpp" "test/*.h" "test/*.c" | sort) ; do
- CURRENT_PREFIX=$(dirname ${FILE#test/})
- if [ "${PREFIX}" != "${CURRENT_PREFIX}" ]; then
- if [ ! -z "${PREFIX}" ]; then echo "" >> cmake/test-files.cmake ; fi
- echo " # ${CURRENT_PREFIX}" >> cmake/test-files.cmake
- PREFIX="${CURRENT_PREFIX}"
- fi
- echo " ${FILE}" >> cmake/test-files.cmake
-done
-echo ")" >> cmake/test-files.cmake
-git diff cmake/test-files.cmake
diff --git a/scripts/nitpick/generated-code.js b/scripts/nitpick/generated-code.js
new file mode 100755
index 0000000000..b8699eaa00
--- /dev/null
+++ b/scripts/nitpick/generated-code.js
@@ -0,0 +1,50 @@
+#!/usr/bin/env node
+const nitpick = require('.');
+const child_process = require('child_process');
+const path = require('path');
+const fs = require('fs');
+const os = require('os');
+
+function checkGeneratedFiles(name, scripts) {
+ var files = [];
+
+ scripts.forEach(function(script) {
+ child_process.execSync(script);
+ const list = path.join(path.dirname(script), path.basename(script, path.extname(script)) + '.list');
+ files.push(list);
+ files = files.concat(fs.readFileSync(list, 'utf8').split('\n'));
+ });
+
+ // List missing files
+ var missing = child_process.execFileSync('git', ['ls-files', '--others', '--exclude-standard', '--'].concat(files)).toString().trim();
+ if (!missing.length) {
+ nitpick.ok(`All generated ${name} files are checked in`);
+ } else {
+ nitpick.fail(`These generated ${name} files are not checked in:`, missing);
+ }
+
+ // Diff existing files
+ var diff = child_process.execFileSync('git', ['-c', 'color.ui=always', 'diff', 'HEAD', '--'].concat(files)).toString().trim();
+ if (!diff.length) {
+ nitpick.ok(`All generated ${name} files are up-to-date`);
+ } else {
+ nitpick.fail(`These generated ${name} files have modifications:`, diff);
+ }
+}
+
+const mode = (process.argv[2] || '').toLowerCase();
+if (!mode || mode == 'cmake') {
+ checkGeneratedFiles('CMake list', ['scripts/generate-cmake-files.js']);
+}
+if (!mode || mode == 'shader') {
+ checkGeneratedFiles('shader', ['scripts/generate-shaders.js']);
+}
+if (!mode || mode == 'style') {
+ checkGeneratedFiles('style', ['scripts/generate-style-code.js']);
+}
+if (!mode || mode == 'android') {
+ checkGeneratedFiles('Android', ['platform/android/scripts/generate-style-code.js']);
+}
+if ((!mode || mode == 'darwin') && os.platform() == 'darwin') {
+ checkGeneratedFiles('Darwin', ['platform/darwin/scripts/generate-style-code.js', 'platform/darwin/scripts/update-examples.js']);
+}
diff --git a/scripts/nitpick/index.js b/scripts/nitpick/index.js
new file mode 100644
index 0000000000..31316e7521
--- /dev/null
+++ b/scripts/nitpick/index.js
@@ -0,0 +1,28 @@
+var failed = false;
+
+process.on('exit', function() {
+ if (failed) {
+ process.exitCode = 1;
+ }
+});
+
+module.exports = function(status, str, desc) {
+ if (status) {
+ console.warn(`\x1b[1m\x1b[32m✔︎ ${str}\x1b[0m`);
+ } else {
+ console.warn(`\x1b[1m\x1b[31m✘ ${str}\x1b[0m`);
+ failed = true;
+ }
+
+ if (desc) {
+ console.warn(`${desc.split('\n').map((line) => '| ' + line).join('\n')}\n`);
+ }
+};
+
+module.exports.ok = function(str, desc) {
+ module.exports(true, str, desc);
+};
+
+module.exports.fail = function(str, desc) {
+ module.exports(false, str, desc);
+};
diff --git a/scripts/nitpick/submodule-pin.js b/scripts/nitpick/submodule-pin.js
new file mode 100755
index 0000000000..7ca129e096
--- /dev/null
+++ b/scripts/nitpick/submodule-pin.js
@@ -0,0 +1,13 @@
+#!/usr/bin/env node
+const nitpick = require('.');
+const child_process = require('child_process');
+
+// Make sure that the mapbox-gl-js submodule pin is up to date
+const head = child_process.execSync('git -C mapbox-gl-js rev-parse HEAD').toString().trim();
+const revs = child_process.execSync(`git -C mapbox-gl-js branch -a --contains ${head}`).toString().split('\n');
+
+if (revs.indexOf(' remotes/origin/master') >= 0) {
+ nitpick.ok(`mapbox-gl-js submodule pin is merged to master`);
+} else {
+ nitpick.fail(`mapbox-gl-js submodule is pinned to ${head}, which isn't merged to master`);
+}
diff --git a/scripts/style-code.js b/scripts/style-code.js
index 70914c5fb6..7a93b13a77 100644
--- a/scripts/style-code.js
+++ b/scripts/style-code.js
@@ -1,6 +1,7 @@
// Global functions //
const fs = require('fs');
+const path = require('path');
global.iff = function (condition, val) {
return condition() ? val : "";
@@ -26,15 +27,25 @@ global.unhyphenate = function (str) {
return str.replace(/-/g, " ");
};
+// Write out a list of files that this script is modifying so that we can check
+var files = [];
+process.on('exit', function() {
+ const list = path.join(path.dirname(process.argv[1]), path.basename(process.argv[1], '.js') + '.list');
+ fs.writeFileSync(list, files.join('\n'));
+});
+
global.writeIfModified = function(filename, newContent) {
+ files.push(filename);
try {
const oldContent = fs.readFileSync(filename, 'utf8');
if (oldContent == newContent) {
- console.warn(`* Skipping current file '${filename}'`);
+ console.warn(`* Skipping file '${filename}' because it is up-to-date`);
return;
}
} catch(err) {
}
- fs.writeFileSync(filename, newContent);
+ if (['0', 'false'].indexOf(process.env.DRY_RUN || '0') !== -1) {
+ fs.writeFileSync(filename, newContent);
+ }
console.warn(`* Updating outdated file '${filename}'`);
};
diff --git a/scripts/style-spec.js b/scripts/style-spec.js
new file mode 100644
index 0000000000..196adc0b32
--- /dev/null
+++ b/scripts/style-spec.js
@@ -0,0 +1,3 @@
+var spec = module.exports = require('../mapbox-gl-js/src/style-spec/reference/v8');
+
+// Make temporary modifications here when Native doesn't have all features that JS has.
diff --git a/scripts/update_ca_bundle.sh b/scripts/update_ca_bundle.sh
index 992bd7030f..3bd268bd0c 100755
--- a/scripts/update_ca_bundle.sh
+++ b/scripts/update_ca_bundle.sh
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
-cd common
+cd misc
curl https://raw.githubusercontent.com/curl/curl/master/lib/mk-ca-bundle.pl | perl
rm certdata.txt
diff --git a/src/mbgl/annotation/render_annotation_source.cpp b/src/mbgl/annotation/render_annotation_source.cpp
index 0aff64583d..a237100d13 100644
--- a/src/mbgl/annotation/render_annotation_source.cpp
+++ b/src/mbgl/annotation/render_annotation_source.cpp
@@ -64,16 +64,17 @@ std::unordered_map<std::string, std::vector<Feature>>
RenderAnnotationSource::queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
const std::vector<const RenderLayer*>& layers,
- const RenderedQueryOptions& options) const {
- return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options);
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) const {
+ return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, collisionIndex);
}
std::vector<Feature> RenderAnnotationSource::querySourceFeatures(const SourceQueryOptions&) const {
return {};
}
-void RenderAnnotationSource::onLowMemory() {
- tilePyramid.onLowMemory();
+void RenderAnnotationSource::reduceMemoryUse() {
+ tilePyramid.reduceMemoryUse();
}
void RenderAnnotationSource::dumpDebugLogs() const {
diff --git a/src/mbgl/annotation/render_annotation_source.hpp b/src/mbgl/annotation/render_annotation_source.hpp
index 9536b2e101..e812ec2883 100644
--- a/src/mbgl/annotation/render_annotation_source.hpp
+++ b/src/mbgl/annotation/render_annotation_source.hpp
@@ -27,12 +27,13 @@ public:
queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
const std::vector<const RenderLayer*>& layers,
- const RenderedQueryOptions& options) const final;
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) const final;
std::vector<Feature>
querySourceFeatures(const SourceQueryOptions&) const final;
- void onLowMemory() final;
+ void reduceMemoryUse() final;
void dumpDebugLogs() const final;
private:
@@ -43,7 +44,7 @@ private:
template <>
inline bool RenderSource::is<RenderAnnotationSource>() const {
- return baseImpl->type == SourceType::Annotations;
+ return baseImpl->type == style::SourceType::Annotations;
}
} // namespace mbgl
diff --git a/src/mbgl/geometry/dem_data.cpp b/src/mbgl/geometry/dem_data.cpp
new file mode 100644
index 0000000000..7fa98950ea
--- /dev/null
+++ b/src/mbgl/geometry/dem_data.cpp
@@ -0,0 +1,106 @@
+#include <mbgl/geometry/dem_data.hpp>
+#include <mbgl/math/clamp.hpp>
+
+namespace mbgl {
+
+DEMData::DEMData(const PremultipliedImage& _image, Tileset::DEMEncoding encoding):
+ dim(_image.size.height),
+ border(std::max<int32_t>(std::ceil(_image.size.height / 2), 1)),
+ stride(dim + 2 * border),
+ image({ static_cast<uint32_t>(stride), static_cast<uint32_t>(stride) }) {
+
+ if (_image.size.height != _image.size.width){
+ throw std::runtime_error("raster-dem tiles must be square.");
+ }
+
+ auto decodeMapbox = [] (const uint8_t r, const uint8_t g, const uint8_t b){
+ // https://www.mapbox.com/help/access-elevation-data/#mapbox-terrain-rgb
+ return (r * 256 * 256 + g * 256 + b)/10 - 10000;
+ };
+
+ auto decodeTerrarium = [] (const uint8_t r, const uint8_t g, const uint8_t b){
+ // https://aws.amazon.com/public-datasets/terrain/
+ return ((r * 256 + g + b / 256) - 32768);
+ };
+
+ auto decodeRGB = encoding == Tileset::DEMEncoding::Terrarium ? decodeTerrarium : decodeMapbox;
+
+ std::memset(image.data.get(), 0, image.bytes());
+
+ for (int32_t y = 0; y < dim; y++) {
+ for (int32_t x = 0; x < dim; x++) {
+ const int32_t i = y * dim + x;
+ const int32_t j = i * 4;
+ set(x, y, decodeRGB(_image.data[j], _image.data[j+1], _image.data[j+2]));
+ }
+ }
+
+ // in order to avoid flashing seams between tiles, here we are initially populating a 1px border of
+ // pixels around the image with the data of the nearest pixel from the image. this data is eventually
+ // replaced when the tile's neighboring tiles are loaded and the accurate data can be backfilled using
+ // DEMData#backfillBorder
+
+ for (int32_t x = 0; x < dim; x++) {
+ // left vertical border
+ set(-1, x, get(0, x));
+
+ // right vertical border
+ set(dim, x, get(dim - 1, x));
+
+ //left horizontal border
+ set(x, -1, get(x, 0));
+
+ // right horizontal border
+ set(x, dim, get(x, dim - 1));
+ }
+
+ // corners
+ set(-1, -1, get(0, 0));
+ set(dim, -1, get(dim - 1, 0));
+ set( -1, dim, get(0, dim - 1));
+ set(dim, dim, get(dim - 1, dim - 1));
+}
+
+// This function takes the DEMData from a neighboring tile and backfills the edge/corner
+// data in order to create a one pixel "buffer" of image data around the tile. This is
+// necessary because the hillshade formula calculates the dx/dz, dy/dz derivatives at each
+// pixel of the tile by querying the 8 surrounding pixels, and if we don't have the pixel
+// buffer we get seams at tile boundaries.
+void DEMData::backfillBorder(const DEMData& borderTileData, int8_t dx, int8_t dy) {
+ auto& o = borderTileData;
+
+ // Tiles from the same source should always be of the same dimensions.
+ assert(dim == o.dim);
+
+ // We determine the pixel range to backfill based which corner/edge `borderTileData`
+ // represents. For example, dx = -1, dy = -1 represents the upper left corner of the
+ // base tile, so we only need to backfill one pixel at coordinates (-1, -1) of the tile
+ // image.
+ int32_t _xMin = dx * dim;
+ int32_t _xMax = dx * dim + dim;
+ int32_t _yMin = dy * dim;
+ int32_t _yMax = dy * dim + dim;
+
+ if (dx == -1) _xMin = _xMax - 1;
+ else if (dx == 1) _xMax = _xMin + 1;
+
+ if (dy == -1) _yMin = _yMax - 1;
+ else if (dy == 1) _yMax = _yMin + 1;
+
+ int32_t xMin = util::clamp(_xMin, -border, dim + border);
+ int32_t xMax = util::clamp(_xMax, -border, dim + border);
+
+ int32_t yMin = util::clamp(_yMin, -border, dim + border);
+ int32_t yMax = util::clamp(_yMax, -border, dim + border);
+
+ int32_t ox = -dx * dim;
+ int32_t oy = -dy * dim;
+
+ for (int32_t y = yMin; y < yMax; y++) {
+ for (int32_t x = xMin; x < xMax; x++) {
+ set(x, y, o.get(x + ox, y + oy));
+ }
+ }
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/geometry/dem_data.hpp b/src/mbgl/geometry/dem_data.hpp
new file mode 100644
index 0000000000..817d3cc9c9
--- /dev/null
+++ b/src/mbgl/geometry/dem_data.hpp
@@ -0,0 +1,49 @@
+#pragma once
+
+#include <mbgl/math/clamp.hpp>
+#include <mbgl/util/image.hpp>
+#include <mbgl/util/tileset.hpp>
+
+#include <memory>
+#include <array>
+#include <cassert>
+#include <vector>
+
+namespace mbgl {
+
+class DEMData {
+public:
+ DEMData(const PremultipliedImage& image, Tileset::DEMEncoding encoding);
+ void backfillBorder(const DEMData& borderTileData, int8_t dx, int8_t dy);
+
+ void set(const int32_t x, const int32_t y, const int32_t value) {
+ reinterpret_cast<int32_t*>(image.data.get())[idx(x, y)] = value + 65536;
+ }
+
+ int32_t get(const int32_t x, const int32_t y) const {
+ return reinterpret_cast<const int32_t*>(image.data.get())[idx(x, y)] - 65536;
+ }
+
+ const PremultipliedImage* getImage() const {
+ return &image;
+ }
+
+ const int32_t dim;
+ const int32_t border;
+ const int32_t stride;
+
+
+ private:
+ PremultipliedImage image;
+
+ size_t idx(const int32_t x, const int32_t y) const {
+ assert(x >= -border);
+ assert(x < dim + border);
+ assert(y >= -border);
+ assert(y < dim + border);
+ return (y + border) * stride + (x + border);
+ }
+
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/geometry/feature_index.cpp b/src/mbgl/geometry/feature_index.cpp
index 1adb933e44..3b5e12b54a 100644
--- a/src/mbgl/geometry/feature_index.cpp
+++ b/src/mbgl/geometry/feature_index.cpp
@@ -2,7 +2,7 @@
#include <mbgl/renderer/render_layer.hpp>
#include <mbgl/renderer/query.hpp>
#include <mbgl/renderer/layers/render_symbol_layer.hpp>
-#include <mbgl/text/collision_tile.hpp>
+#include <mbgl/text/collision_index.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/math.hpp>
#include <mbgl/math/minmax.hpp>
@@ -18,7 +18,7 @@
namespace mbgl {
FeatureIndex::FeatureIndex()
- : grid(util::EXTENT, 16, 0) {
+ : grid(util::EXTENT, util::EXTENT, util::EXTENT / 16) { // 16x16 grid -> 32px cell
}
void FeatureIndex::insert(const GeometryCollection& geometries,
@@ -26,8 +26,9 @@ void FeatureIndex::insert(const GeometryCollection& geometries,
const std::string& sourceLayerName,
const std::string& bucketName) {
for (const auto& ring : geometries) {
- grid.insert(IndexedSubfeature { index, sourceLayerName, bucketName, sortIndex++ },
- mapbox::geometry::envelope(ring));
+ auto envelope = mapbox::geometry::envelope(ring);
+ grid.insert(IndexedSubfeature(index, sourceLayerName, bucketName, sortIndex++),
+ {convertPoint<float>(envelope.min), convertPoint<float>(envelope.max)});
}
}
@@ -47,9 +48,10 @@ void FeatureIndex::query(
const double scale,
const RenderedQueryOptions& queryOptions,
const GeometryTileData& geometryTileData,
- const CanonicalTileID& tileID,
+ const UnwrappedTileID& tileID,
+ const std::string& sourceID,
const std::vector<const RenderLayer*>& layers,
- const CollisionTile* collisionTile,
+ const CollisionIndex& collisionIndex,
const float additionalQueryRadius) const {
// Determine query radius
@@ -58,7 +60,8 @@ void FeatureIndex::query(
// Query the grid index
mapbox::geometry::box<int16_t> box = mapbox::geometry::envelope(queryGeometry);
- std::vector<IndexedSubfeature> features = grid.query({ box.min - additionalRadius, box.max + additionalRadius });
+ std::vector<IndexedSubfeature> features = grid.query({ convertPoint<float>(box.min - additionalRadius),
+ convertPoint<float>(box.max + additionalRadius) });
std::sort(features.begin(), features.end(), topDown);
@@ -69,18 +72,13 @@ void FeatureIndex::query(
if (indexedFeature.sortIndex == previousSortIndex) continue;
previousSortIndex = indexedFeature.sortIndex;
- addFeature(result, indexedFeature, queryGeometry, queryOptions, geometryTileData, tileID, layers, bearing, pixelsToTileUnits);
+ addFeature(result, indexedFeature, queryGeometry, queryOptions, geometryTileData, tileID.canonical, layers, bearing, pixelsToTileUnits);
}
- // Query symbol features, if they've been placed.
- if (!collisionTile) {
- return;
- }
-
- std::vector<IndexedSubfeature> symbolFeatures = collisionTile->queryRenderedSymbols(queryGeometry, scale);
+ std::vector<IndexedSubfeature> symbolFeatures = collisionIndex.queryRenderedSymbols(queryGeometry, tileID, sourceID);
std::sort(symbolFeatures.begin(), symbolFeatures.end(), topDownSymbols);
for (const auto& symbolFeature : symbolFeatures) {
- addFeature(result, symbolFeature, queryGeometry, queryOptions, geometryTileData, tileID, layers, bearing, pixelsToTileUnits);
+ addFeature(result, symbolFeature, queryGeometry, queryOptions, geometryTileData, tileID.canonical, layers, bearing, pixelsToTileUnits);
}
}
diff --git a/src/mbgl/geometry/feature_index.hpp b/src/mbgl/geometry/feature_index.hpp
index 2ae7da33df..e95bb94da6 100644
--- a/src/mbgl/geometry/feature_index.hpp
+++ b/src/mbgl/geometry/feature_index.hpp
@@ -2,6 +2,7 @@
#include <mbgl/style/types.hpp>
#include <mbgl/tile/geometry_tile_data.hpp>
+#include <mbgl/tile/tile_id.hpp>
#include <mbgl/util/grid_index.hpp>
#include <mbgl/util/feature.hpp>
@@ -14,16 +15,37 @@ namespace mbgl {
class RenderedQueryOptions;
class RenderLayer;
-class CollisionTile;
-class CanonicalTileID;
+class CollisionIndex;
class IndexedSubfeature {
public:
IndexedSubfeature() = delete;
- std::size_t index;
+ IndexedSubfeature(std::size_t index_, std::string sourceLayerName_, std::string bucketName_, size_t sortIndex_)
+ : index(index_)
+ , sourceLayerName(std::move(sourceLayerName_))
+ , bucketName(std::move(bucketName_))
+ , sortIndex(sortIndex_)
+ , tileID(0, 0, 0)
+ {}
+
+ IndexedSubfeature(std::size_t index_, std::string sourceLayerName_, std::string bucketName_, size_t sortIndex_,
+ std::string sourceID_, CanonicalTileID tileID_)
+ : index(index_)
+ , sourceLayerName(std::move(sourceLayerName_))
+ , bucketName(std::move(bucketName_))
+ , sortIndex(std::move(sortIndex_))
+ , sourceID(std::move(sourceID_))
+ , tileID(std::move(tileID_))
+ {}
+
+ size_t index;
std::string sourceLayerName;
std::string bucketName;
size_t sortIndex;
+
+ // Only used for symbol features
+ std::string sourceID;
+ CanonicalTileID tileID;
};
class FeatureIndex {
@@ -40,9 +62,10 @@ public:
const double scale,
const RenderedQueryOptions& options,
const GeometryTileData&,
- const CanonicalTileID&,
+ const UnwrappedTileID&,
+ const std::string&,
const std::vector<const RenderLayer*>&,
- const CollisionTile*,
+ const CollisionIndex&,
const float additionalQueryRadius) const;
static optional<GeometryCoordinates> translateQueryGeometry(
diff --git a/src/mbgl/gl/color_mode.hpp b/src/mbgl/gl/color_mode.hpp
index e73c8737eb..e394f43501 100644
--- a/src/mbgl/gl/color_mode.hpp
+++ b/src/mbgl/gl/color_mode.hpp
@@ -49,7 +49,7 @@ public:
struct Replace {
static constexpr BlendEquation equation = BlendEquation::Add;
static constexpr BlendFactor srcFactor = One;
- static constexpr BlendFactor dstFactor = One;
+ static constexpr BlendFactor dstFactor = Zero;
};
using Add = LinearBlend<BlendEquation::Add>;
@@ -85,6 +85,10 @@ public:
static ColorMode alphaBlended() {
return ColorMode { Add { One, OneMinusSrcAlpha }, {}, { true, true, true, true } };
}
+
+ static ColorMode additive() {
+ return ColorMode { Add { One, One }, {}, { true, true, true, true } };
+ }
};
constexpr bool operator!=(const ColorMode::Mask& a, const ColorMode::Mask& b) {
diff --git a/src/mbgl/gl/context.cpp b/src/mbgl/gl/context.cpp
index d1a37a861a..f40cfa1f2c 100644
--- a/src/mbgl/gl/context.cpp
+++ b/src/mbgl/gl/context.cpp
@@ -60,6 +60,16 @@ static_assert(std::is_same<std::underlying_type_t<TextureFormat>, GLenum>::value
static_assert(underlying_type(TextureFormat::RGBA) == GL_RGBA, "OpenGL type mismatch");
static_assert(underlying_type(TextureFormat::Alpha) == GL_ALPHA, "OpenGL type mismatch");
+static_assert(std::is_same<std::underlying_type_t<TextureType>, GLenum>::value, "OpenGL type mismatch");
+static_assert(underlying_type(TextureType::UnsignedByte) == GL_UNSIGNED_BYTE, "OpenGL type mismatch");
+
+#if MBGL_USE_GLES2 && GL_HALF_FLOAT_OES
+static_assert(underlying_type(TextureType::HalfFloat) == GL_HALF_FLOAT_OES, "OpenGL type mismatch");
+#endif
+#if !MBGL_USE_GLES2 && GL_HALF_FLOAT_ARB
+static_assert(underlying_type(TextureType::HalfFloat) == GL_HALF_FLOAT_ARB, "OpenGL type mismatch");
+#endif
+
static_assert(underlying_type(UniformDataType::Float) == GL_FLOAT, "OpenGL type mismatch");
static_assert(underlying_type(UniformDataType::FloatVec2) == GL_FLOAT_VEC2, "OpenGL type mismatch");
static_assert(underlying_type(UniformDataType::FloatVec3) == GL_FLOAT_VEC3, "OpenGL type mismatch");
@@ -116,6 +126,19 @@ void Context::initializeExtensions(const std::function<gl::ProcAddress(const cha
programBinary = std::make_unique<extension::ProgramBinary>(fn);
#endif
+#if MBGL_USE_GLES2
+ constexpr const char* halfFloatExtensionName = "OES_texture_half_float";
+ constexpr const char* halfFloatColorBufferExtensionName = "EXT_color_buffer_half_float";
+#else
+ constexpr const char* halfFloatExtensionName = "ARB_half_float_pixel";
+ constexpr const char* halfFloatColorBufferExtensionName = "ARB_color_buffer_float";
+#endif
+ if (strstr(extensions, halfFloatExtensionName) != nullptr &&
+ strstr(extensions, halfFloatColorBufferExtensionName) != nullptr) {
+
+ supportsHalfFloatTextures = true;
+ }
+
if (!supportsVertexArrays()) {
Log::Warning(Event::OpenGL, "Not using Vertex Array Objects");
}
@@ -226,16 +249,25 @@ void Context::updateVertexBuffer(UniqueBuffer& buffer, const void* data, std::si
MBGL_CHECK_ERROR(glBufferSubData(GL_ARRAY_BUFFER, 0, size, data));
}
-UniqueBuffer Context::createIndexBuffer(const void* data, std::size_t size) {
+UniqueBuffer Context::createIndexBuffer(const void* data, std::size_t size, const BufferUsage usage) {
BufferID id = 0;
MBGL_CHECK_ERROR(glGenBuffers(1, &id));
UniqueBuffer result { std::move(id), { this } };
bindVertexArray = 0;
globalVertexArrayState.indexBuffer = result;
- MBGL_CHECK_ERROR(glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, data, GL_STATIC_DRAW));
+ MBGL_CHECK_ERROR(glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, data, static_cast<GLenum>(usage)));
return result;
}
+void Context::updateIndexBuffer(UniqueBuffer& buffer, const void* data, std::size_t size) {
+ // Be sure to unbind any existing vertex array object before binding the index buffer
+ // so that we don't mess up another VAO
+ bindVertexArray = 0;
+ globalVertexArrayState.indexBuffer = buffer;
+ MBGL_CHECK_ERROR(glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, size, data));
+}
+
+
UniqueTexture Context::createTexture() {
if (pooledTextures.empty()) {
pooledTextures.resize(TextureMax);
@@ -482,10 +514,10 @@ Context::createFramebuffer(const Texture& color,
}
UniqueTexture
-Context::createTexture(const Size size, const void* data, TextureFormat format, TextureUnit unit) {
+Context::createTexture(const Size size, const void* data, TextureFormat format, TextureUnit unit, TextureType type) {
auto obj = createTexture();
pixelStoreUnpack = { 1 };
- updateTexture(obj, size, data, format, unit);
+ updateTexture(obj, size, data, format, unit, type);
// We are using clamp to edge here since OpenGL ES doesn't allow GL_REPEAT on NPOT textures.
// We use those when the pixelRatio isn't a power of two, e.g. on iPhone 6 Plus.
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
@@ -496,11 +528,11 @@ Context::createTexture(const Size size, const void* data, TextureFormat format,
}
void Context::updateTexture(
- TextureID id, const Size size, const void* data, TextureFormat format, TextureUnit unit) {
+ TextureID id, const Size size, const void* data, TextureFormat format, TextureUnit unit, TextureType type) {
activeTextureUnit = unit;
texture[unit] = id;
MBGL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, static_cast<GLenum>(format), size.width,
- size.height, 0, static_cast<GLenum>(format), GL_UNSIGNED_BYTE,
+ size.height, 0, static_cast<GLenum>(format), static_cast<GLenum>(type),
data));
}
@@ -600,19 +632,19 @@ void Context::clear(optional<mbgl::Color> color,
if (color) {
mask |= GL_COLOR_BUFFER_BIT;
clearColor = *color;
- colorMask = { true, true, true, true };
+ colorMask = value::ColorMask::Default;
}
if (depth) {
mask |= GL_DEPTH_BUFFER_BIT;
clearDepth = *depth;
- depthMask = true;
+ depthMask = value::DepthMask::Default;
}
if (stencil) {
mask |= GL_STENCIL_BUFFER_BIT;
clearStencil = *stencil;
- stencilMask = 0xFF;
+ stencilMask = value::StencilMask::Default;
}
MBGL_CHECK_ERROR(glClear(mask));
diff --git a/src/mbgl/gl/context.hpp b/src/mbgl/gl/context.hpp
index 528113cbba..67624288e2 100644
--- a/src/mbgl/gl/context.hpp
+++ b/src/mbgl/gl/context.hpp
@@ -61,7 +61,7 @@ public:
optional<std::pair<BinaryProgramFormat, std::string>> getBinaryProgram(ProgramID) const;
template <class Vertex, class DrawMode>
- VertexBuffer<Vertex, DrawMode> createVertexBuffer(VertexVector<Vertex, DrawMode>&& v, const BufferUsage usage=BufferUsage::StaticDraw) {
+ VertexBuffer<Vertex, DrawMode> createVertexBuffer(VertexVector<Vertex, DrawMode>&& v, const BufferUsage usage = BufferUsage::StaticDraw) {
return VertexBuffer<Vertex, DrawMode> {
v.vertexSize(),
createVertexBuffer(v.data(), v.byteSize(), usage)
@@ -75,11 +75,18 @@ public:
}
template <class DrawMode>
- IndexBuffer<DrawMode> createIndexBuffer(IndexVector<DrawMode>&& v) {
+ IndexBuffer<DrawMode> createIndexBuffer(IndexVector<DrawMode>&& v, const BufferUsage usage = BufferUsage::StaticDraw) {
return IndexBuffer<DrawMode> {
- createIndexBuffer(v.data(), v.byteSize())
+ v.indexSize(),
+ createIndexBuffer(v.data(), v.byteSize(), usage)
};
}
+
+ template <class DrawMode>
+ void updateIndexBuffer(IndexBuffer<DrawMode>& buffer, IndexVector<DrawMode>&& v) {
+ assert(v.indexSize() == buffer.indexCount);
+ updateIndexBuffer(buffer.buffer, v.data(), v.byteSize());
+ }
template <RenderbufferType type>
Renderbuffer<type> createRenderbuffer(const Size size) {
@@ -118,23 +125,29 @@ public:
// Create a texture from an image with data.
template <typename Image>
- Texture createTexture(const Image& image, TextureUnit unit = 0) {
+ Texture createTexture(const Image& image,
+ TextureUnit unit = 0,
+ TextureType type = TextureType::UnsignedByte) {
auto format = image.channels == 4 ? TextureFormat::RGBA : TextureFormat::Alpha;
- return { image.size, createTexture(image.size, image.data.get(), format, unit) };
+ return { image.size, createTexture(image.size, image.data.get(), format, unit, type) };
}
template <typename Image>
- void updateTexture(Texture& obj, const Image& image, TextureUnit unit = 0) {
+ void updateTexture(Texture& obj,
+ const Image& image,
+ TextureUnit unit = 0,
+ TextureType type = TextureType::UnsignedByte) {
auto format = image.channels == 4 ? TextureFormat::RGBA : TextureFormat::Alpha;
- updateTexture(obj.texture.get(), image.size, image.data.get(), format, unit);
+ updateTexture(obj.texture.get(), image.size, image.data.get(), format, unit, type);
obj.size = image.size;
}
// Creates an empty texture with the specified dimensions.
Texture createTexture(const Size size,
TextureFormat format = TextureFormat::RGBA,
- TextureUnit unit = 0) {
- return { size, createTexture(size, nullptr, format, unit) };
+ TextureUnit unit = 0,
+ TextureType type = TextureType::UnsignedByte) {
+ return { size, createTexture(size, nullptr, format, unit, type) };
}
void bindTexture(Texture&,
@@ -225,6 +238,8 @@ public:
State<value::PixelTransferStencil> pixelTransferStencil;
#endif // MBGL_USE_GLES2
+ bool supportsHalfFloatTextures = false;
+
private:
State<value::StencilFunc> stencilFunc;
State<value::StencilMask> stencilMask;
@@ -250,9 +265,10 @@ private:
UniqueBuffer createVertexBuffer(const void* data, std::size_t size, const BufferUsage usage);
void updateVertexBuffer(UniqueBuffer& buffer, const void* data, std::size_t size);
- UniqueBuffer createIndexBuffer(const void* data, std::size_t size);
- UniqueTexture createTexture(Size size, const void* data, TextureFormat, TextureUnit);
- void updateTexture(TextureID, Size size, const void* data, TextureFormat, TextureUnit);
+ UniqueBuffer createIndexBuffer(const void* data, std::size_t size, const BufferUsage usage);
+ void updateIndexBuffer(UniqueBuffer& buffer, const void* data, std::size_t size);
+ UniqueTexture createTexture(Size size, const void* data, TextureFormat, TextureUnit, TextureType);
+ void updateTexture(TextureID, Size size, const void* data, TextureFormat, TextureUnit, TextureType);
UniqueFramebuffer createFramebuffer();
UniqueRenderbuffer createRenderbuffer(RenderbufferType, Size size);
std::unique_ptr<uint8_t[]> readFramebuffer(Size, TextureFormat, bool flip);
@@ -281,8 +297,13 @@ private:
std::vector<RenderbufferID> abandonedRenderbuffers;
public:
- // For testing
+ // For testing and Windows because Qt + ANGLE
+ // crashes with VAO enabled.
+#if defined(_WINDOWS)
+ bool disableVAOExtension = true;
+#else
bool disableVAOExtension = false;
+#endif
};
} // namespace gl
diff --git a/src/mbgl/gl/gl.hpp b/src/mbgl/gl/gl.hpp
index 3e21731330..976b7d2f74 100644
--- a/src/mbgl/gl/gl.hpp
+++ b/src/mbgl/gl/gl.hpp
@@ -1,36 +1,10 @@
#pragma once
+#include <mbgl/gl/gl_impl.hpp>
+
#include <stdexcept>
#include <limits>
-#if __APPLE__
- #include "TargetConditionals.h"
- #if TARGET_OS_IPHONE
- #include <OpenGLES/ES2/gl.h>
- #include <OpenGLES/ES2/glext.h>
- #elif TARGET_IPHONE_SIMULATOR
- #include <OpenGLES/ES2/gl.h>
- #include <OpenGLES/ES2/glext.h>
- #elif TARGET_OS_MAC
- #include <OpenGL/OpenGL.h>
- #include <OpenGL/gl.h>
- #include <OpenGL/glext.h>
- #else
- #error Unsupported Apple platform
- #endif
-#elif __ANDROID__ || MBGL_USE_GLES2
- #define GL_GLEXT_PROTOTYPES
- #include <GLES2/gl2.h>
- #include <GLES2/gl2ext.h>
-#elif __QT__ && QT_VERSION >= 0x050000
- #define GL_GLEXT_PROTOTYPES
- #include <QtGui/qopengl.h>
-#else
- #define GL_GLEXT_PROTOTYPES
- #include <GL/gl.h>
- #include <GL/glext.h>
-#endif
-
namespace mbgl {
namespace gl {
diff --git a/src/mbgl/gl/index_buffer.hpp b/src/mbgl/gl/index_buffer.hpp
index 01b6396e00..87bfb6068f 100644
--- a/src/mbgl/gl/index_buffer.hpp
+++ b/src/mbgl/gl/index_buffer.hpp
@@ -35,6 +35,7 @@ private:
template <class DrawMode>
class IndexBuffer {
public:
+ std::size_t indexCount;
UniqueBuffer buffer;
};
diff --git a/src/mbgl/gl/types.hpp b/src/mbgl/gl/types.hpp
index da08195e58..376a784a0c 100644
--- a/src/mbgl/gl/types.hpp
+++ b/src/mbgl/gl/types.hpp
@@ -64,6 +64,15 @@ enum class TextureFormat : uint32_t {
#endif // MBGL_USE_GLES2
};
+enum class TextureType : uint32_t {
+ UnsignedByte = 0x1401,
+#if MBGL_USE_GLES2
+ HalfFloat = 0x8D61,
+#else
+ HalfFloat = 0x140B,
+#endif // MBGL_USE_GLES2
+};
+
enum class PrimitiveType {
Points = 0x0000,
Lines = 0x0001,
diff --git a/src/mbgl/layout/symbol_instance.cpp b/src/mbgl/layout/symbol_instance.cpp
index 02fb800df6..6e152349ca 100644
--- a/src/mbgl/layout/symbol_instance.cpp
+++ b/src/mbgl/layout/symbol_instance.cpp
@@ -11,7 +11,6 @@ SymbolInstance::SymbolInstance(Anchor& anchor_,
optional<PositionedIcon> shapedIcon,
const SymbolLayoutProperties::Evaluated& layout,
const float layoutTextSize,
- const bool addToBuffers,
const uint32_t index_,
const float textBoxScale,
const float textPadding,
@@ -19,11 +18,12 @@ SymbolInstance::SymbolInstance(Anchor& anchor_,
const std::array<float, 2> textOffset_,
const float iconBoxScale,
const float iconPadding,
- const SymbolPlacementType iconPlacement,
const std::array<float, 2> iconOffset_,
const GlyphPositionMap& positions,
const IndexedSubfeature& indexedFeature,
- const std::size_t featureIndex_) :
+ const std::size_t featureIndex_,
+ const std::u16string& key_,
+ const float overscaling) :
anchor(anchor_),
line(line_),
index(index_),
@@ -31,25 +31,22 @@ SymbolInstance::SymbolInstance(Anchor& anchor_,
hasIcon(shapedIcon),
// Create the collision features that will be used to check whether this symbol instance can be placed
- textCollisionFeature(line_, anchor, shapedTextOrientations.second ?: shapedTextOrientations.first, textBoxScale, textPadding, textPlacement, indexedFeature),
- iconCollisionFeature(line_, anchor, shapedIcon, iconBoxScale, iconPadding, iconPlacement, indexedFeature),
+ textCollisionFeature(line_, anchor, shapedTextOrientations.first, textBoxScale, textPadding, textPlacement, indexedFeature, overscaling),
+ iconCollisionFeature(line_, anchor, shapedIcon, iconBoxScale, iconPadding, indexedFeature),
featureIndex(featureIndex_),
textOffset(textOffset_),
- iconOffset(iconOffset_) {
+ iconOffset(iconOffset_),
+ key(key_) {
// Create the quads used for rendering the icon and glyphs.
- if (addToBuffers) {
- if (shapedIcon) {
- iconQuad = getIconQuad(*shapedIcon, layout, layoutTextSize, shapedTextOrientations.first);
- }
- if (shapedTextOrientations.first) {
- auto quads = getGlyphQuads(shapedTextOrientations.first, layout, textPlacement, positions);
- glyphQuads.insert(glyphQuads.end(), quads.begin(), quads.end());
- }
- if (shapedTextOrientations.second) {
- auto quads = getGlyphQuads(shapedTextOrientations.second, layout, textPlacement, positions);
- glyphQuads.insert(glyphQuads.end(), quads.begin(), quads.end());
- }
+ if (shapedIcon) {
+ iconQuad = getIconQuad(*shapedIcon, layout, layoutTextSize, shapedTextOrientations.first);
+ }
+ if (shapedTextOrientations.first) {
+ horizontalGlyphQuads = getGlyphQuads(shapedTextOrientations.first, layout, textPlacement, positions);
+ }
+ if (shapedTextOrientations.second) {
+ verticalGlyphQuads = getGlyphQuads(shapedTextOrientations.second, layout, textPlacement, positions);
}
if (shapedTextOrientations.first && shapedTextOrientations.second) {
diff --git a/src/mbgl/layout/symbol_instance.hpp b/src/mbgl/layout/symbol_instance.hpp
index f1df416cd1..827a5dbbdb 100644
--- a/src/mbgl/layout/symbol_instance.hpp
+++ b/src/mbgl/layout/symbol_instance.hpp
@@ -5,6 +5,7 @@
#include <mbgl/text/collision_feature.hpp>
#include <mbgl/style/layers/symbol_layer_properties.hpp>
+
namespace mbgl {
class Anchor;
@@ -18,7 +19,6 @@ public:
optional<PositionedIcon> shapedIcon,
const style::SymbolLayoutProperties::Evaluated&,
const float layoutTextSize,
- const bool inside,
const uint32_t index,
const float textBoxScale,
const float textPadding,
@@ -26,18 +26,20 @@ public:
const std::array<float, 2> textOffset,
const float iconBoxScale,
const float iconPadding,
- style::SymbolPlacementType iconPlacement,
const std::array<float, 2> iconOffset,
const GlyphPositionMap&,
const IndexedSubfeature&,
- const std::size_t featureIndex);
+ const std::size_t featureIndex,
+ const std::u16string& key,
+ const float overscaling);
Anchor anchor;
GeometryCoordinates line;
uint32_t index;
bool hasText;
bool hasIcon;
- SymbolQuads glyphQuads;
+ SymbolQuads horizontalGlyphQuads;
+ SymbolQuads verticalGlyphQuads;
optional<SymbolQuad> iconQuad;
CollisionFeature textCollisionFeature;
CollisionFeature iconCollisionFeature;
@@ -45,6 +47,12 @@ public:
std::size_t featureIndex;
std::array<float, 2> textOffset;
std::array<float, 2> iconOffset;
+ std::u16string key;
+ bool isDuplicate;
+ optional<size_t> placedTextIndex;
+ optional<size_t> placedVerticalTextIndex;
+ optional<size_t> placedIconIndex;
+ uint32_t crossTileID = 0;
};
} // namespace mbgl
diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp
index 2c90b69b08..a41a98fcaf 100644
--- a/src/mbgl/layout/symbol_layout.cpp
+++ b/src/mbgl/layout/symbol_layout.cpp
@@ -8,7 +8,6 @@
#include <mbgl/renderer/image_atlas.hpp>
#include <mbgl/style/layers/symbol_layer_impl.hpp>
#include <mbgl/text/get_anchors.hpp>
-#include <mbgl/text/collision_tile.hpp>
#include <mbgl/text/shaping.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/utf.hpp>
@@ -33,7 +32,7 @@ using namespace style;
template <class Property>
static bool has(const style::SymbolLayoutProperties::PossiblyEvaluated& layout) {
return layout.get<Property>().match(
- [&] (const std::string& s) { return !s.empty(); },
+ [&] (const typename Property::Type& t) { return !t.empty(); },
[&] (const auto&) { return true; }
);
}
@@ -43,8 +42,8 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters,
std::unique_ptr<GeometryTileLayer> sourceLayer_,
ImageDependencies& imageDependencies,
GlyphDependencies& glyphDependencies)
- : sourceLayer(std::move(sourceLayer_)),
- bucketName(layers.at(0)->getID()),
+ : bucketName(layers.at(0)->getID()),
+ sourceLayer(std::move(sourceLayer_)),
overscaling(parameters.tileID.overscaleFactor()),
zoom(parameters.tileID.overscaledZ),
mode(parameters.mode),
@@ -83,7 +82,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters,
layout.get<IconPitchAlignment>() = layout.get<IconRotationAlignment>();
}
- const bool hasText = has<TextField>(layout) && !layout.get<TextFont>().empty();
+ const bool hasText = has<TextField>(layout) && has<TextFont>(layout);
const bool hasIcon = has<IconImage>(layout);
if (!hasText && !hasIcon) {
@@ -144,12 +143,15 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters,
&& layout.get<SymbolPlacement>() == SymbolPlacementType::Line
&& util::i18n::allowsVerticalWritingMode(*ft.text);
+ FontStack fontStack = layout.evaluate<TextFont>(zoom, ft);
+ GlyphIDs& dependencies = glyphDependencies[fontStack];
+
// Loop through all characters of this text and collect unique codepoints.
for (char16_t chr : *ft.text) {
- glyphDependencies[layout.get<TextFont>()].insert(chr);
+ dependencies.insert(chr);
if (canVerticalizeText) {
if (char16_t verticalChr = util::i18n::verticalizePunctuation(chr)) {
- glyphDependencies[layout.get<TextFont>()].insert(verticalChr);
+ dependencies.insert(verticalChr);
}
}
}
@@ -179,22 +181,25 @@ bool SymbolLayout::hasSymbolInstances() const {
}
void SymbolLayout::prepare(const GlyphMap& glyphMap, const GlyphPositions& glyphPositions,
- const ImageMap& imageMap, const ImagePositions& imagePositions) {
+ const ImageMap& imageMap, const ImagePositions& imagePositions,
+ const OverscaledTileID& tileID, const std::string& sourceID) {
const bool textAlongLine = layout.get<TextRotationAlignment>() == AlignmentType::Map &&
layout.get<SymbolPlacement>() == SymbolPlacementType::Line;
- auto glyphMapIt = glyphMap.find(layout.get<TextFont>());
- const Glyphs& glyphs = glyphMapIt != glyphMap.end()
- ? glyphMapIt->second : Glyphs();
-
- auto glyphPositionsIt = glyphPositions.find(layout.get<TextFont>());
- const GlyphPositionMap& glyphPositionMap = glyphPositionsIt != glyphPositions.end()
- ? glyphPositionsIt->second : GlyphPositionMap();
-
for (auto it = features.begin(); it != features.end(); ++it) {
auto& feature = *it;
if (feature.geometry.empty()) continue;
+ FontStack fontStack = layout.evaluate<TextFont>(zoom, feature);
+
+ auto glyphMapIt = glyphMap.find(fontStack);
+ const Glyphs& glyphs = glyphMapIt != glyphMap.end()
+ ? glyphMapIt->second : Glyphs();
+
+ auto glyphPositionsIt = glyphPositions.find(fontStack);
+ const GlyphPositionMap& glyphPositionMap = glyphPositionsIt != glyphPositions.end()
+ ? glyphPositionsIt->second : GlyphPositionMap();
+
std::pair<Shaping, Shaping> shapedTextOrientations;
optional<PositionedIcon> shapedIcon;
@@ -248,7 +253,7 @@ void SymbolLayout::prepare(const GlyphMap& glyphMap, const GlyphPositions& glyph
// if either shapedText or icon position is present, add the feature
if (shapedTextOrientations.first || shapedIcon) {
- addFeature(std::distance(features.begin(), it), feature, shapedTextOrientations, shapedIcon, glyphPositionMap);
+ addFeature(std::distance(features.begin(), it), feature, shapedTextOrientations, shapedIcon, glyphPositionMap, tileID, sourceID);
}
feature.geometry.clear();
@@ -261,7 +266,9 @@ void SymbolLayout::addFeature(const std::size_t index,
const SymbolFeature& feature,
const std::pair<Shaping, Shaping>& shapedTextOrientations,
optional<PositionedIcon> shapedIcon,
- const GlyphPositionMap& glyphPositionMap) {
+ const GlyphPositionMap& glyphPositionMap,
+ const OverscaledTileID& tileID,
+ const std::string& sourceID) {
const float minScale = 0.5f;
const float glyphSize = 24.0f;
@@ -288,12 +295,10 @@ void SymbolLayout::addFeature(const std::size_t index,
const SymbolPlacementType textPlacement = layout.get<TextRotationAlignment>() != AlignmentType::Map
? SymbolPlacementType::Point
: layout.get<SymbolPlacement>();
- const SymbolPlacementType iconPlacement = layout.get<IconRotationAlignment>() != AlignmentType::Map
- ? SymbolPlacementType::Point
- : layout.get<SymbolPlacement>();
+
const float textRepeatDistance = symbolSpacing / 2;
- IndexedSubfeature indexedFeature = { feature.index, sourceLayer->getName(), bucketName,
- symbolInstances.size() };
+ IndexedSubfeature indexedFeature(feature.index, sourceLayer->getName(), bucketName, symbolInstances.size(),
+ sourceID, tileID.canonical);
auto addSymbolInstance = [&] (const GeometryCoordinates& line, Anchor& anchor) {
// https://github.com/mapbox/vector-tile-spec/tree/master/2.1#41-layers
@@ -314,14 +319,14 @@ void SymbolLayout::addFeature(const std::size_t index,
if (avoidEdges && !inside) return;
- const bool addToBuffers = mode == MapMode::Still || withinPlus0;
-
- symbolInstances.emplace_back(anchor, line, shapedTextOrientations, shapedIcon,
- layout.evaluate(zoom, feature), layoutTextSize,
- addToBuffers, symbolInstances.size(),
- textBoxScale, textPadding, textPlacement, textOffset,
- iconBoxScale, iconPadding, iconPlacement, iconOffset,
- glyphPositionMap, indexedFeature, index);
+ if (mode == MapMode::Tile || withinPlus0) {
+ symbolInstances.emplace_back(anchor, line, shapedTextOrientations, shapedIcon,
+ layout.evaluate(zoom, feature), layoutTextSize,
+ symbolInstances.size(),
+ textBoxScale, textPadding, textPlacement, textOffset,
+ iconBoxScale, iconPadding, iconOffset,
+ glyphPositionMap, indexedFeature, index, feature.text.value_or(std::u16string()), overscaling);
+ }
};
const auto& type = feature.getType();
@@ -392,108 +397,93 @@ bool SymbolLayout::anchorIsTooClose(const std::u16string& text, const float repe
return false;
}
-std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile) {
- auto bucket = std::make_unique<SymbolBucket>(layout, layerPaintProperties, textSize, iconSize, zoom, sdfIcons, iconsNeedLinear);
-
- // Calculate which labels can be shown and when they can be shown and
- // create the bufers used for rendering.
-
- const SymbolPlacementType textPlacement = layout.get<TextRotationAlignment>() != AlignmentType::Map
- ? SymbolPlacementType::Point
- : layout.get<SymbolPlacement>();
- const SymbolPlacementType iconPlacement = layout.get<IconRotationAlignment>() != AlignmentType::Map
- ? SymbolPlacementType::Point
- : layout.get<SymbolPlacement>();
+// Analog of `addToLineVertexArray` in JS. This version doesn't need to build up a line array like the
+// JS version does, but it uses the same logic to calculate tile distances.
+std::vector<float> CalculateTileDistances(const GeometryCoordinates& line, const Anchor& anchor) {
+ std::vector<float> tileDistances(line.size());
+ if (anchor.segment != -1) {
+ auto sumForwardLength = util::dist<float>(anchor.point, line[anchor.segment + 1]);
+ auto sumBackwardLength = util::dist<float>(anchor.point, line[anchor.segment]);
+ for (size_t i = anchor.segment + 1; i < line.size(); i++) {
+ tileDistances[i] = sumForwardLength;
+ if (i < line.size() - 1) {
+ sumForwardLength += util::dist<float>(line[i + 1], line[i]);
+ }
+ }
+ for (auto i = anchor.segment; i >= 0; i--) {
+ tileDistances[i] = sumBackwardLength;
+ if (i > 0) {
+ sumBackwardLength += util::dist<float>(line[i - 1], line[i]);
+ }
+ }
+ }
+ return tileDistances;
+}
+std::unique_ptr<SymbolBucket> SymbolLayout::place(const bool showCollisionBoxes) {
const bool mayOverlap = layout.get<TextAllowOverlap>() || layout.get<IconAllowOverlap>() ||
layout.get<TextIgnorePlacement>() || layout.get<IconIgnorePlacement>();
+
+ auto bucket = std::make_unique<SymbolBucket>(layout, layerPaintProperties, textSize, iconSize, zoom, sdfIcons, iconsNeedLinear, mayOverlap, std::move(symbolInstances));
- const bool keepUpright = layout.get<TextKeepUpright>();
-
- // Sort symbols by their y position on the canvas so that they lower symbols
- // are drawn on top of higher symbols.
- // Don't sort symbols that won't overlap because it isn't necessary and
- // because it causes more labels to pop in and out when rotating.
- if (mayOverlap) {
- const float sin = std::sin(collisionTile.config.angle);
- const float cos = std::cos(collisionTile.config.angle);
-
- std::sort(symbolInstances.begin(), symbolInstances.end(), [sin, cos](SymbolInstance &a, SymbolInstance &b) {
- const int32_t aRotated = sin * a.anchor.point.x + cos * a.anchor.point.y;
- const int32_t bRotated = sin * b.anchor.point.x + cos * b.anchor.point.y;
- return aRotated != bRotated ?
- aRotated < bRotated :
- a.index > b.index;
- });
- }
-
- for (SymbolInstance &symbolInstance : symbolInstances) {
+ for (SymbolInstance &symbolInstance : bucket->symbolInstances) {
const bool hasText = symbolInstance.hasText;
const bool hasIcon = symbolInstance.hasIcon;
- const bool iconWithoutText = layout.get<TextOptional>() || !hasText;
- const bool textWithoutIcon = layout.get<IconOptional>() || !hasIcon;
-
- // Calculate the scales at which the text and icon can be placed without collision.
-
- float glyphScale = hasText ?
- collisionTile.placeFeature(symbolInstance.textCollisionFeature,
- layout.get<TextAllowOverlap>(), layout.get<SymbolAvoidEdges>()) :
- collisionTile.minScale;
- float iconScale = hasIcon ?
- collisionTile.placeFeature(symbolInstance.iconCollisionFeature,
- layout.get<IconAllowOverlap>(), layout.get<SymbolAvoidEdges>()) :
- collisionTile.minScale;
-
-
- // Combine the scales for icons and text.
-
- if (!iconWithoutText && !textWithoutIcon) {
- iconScale = glyphScale = util::max(iconScale, glyphScale);
- } else if (!textWithoutIcon && glyphScale) {
- glyphScale = util::max(iconScale, glyphScale);
- } else if (!iconWithoutText && iconScale) {
- iconScale = util::max(iconScale, glyphScale);
- }
-
const auto& feature = features.at(symbolInstance.featureIndex);
// Insert final placement into collision tree and add glyphs/icons to buffers
if (hasText) {
- const float placementZoom = util::max(util::log2(glyphScale) + zoom, 0.0f);
- collisionTile.insertFeature(symbolInstance.textCollisionFeature, glyphScale, layout.get<TextIgnorePlacement>());
- if (glyphScale < collisionTile.maxScale) {
-
- const float labelAngle = std::fmod((symbolInstance.anchor.angle + collisionTile.config.angle) + 2 * M_PI, 2 * M_PI);
- const bool inVerticalRange = (
- (labelAngle > M_PI * 1.0 / 4.0 && labelAngle <= M_PI * 3.0 / 4) ||
- (labelAngle > M_PI * 5.0 / 4.0 && labelAngle <= M_PI * 7.0 / 4));
- const bool useVerticalMode = symbolInstance.writingModes & WritingModeType::Vertical && inVerticalRange;
-
- const Range<float> sizeData = bucket->textSizeBinder->getVertexSizeData(feature);
+ const Range<float> sizeData = bucket->textSizeBinder->getVertexSizeData(feature);
+ bucket->text.placedSymbols.emplace_back(symbolInstance.anchor.point, symbolInstance.anchor.segment, sizeData.min, sizeData.max,
+ symbolInstance.textOffset, symbolInstance.writingModes, symbolInstance.line, CalculateTileDistances(symbolInstance.line, symbolInstance.anchor));
+ symbolInstance.placedTextIndex = bucket->text.placedSymbols.size() - 1;
+ PlacedSymbol& horizontalSymbol = bucket->text.placedSymbols.back();
+
+ bool firstHorizontal = true;
+ for (const auto& symbol : symbolInstance.horizontalGlyphQuads) {
+ size_t index = addSymbol(
+ bucket->text, sizeData, symbol,
+ symbolInstance.anchor, horizontalSymbol);
+ if (firstHorizontal) {
+ horizontalSymbol.vertexStartIndex = index;
+ firstHorizontal = false;
+ }
+ }
+
+ if (symbolInstance.writingModes & WritingModeType::Vertical) {
bucket->text.placedSymbols.emplace_back(symbolInstance.anchor.point, symbolInstance.anchor.segment, sizeData.min, sizeData.max,
- symbolInstance.textOffset, placementZoom, useVerticalMode, symbolInstance.line);
-
- for (const auto& symbol : symbolInstance.glyphQuads) {
- addSymbol(
- bucket->text, sizeData, symbol, placementZoom,
- keepUpright, textPlacement, symbolInstance.anchor, bucket->text.placedSymbols.back());
+ symbolInstance.textOffset, WritingModeType::Vertical, symbolInstance.line, CalculateTileDistances(symbolInstance.line, symbolInstance.anchor));
+ symbolInstance.placedVerticalTextIndex = bucket->text.placedSymbols.size() - 1;
+
+ PlacedSymbol& verticalSymbol = bucket->text.placedSymbols.back();
+ bool firstVertical = true;
+
+ for (const auto& symbol : symbolInstance.verticalGlyphQuads) {
+ size_t index = addSymbol(
+ bucket->text, sizeData, symbol,
+ symbolInstance.anchor, verticalSymbol);
+
+ if (firstVertical) {
+ verticalSymbol.vertexStartIndex = index;
+ firstVertical = false;
+ }
}
}
}
if (hasIcon) {
- const float placementZoom = util::max(util::log2(iconScale) + zoom, 0.0f);
- collisionTile.insertFeature(symbolInstance.iconCollisionFeature, iconScale, layout.get<IconIgnorePlacement>());
- if (iconScale < collisionTile.maxScale && symbolInstance.iconQuad) {
+ if (symbolInstance.iconQuad) {
const Range<float> sizeData = bucket->iconSizeBinder->getVertexSizeData(feature);
bucket->icon.placedSymbols.emplace_back(symbolInstance.anchor.point, symbolInstance.anchor.segment, sizeData.min, sizeData.max,
- symbolInstance.iconOffset, placementZoom, false, symbolInstance.line);
- addSymbol(
- bucket->icon, sizeData, *symbolInstance.iconQuad, placementZoom,
- keepUpright, iconPlacement, symbolInstance.anchor, bucket->icon.placedSymbols.back());
+ symbolInstance.iconOffset, WritingModeType::None, symbolInstance.line, std::vector<float>());
+ symbolInstance.placedIconIndex = bucket->icon.placedSymbols.size() - 1;
+ PlacedSymbol& iconSymbol = bucket->icon.placedSymbols.back();
+ iconSymbol.vertexStartIndex = addSymbol(
+ bucket->icon, sizeData, *symbolInstance.iconQuad,
+ symbolInstance.anchor, iconSymbol);
}
}
@@ -503,20 +493,17 @@ std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile)
}
}
- if (collisionTile.config.debug) {
- addToDebugBuffers(collisionTile, *bucket);
+ if (showCollisionBoxes) {
+ addToDebugBuffers(*bucket);
}
return bucket;
}
template <typename Buffer>
-void SymbolLayout::addSymbol(Buffer& buffer,
+size_t SymbolLayout::addSymbol(Buffer& buffer,
const Range<float> sizeData,
const SymbolQuad& symbol,
- const float placementZoom,
- const bool keepUpright,
- const style::SymbolPlacementType placement,
const Anchor& labelAnchor,
PlacedSymbol& placedSymbol) {
constexpr const uint16_t vertexLength = 4;
@@ -527,11 +514,6 @@ void SymbolLayout::addSymbol(Buffer& buffer,
const auto &br = symbol.br;
const auto &tex = symbol.tex;
- if (placement == style::SymbolPlacementType::Line && keepUpright) {
- // drop incorrectly oriented glyphs
- if ((symbol.writingMode == WritingModeType::Vertical) != placedSymbol.useVerticalMode) return;
- }
-
if (buffer.segments.empty() || buffer.segments.back().vertexLength + vertexLength > std::numeric_limits<uint16_t>::max()) {
buffer.segments.emplace_back(buffer.vertices.vertexSize(), buffer.triangles.indexSize());
}
@@ -548,11 +530,19 @@ void SymbolLayout::addSymbol(Buffer& buffer,
buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(labelAnchor.point, bl, symbol.glyphOffset.y, tex.x, tex.y + tex.h, sizeData));
buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(labelAnchor.point, br, symbol.glyphOffset.y, tex.x + tex.w, tex.y + tex.h, sizeData));
- auto dynamicVertex = SymbolDynamicLayoutAttributes::vertex(labelAnchor.point, 0, placementZoom);
+ // Dynamic/Opacity vertices are initialized so that the vertex count always agrees with
+ // the layout vertex buffer, but they will always be updated before rendering happens
+ auto dynamicVertex = SymbolDynamicLayoutAttributes::vertex(labelAnchor.point, 0);
buffer.dynamicVertices.emplace_back(dynamicVertex);
buffer.dynamicVertices.emplace_back(dynamicVertex);
buffer.dynamicVertices.emplace_back(dynamicVertex);
buffer.dynamicVertices.emplace_back(dynamicVertex);
+
+ auto opacityVertex = SymbolOpacityAttributes::vertex(1.0, 1.0);
+ buffer.opacityVertices.emplace_back(opacityVertex);
+ buffer.opacityVertices.emplace_back(opacityVertex);
+ buffer.opacityVertices.emplace_back(opacityVertex);
+ buffer.opacityVertices.emplace_back(opacityVertex);
// add the two triangles, referencing the four coordinates we just inserted.
buffer.triangles.emplace_back(index + 0, index + 1, index + 2);
@@ -562,54 +552,62 @@ void SymbolLayout::addSymbol(Buffer& buffer,
segment.indexLength += 6;
placedSymbol.glyphOffsets.push_back(symbol.glyphOffset.x);
+
+ return index;
}
-void SymbolLayout::addToDebugBuffers(CollisionTile& collisionTile, SymbolBucket& bucket) {
+void SymbolLayout::addToDebugBuffers(SymbolBucket& bucket) {
if (!hasSymbolInstances()) {
return;
}
- const float yStretch = collisionTile.yStretch;
-
- auto& collisionBox = bucket.collisionBox;
-
for (const SymbolInstance &symbolInstance : symbolInstances) {
auto populateCollisionBox = [&](const auto& feature) {
+ SymbolBucket::CollisionBuffer& collisionBuffer = feature.alongLine ?
+ static_cast<SymbolBucket::CollisionBuffer&>(bucket.collisionCircle) :
+ static_cast<SymbolBucket::CollisionBuffer&>(bucket.collisionBox);
for (const CollisionBox &box : feature.boxes) {
auto& anchor = box.anchor;
- Point<float> tl{box.x1, box.y1 * yStretch};
- Point<float> tr{box.x2, box.y1 * yStretch};
- Point<float> bl{box.x1, box.y2 * yStretch};
- Point<float> br{box.x2, box.y2 * yStretch};
- tl = util::matrixMultiply(collisionTile.reverseRotationMatrix, tl);
- tr = util::matrixMultiply(collisionTile.reverseRotationMatrix, tr);
- bl = util::matrixMultiply(collisionTile.reverseRotationMatrix, bl);
- br = util::matrixMultiply(collisionTile.reverseRotationMatrix, br);
-
- const float maxZoom = util::clamp(zoom + util::log2(box.maxScale), util::MIN_ZOOM_F, util::MAX_ZOOM_F);
- const float placementZoom = util::clamp(zoom + util::log2(box.placementScale), util::MIN_ZOOM_F, util::MAX_ZOOM_F);
+ Point<float> tl{box.x1, box.y1};
+ Point<float> tr{box.x2, box.y1};
+ Point<float> bl{box.x1, box.y2};
+ Point<float> br{box.x2, box.y2};
static constexpr std::size_t vertexLength = 4;
- static constexpr std::size_t indexLength = 8;
+ const std::size_t indexLength = feature.alongLine ? 6 : 8;
- if (collisionBox.segments.empty() || collisionBox.segments.back().vertexLength + vertexLength > std::numeric_limits<uint16_t>::max()) {
- collisionBox.segments.emplace_back(collisionBox.vertices.vertexSize(), collisionBox.lines.indexSize());
+ if (collisionBuffer.segments.empty() || collisionBuffer.segments.back().vertexLength + vertexLength > std::numeric_limits<uint16_t>::max()) {
+ collisionBuffer.segments.emplace_back(collisionBuffer.vertices.vertexSize(),
+ feature.alongLine? bucket.collisionCircle.triangles.indexSize() : bucket.collisionBox.lines.indexSize());
}
- auto& segment = collisionBox.segments.back();
+ auto& segment = collisionBuffer.segments.back();
uint16_t index = segment.vertexLength;
- collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, tl, maxZoom, placementZoom));
- collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, tr, maxZoom, placementZoom));
- collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, br, maxZoom, placementZoom));
- collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, bl, maxZoom, placementZoom));
-
- collisionBox.lines.emplace_back(index + 0, index + 1);
- collisionBox.lines.emplace_back(index + 1, index + 2);
- collisionBox.lines.emplace_back(index + 2, index + 3);
- collisionBox.lines.emplace_back(index + 3, index + 0);
+ collisionBuffer.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, tl));
+ collisionBuffer.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, tr));
+ collisionBuffer.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, br));
+ collisionBuffer.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, bl));
+
+ // Dynamic vertices are initialized so that the vertex count always agrees with
+ // the layout vertex buffer, but they will always be updated before rendering happens
+ auto dynamicVertex = CollisionBoxDynamicAttributes::vertex(false, false);
+ collisionBuffer.dynamicVertices.emplace_back(dynamicVertex);
+ collisionBuffer.dynamicVertices.emplace_back(dynamicVertex);
+ collisionBuffer.dynamicVertices.emplace_back(dynamicVertex);
+ collisionBuffer.dynamicVertices.emplace_back(dynamicVertex);
+
+ if (feature.alongLine) {
+ bucket.collisionCircle.triangles.emplace_back(index, index + 1, index + 2);
+ bucket.collisionCircle.triangles.emplace_back(index, index + 2, index + 3);
+ } else {
+ bucket.collisionBox.lines.emplace_back(index + 0, index + 1);
+ bucket.collisionBox.lines.emplace_back(index + 1, index + 2);
+ bucket.collisionBox.lines.emplace_back(index + 2, index + 3);
+ bucket.collisionBox.lines.emplace_back(index + 3, index + 0);
+ }
segment.vertexLength += vertexLength;
segment.indexLength += indexLength;
diff --git a/src/mbgl/layout/symbol_layout.hpp b/src/mbgl/layout/symbol_layout.hpp
index 90f5b3c91d..6951c29ada 100644
--- a/src/mbgl/layout/symbol_layout.hpp
+++ b/src/mbgl/layout/symbol_layout.hpp
@@ -16,7 +16,6 @@
namespace mbgl {
class BucketParameters;
-class CollisionTile;
class SymbolBucket;
class Anchor;
class RenderLayer;
@@ -35,42 +34,44 @@ public:
GlyphDependencies&);
void prepare(const GlyphMap&, const GlyphPositions&,
- const ImageMap&, const ImagePositions&);
+ const ImageMap&, const ImagePositions&,
+ const OverscaledTileID&, const std::string&);
- std::unique_ptr<SymbolBucket> place(CollisionTile&);
+ std::unique_ptr<SymbolBucket> place(const bool showCollisionBoxes);
bool hasSymbolInstances() const;
std::map<std::string,
std::pair<style::IconPaintProperties::PossiblyEvaluated, style::TextPaintProperties::PossiblyEvaluated>> layerPaintProperties;
+ const std::string bucketName;
+ std::vector<SymbolInstance> symbolInstances;
+
private:
void addFeature(const size_t,
const SymbolFeature&,
const std::pair<Shaping, Shaping>& shapedTextOrientations,
optional<PositionedIcon> shapedIcon,
- const GlyphPositionMap&);
+ const GlyphPositionMap&,
+ const OverscaledTileID&,
+ const std::string&);
bool anchorIsTooClose(const std::u16string& text, const float repeatDistance, const Anchor&);
std::map<std::u16string, std::vector<Anchor>> compareText;
- void addToDebugBuffers(CollisionTile&, SymbolBucket&);
+ void addToDebugBuffers(SymbolBucket&);
// Adds placed items to the buffer.
template <typename Buffer>
- void addSymbol(Buffer&,
+ size_t addSymbol(Buffer&,
const Range<float> sizeData,
const SymbolQuad&,
- float scale,
- const bool keepUpright,
- const style::SymbolPlacementType,
const Anchor& labelAnchor,
PlacedSymbol& placedSymbol);
// Stores the layer so that we can hold on to GeometryTileFeature instances in SymbolFeature,
// which may reference data from this object.
const std::unique_ptr<GeometryTileLayer> sourceLayer;
- const std::string bucketName;
const float overscaling;
const float zoom;
const MapMode mode;
@@ -87,7 +88,6 @@ private:
style::TextSize::UnevaluatedType textSize;
style::IconSize::UnevaluatedType iconSize;
- std::vector<SymbolInstance> symbolInstances;
std::vector<SymbolFeature> features;
BiDi bidi; // Consider moving this up to geometry tile worker to reduce reinstantiation costs; use of BiDi/ubiditransform object must be constrained to one thread
diff --git a/src/mbgl/layout/symbol_projection.cpp b/src/mbgl/layout/symbol_projection.cpp
index d97bfb1ac0..9e077e2532 100644
--- a/src/mbgl/layout/symbol_projection.cpp
+++ b/src/mbgl/layout/symbol_projection.cpp
@@ -3,7 +3,6 @@
#include <mbgl/renderer/render_tile.hpp>
#include <mbgl/renderer/buckets/symbol_bucket.hpp>
#include <mbgl/renderer/layers/render_symbol_layer.hpp>
-#include <mbgl/renderer/frame_history.hpp>
#include <mbgl/util/optional.hpp>
#include <mbgl/util/math.hpp>
@@ -93,9 +92,6 @@ namespace mbgl {
return m;
}
-
- typedef std::pair<Point<float>,float> PointAndCameraDistance;
-
PointAndCameraDistance project(const Point<float>& point, const mat4& matrix) {
vec4 pos = {{ point.x, point.y, 0, 1 }};
matrix::transformMat4(pos, pos, matrix);
@@ -114,7 +110,7 @@ namespace mbgl {
}
}
- bool isVisible(const vec4& anchorPos, const float placementZoom, const std::array<double, 2>& clippingBuffer, const FrameHistory& frameHistory) {
+ bool isVisible(const vec4& anchorPos, const std::array<double, 2>& clippingBuffer) {
const float x = anchorPos[0] / anchorPos[3];
const float y = anchorPos[1] / anchorPos[3];
const bool inPaddedViewport = (
@@ -122,12 +118,12 @@ namespace mbgl {
x <= clippingBuffer[0] &&
y >= -clippingBuffer[1] &&
y <= clippingBuffer[1]);
- return inPaddedViewport && frameHistory.isVisible(placementZoom);
+ return inPaddedViewport;
}
- void addDynamicAttributes(const Point<float>& anchorPoint, const float angle, const float placementZoom,
+ void addDynamicAttributes(const Point<float>& anchorPoint, const float angle,
gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>& dynamicVertexArray) {
- auto dynamicVertex = SymbolDynamicLayoutAttributes::vertex(anchorPoint, angle, placementZoom);
+ auto dynamicVertex = SymbolDynamicLayoutAttributes::vertex(anchorPoint, angle);
dynamicVertexArray.emplace_back(dynamicVertex);
dynamicVertexArray.emplace_back(dynamicVertex);
dynamicVertexArray.emplace_back(dynamicVertex);
@@ -137,20 +133,15 @@ namespace mbgl {
void hideGlyphs(size_t numGlyphs, gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>& dynamicVertexArray) {
const Point<float> offscreenPoint = { -INFINITY, -INFINITY };
for (size_t i = 0; i < numGlyphs; i++) {
- addDynamicAttributes(offscreenPoint, 0, 25, dynamicVertexArray);
+ addDynamicAttributes(offscreenPoint, 0, dynamicVertexArray);
}
}
- struct PlacedGlyph {
- PlacedGlyph(Point<float> point_, float angle_) : point(point_), angle(angle_) {}
- Point<float> point;
- float angle;
- };
-
enum PlacementResult {
OK,
NotEnoughRoom,
- NeedsFlipping
+ NeedsFlipping,
+ UseVertical
};
Point<float> projectTruncatedLineSegment(const Point<float>& previousTilePoint, const Point<float>& currentTilePoint, const Point<float>& previousProjectedPoint, const float minimumLength, const mat4& projectionMatrix) {
@@ -165,7 +156,7 @@ namespace mbgl {
}
optional<PlacedGlyph> placeGlyphAlongLine(const float offsetX, const float lineOffsetX, const float lineOffsetY, const bool flip,
- const Point<float>& projectedAnchorPoint, const Point<float>& tileAnchorPoint, const uint16_t anchorSegment, const GeometryCoordinates& line, const mat4& labelPlaneMatrix) {
+ const Point<float>& projectedAnchorPoint, const Point<float>& tileAnchorPoint, const uint16_t anchorSegment, const GeometryCoordinates& line, const std::vector<float>& tileDistances, const mat4& labelPlaneMatrix, const bool returnTileDistance) {
const float combinedOffsetX = flip ?
offsetX - lineOffsetX :
@@ -185,6 +176,7 @@ namespace mbgl {
int32_t currentIndex = dir > 0 ? anchorSegment : anchorSegment + 1;
+ const int32_t initialIndex = currentIndex;
Point<float> current = projectedAnchorPoint;
Point<float> prev = projectedAnchorPoint;
float distanceToPrev = 0.0;
@@ -195,7 +187,9 @@ namespace mbgl {
currentIndex += dir;
// offset does not fit on the projected line
- if (currentIndex < 0 || currentIndex >= static_cast<int32_t>(line.size())) return {};
+ if (currentIndex < 0 || currentIndex >= static_cast<int32_t>(line.size())) {
+ return {};
+ }
prev = current;
PointAndCameraDistance projection = project(convertPoint<float>(line.at(currentIndex)), labelPlaneMatrix);
@@ -225,7 +219,62 @@ namespace mbgl {
const float segmentAngle = angle + std::atan2(current.y - prev.y, current.x - prev.x);
- return {{ p, segmentAngle }};
+ return {{
+ p,
+ segmentAngle,
+ returnTileDistance ?
+ TileDistance(
+ (currentIndex - dir) == initialIndex ? 0 : tileDistances[currentIndex - dir],
+ absOffsetX - distanceToPrev
+ ) :
+ optional<TileDistance>()
+ }};
+ }
+
+ optional<std::pair<PlacedGlyph, PlacedGlyph>> placeFirstAndLastGlyph(const float fontScale,
+ const float lineOffsetX,
+ const float lineOffsetY,
+ const bool flip,
+ const Point<float>& anchorPoint,
+ const Point<float>& tileAnchorPoint,
+ const PlacedSymbol& symbol,
+ const mat4& labelPlaneMatrix,
+ const bool returnTileDistance) {
+
+ const float firstGlyphOffset = symbol.glyphOffsets.front();
+ const float lastGlyphOffset = symbol.glyphOffsets.back();;
+
+ optional<PlacedGlyph> firstPlacedGlyph = placeGlyphAlongLine(fontScale * firstGlyphOffset, lineOffsetX, lineOffsetY, flip, anchorPoint, tileAnchorPoint, symbol.segment, symbol.line, symbol.tileDistances, labelPlaneMatrix, returnTileDistance);
+ if (!firstPlacedGlyph)
+ return optional<std::pair<PlacedGlyph, PlacedGlyph>>();
+
+ optional<PlacedGlyph> lastPlacedGlyph = placeGlyphAlongLine(fontScale * lastGlyphOffset, lineOffsetX, lineOffsetY, flip, anchorPoint, tileAnchorPoint, symbol.segment, symbol.line, symbol.tileDistances, labelPlaneMatrix, returnTileDistance);
+ if (!lastPlacedGlyph)
+ return optional<std::pair<PlacedGlyph, PlacedGlyph>>();
+
+ return std::make_pair(*firstPlacedGlyph, *lastPlacedGlyph);
+ }
+
+ optional<PlacementResult> requiresOrientationChange(const WritingModeType writingModes, const Point<float>& firstPoint, const Point<float>& lastPoint, const float aspectRatio) {
+ if (writingModes == (WritingModeType::Horizontal | WritingModeType::Vertical)) {
+ // On top of choosing whether to flip, choose whether to render this version of the glyphs or the alternate
+ // vertical glyphs. We can't just filter out vertical glyphs in the horizontal range because the horizontal
+ // and vertical versions can have slightly different projections which could lead to angles where both or
+ // neither showed.
+ auto rise = std::abs(lastPoint.y - firstPoint.y);
+ auto run = std::abs(lastPoint.x - firstPoint.x) * aspectRatio;
+ if (rise > run) {
+ return PlacementResult::UseVertical;
+ }
+ }
+
+ if ((writingModes == WritingModeType::Vertical) ?
+ (firstPoint.y < lastPoint.y) :
+ (firstPoint.x > lastPoint.x)) {
+ // Includes "horizontalOnly" case for labels without vertical glyphs
+ return PlacementResult::NeedsFlipping;
+ }
+ return {};
}
PlacementResult placeGlyphsAlongLine(const PlacedSymbol& symbol,
@@ -236,7 +285,8 @@ namespace mbgl {
const mat4& labelPlaneMatrix,
const mat4& glCoordMatrix,
gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>& dynamicVertexArray,
- const Point<float>& projectedAnchorPoint) {
+ const Point<float>& projectedAnchorPoint,
+ const float aspectRatio) {
const float fontScale = fontSize / 24.0;
const float lineOffsetX = symbol.lineOffset[0] * fontSize;
const float lineOffsetY = symbol.lineOffset[1] * fontSize;
@@ -244,33 +294,30 @@ namespace mbgl {
std::vector<PlacedGlyph> placedGlyphs;
if (symbol.glyphOffsets.size() > 1) {
- const float firstGlyphOffset = symbol.glyphOffsets.front();
- const float lastGlyphOffset = symbol.glyphOffsets.back();
-
- optional<PlacedGlyph> firstPlacedGlyph = placeGlyphAlongLine(fontScale * firstGlyphOffset, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix);
- if (!firstPlacedGlyph)
- return PlacementResult::NotEnoughRoom;
-
- optional<PlacedGlyph> lastPlacedGlyph = placeGlyphAlongLine(fontScale * lastGlyphOffset, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix);
- if (!lastPlacedGlyph)
+ const optional<std::pair<PlacedGlyph, PlacedGlyph>> firstAndLastGlyph =
+ placeFirstAndLastGlyph(fontScale, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol, labelPlaneMatrix, false);
+ if (!firstAndLastGlyph) {
return PlacementResult::NotEnoughRoom;
+ }
- const Point<float> firstPoint = project(firstPlacedGlyph->point, glCoordMatrix).first;
- const Point<float> lastPoint = project(lastPlacedGlyph->point, glCoordMatrix).first;
+ const Point<float> firstPoint = project(firstAndLastGlyph->first.point, glCoordMatrix).first;
+ const Point<float> lastPoint = project(firstAndLastGlyph->second.point, glCoordMatrix).first;
- if (keepUpright && !flip &&
- (symbol.useVerticalMode ? firstPoint.y < lastPoint.y : firstPoint.x > lastPoint.x)) {
- return PlacementResult::NeedsFlipping;
+ if (keepUpright && !flip) {
+ auto orientationChange = requiresOrientationChange(symbol.writingModes, firstPoint, lastPoint, aspectRatio);
+ if (orientationChange) {
+ return *orientationChange;
+ }
}
- placedGlyphs.push_back(*firstPlacedGlyph);
+ placedGlyphs.push_back(firstAndLastGlyph->first);
for (size_t glyphIndex = 1; glyphIndex < symbol.glyphOffsets.size() - 1; glyphIndex++) {
const float glyphOffsetX = symbol.glyphOffsets[glyphIndex];
// Since first and last glyph fit on the line, we're sure that the rest of the glyphs can be placed
- auto placedGlyph = placeGlyphAlongLine(glyphOffsetX * fontScale, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix);
+ auto placedGlyph = placeGlyphAlongLine(glyphOffsetX * fontScale, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment, symbol.line, symbol.tileDistances, labelPlaneMatrix, false);
placedGlyphs.push_back(*placedGlyph);
}
- placedGlyphs.push_back(*lastPlacedGlyph);
+ placedGlyphs.push_back(firstAndLastGlyph->second);
} else if (symbol.glyphOffsets.size() == 1) {
// Only a single glyph to place
// So, determine whether to flip based on projected angle of the line segment it's on
@@ -285,13 +332,14 @@ namespace mbgl {
projectedVertex.first :
projectTruncatedLineSegment(symbol.anchorPoint,tileSegmentEnd, a, 1, posMatrix);
- if (symbol.useVerticalMode ? b.y > a.y : b.x < a.x) {
- return PlacementResult::NeedsFlipping;
+ auto orientationChange = requiresOrientationChange(symbol.writingModes, a, b, aspectRatio);
+ if (orientationChange) {
+ return *orientationChange;
}
}
const float glyphOffsetX = symbol.glyphOffsets.front();
optional<PlacedGlyph> singleGlyph = placeGlyphAlongLine(fontScale * glyphOffsetX, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment,
- symbol.line, labelPlaneMatrix);
+ symbol.line, symbol.tileDistances, labelPlaneMatrix, false);
if (!singleGlyph)
return PlacementResult::NotEnoughRoom;
@@ -301,7 +349,7 @@ namespace mbgl {
// The number of placedGlyphs must equal the number of glyphOffsets, which must correspond to the number of glyph vertices
// There may be 0 glyphs here, if a label consists entirely of glyphs that have 0x0 dimensions
for (auto& placedGlyph : placedGlyphs) {
- addDynamicAttributes(placedGlyph.point, placedGlyph.angle, symbol.placementZoom, dynamicVertexArray);
+ addDynamicAttributes(placedGlyph.point, placedGlyph.angle, dynamicVertexArray);
}
return PlacementResult::OK;
@@ -310,7 +358,7 @@ namespace mbgl {
void reprojectLineLabels(gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>& dynamicVertexArray, const std::vector<PlacedSymbol>& placedSymbols,
const mat4& posMatrix, const style::SymbolPropertyValues& values,
- const RenderTile& tile, const SymbolSizeBinder& sizeBinder, const TransformState& state, const FrameHistory& frameHistory) {
+ const RenderTile& tile, const SymbolSizeBinder& sizeBinder, const TransformState& state) {
const ZoomEvaluatedSize partiallyEvaluatedSize = sizeBinder.evaluateForZoom(state.getZoom());
@@ -326,19 +374,31 @@ namespace mbgl {
const mat4 glCoordMatrix = getGlCoordMatrix(posMatrix, pitchWithMap, rotateWithMap, state, pixelsToTileUnits);
dynamicVertexArray.clear();
+
+ bool useVertical = false;
for (auto& placedSymbol : placedSymbols) {
+ // Don't do calculations for vertical glyphs unless the previous symbol was horizontal
+ // and we determined that vertical glyphs were necessary.
+ // Also don't do calculations for symbols that are collided and fully faded out
+ if (placedSymbol.hidden || (placedSymbol.writingModes == WritingModeType::Vertical && !useVertical)) {
+ hideGlyphs(placedSymbol.glyphOffsets.size(), dynamicVertexArray);
+ continue;
+ }
+ // Awkward... but we're counting on the paired "vertical" symbol coming immediately after its horizontal counterpart
+ useVertical = false;
+
vec4 anchorPos = {{ placedSymbol.anchorPoint.x, placedSymbol.anchorPoint.y, 0, 1 }};
matrix::transformMat4(anchorPos, anchorPos, posMatrix);
// Don't bother calculating the correct point for invisible labels.
- if (!isVisible(anchorPos, placedSymbol.placementZoom, clippingBuffer, frameHistory)) {
+ if (!isVisible(anchorPos, clippingBuffer)) {
hideGlyphs(placedSymbol.glyphOffsets.size(), dynamicVertexArray);
continue;
}
const float cameraToAnchorDistance = anchorPos[3];
- const float perspectiveRatio = 1 + 0.5 * ((cameraToAnchorDistance / state.getCameraToCenterDistance()) - 1.0);
+ const float perspectiveRatio = 0.5 + 0.5 * (cameraToAnchorDistance / state.getCameraToCenterDistance());
const float fontSize = evaluateSizeForFeature(partiallyEvaluatedSize, placedSymbol);
const float pitchScaledFontSize = values.pitchAlignment == style::AlignmentType::Map ?
@@ -347,11 +407,13 @@ namespace mbgl {
const Point<float> anchorPoint = project(placedSymbol.anchorPoint, labelPlaneMatrix).first;
- PlacementResult placeUnflipped = placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, false /*unflipped*/, values.keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray, anchorPoint);
+ PlacementResult placeUnflipped = placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, false /*unflipped*/, values.keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray, anchorPoint, state.getSize().aspectRatio());
+
+ useVertical = placeUnflipped == PlacementResult::UseVertical;
- if (placeUnflipped == PlacementResult::NotEnoughRoom ||
+ if (placeUnflipped == PlacementResult::NotEnoughRoom || useVertical ||
(placeUnflipped == PlacementResult::NeedsFlipping &&
- placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, true /*flipped*/, values.keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray, anchorPoint) == PlacementResult::NotEnoughRoom)) {
+ placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, true /*flipped*/, values.keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray, anchorPoint, state.getSize().aspectRatio()) == PlacementResult::NotEnoughRoom)) {
hideGlyphs(placedSymbol.glyphOffsets.size(), dynamicVertexArray);
}
}
diff --git a/src/mbgl/layout/symbol_projection.hpp b/src/mbgl/layout/symbol_projection.hpp
index 2652fe7ace..3e57d162fd 100644
--- a/src/mbgl/layout/symbol_projection.hpp
+++ b/src/mbgl/layout/symbol_projection.hpp
@@ -8,18 +8,56 @@ namespace mbgl {
class TransformState;
class RenderTile;
- class FrameHistory;
class SymbolSizeBinder;
class PlacedSymbol;
namespace style {
class SymbolPropertyValues;
} // end namespace style
+
+ struct TileDistance {
+ TileDistance(float prevTileDistance_, float lastSegmentViewportDistance_)
+ : prevTileDistance(prevTileDistance_), lastSegmentViewportDistance(lastSegmentViewportDistance_)
+ {}
+ float prevTileDistance;
+ float lastSegmentViewportDistance;
+ };
+
+ struct PlacedGlyph {
+ PlacedGlyph() = default;
+ PlacedGlyph(Point<float> point_, float angle_, optional<TileDistance> tileDistance_)
+ : point(point_), angle(angle_), tileDistance(std::move(tileDistance_))
+ {}
+ PlacedGlyph(PlacedGlyph&& other) noexcept
+ : point(std::move(other.point)), angle(other.angle), tileDistance(std::move(other.tileDistance))
+ {}
+ PlacedGlyph(const PlacedGlyph& other)
+ : point(std::move(other.point)), angle(other.angle), tileDistance(std::move(other.tileDistance))
+ {}
+ Point<float> point;
+ float angle;
+ optional<TileDistance> tileDistance;
+ };
+
+ float evaluateSizeForFeature(const ZoomEvaluatedSize& zoomEvaluatedSize, const PlacedSymbol& placedSymbol);
mat4 getLabelPlaneMatrix(const mat4& posMatrix, const bool pitchWithMap, const bool rotateWithMap, const TransformState& state, const float pixelsToTileUnits);
mat4 getGlCoordMatrix(const mat4& posMatrix, const bool pitchWithMap, const bool rotateWithMap, const TransformState& state, const float pixelsToTileUnits);
+
+ using PointAndCameraDistance = std::pair<Point<float>,float>;
+ PointAndCameraDistance project(const Point<float>& point, const mat4& matrix);
void reprojectLineLabels(gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>&, const std::vector<PlacedSymbol>&,
const mat4& posMatrix, const style::SymbolPropertyValues&,
- const RenderTile&, const SymbolSizeBinder& sizeBinder, const TransformState&, const FrameHistory& frameHistory);
+ const RenderTile&, const SymbolSizeBinder& sizeBinder, const TransformState&);
+
+ optional<std::pair<PlacedGlyph, PlacedGlyph>> placeFirstAndLastGlyph(const float fontScale,
+ const float lineOffsetX,
+ const float lineOffsetY,
+ const bool flip,
+ const Point<float>& anchorPoint,
+ const Point<float>& tileAnchorPoint,
+ const PlacedSymbol& symbol,
+ const mat4& labelPlaneMatrix,
+ const bool returnTileDistance);
} // end namespace mbgl
diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp
index 5e5063905e..947973415a 100644
--- a/src/mbgl/map/map.cpp
+++ b/src/mbgl/map/map.cpp
@@ -149,8 +149,8 @@ void Map::renderStill(StillImageCallback callback) {
return;
}
- if (impl->mode != MapMode::Still) {
- callback(std::make_exception_ptr(util::MisuseException("Map is not in still image render mode")));
+ if (impl->mode != MapMode::Static && impl->mode != MapMode::Tile) {
+ callback(std::make_exception_ptr(util::MisuseException("Map is not in static or tile image render modes")));
return;
}
@@ -617,6 +617,35 @@ ViewportMode Map::getViewportMode() const {
return impl->transform.getViewportMode();
}
+#pragma mark - Projection mode
+
+void Map::setAxonometric(bool axonometric) {
+ impl->transform.setAxonometric(axonometric);
+ impl->onUpdate();
+}
+
+bool Map::getAxonometric() const {
+ return impl->transform.getAxonometric();
+}
+
+void Map::setXSkew(double xSkew) {
+ impl->transform.setXSkew(xSkew);
+ impl->onUpdate();
+}
+
+double Map::getXSkew() const {
+ return impl->transform.getXSkew();
+}
+
+void Map::setYSkew(double ySkew) {
+ impl->transform.setYSkew(ySkew);
+ impl->onUpdate();
+}
+
+double Map::getYSkew() const {
+ return impl->transform.getYSkew();
+}
+
#pragma mark - Projection
ScreenCoordinate Map::pixelForLatLng(const LatLng& latLng) const {
@@ -765,7 +794,7 @@ void Map::Impl::onStyleError(std::exception_ptr error) {
}
void Map::Impl::onResourceError(std::exception_ptr error) {
- if (mode == MapMode::Still && stillImageRequest) {
+ if (mode != MapMode::Continuous && stillImageRequest) {
auto request = std::move(stillImageRequest);
request->callback(error);
}
diff --git a/src/mbgl/map/transform.cpp b/src/mbgl/map/transform.cpp
index 2bb25af28f..105adf0400 100644
--- a/src/mbgl/map/transform.cpp
+++ b/src/mbgl/map/transform.cpp
@@ -527,6 +527,32 @@ ViewportMode Transform::getViewportMode() const {
return state.getViewportMode();
}
+#pragma mark - Projection mode
+
+void Transform::setAxonometric(bool axonometric) {
+ state.axonometric = axonometric;
+}
+
+bool Transform::getAxonometric() const {
+ return state.axonometric;
+}
+
+void Transform::setXSkew(double xSkew) {
+ state.xSkew = xSkew;
+}
+
+double Transform::getXSkew() const {
+ return state.xSkew;
+}
+
+void Transform::setYSkew(double ySkew) {
+ state.ySkew = ySkew;
+}
+
+double Transform::getYSkew() const {
+ return state.ySkew;
+}
+
#pragma mark - Transition
void Transform::startTransition(const CameraOptions& camera,
diff --git a/src/mbgl/map/transform.hpp b/src/mbgl/map/transform.hpp
index 749228bdf5..d429c57661 100644
--- a/src/mbgl/map/transform.hpp
+++ b/src/mbgl/map/transform.hpp
@@ -125,6 +125,14 @@ public:
void setViewportMode(ViewportMode);
ViewportMode getViewportMode() const;
+ // Projection mode
+ void setAxonometric(bool);
+ bool getAxonometric() const;
+ void setXSkew(double xSkew);
+ double getXSkew() const;
+ void setYSkew(double ySkew);
+ double getYSkew() const;
+
// Transitions
bool inTransition() const;
void updateTransitions(const TimePoint& now);
diff --git a/src/mbgl/map/transform_state.cpp b/src/mbgl/map/transform_state.cpp
index d1a320beae..18d2c24aee 100644
--- a/src/mbgl/map/transform_state.cpp
+++ b/src/mbgl/map/transform_state.cpp
@@ -27,7 +27,7 @@ void TransformState::matrixFor(mat4& matrix, const UnwrappedTileID& tileID) cons
matrix::scale(matrix, matrix, s / util::EXTENT, s / util::EXTENT, 1);
}
-void TransformState::getProjMatrix(mat4& projMatrix, uint16_t nearZ) const {
+void TransformState::getProjMatrix(mat4& projMatrix, uint16_t nearZ, bool aligned) const {
if (size.isEmpty()) {
return;
}
@@ -63,11 +63,35 @@ void TransformState::getProjMatrix(mat4& projMatrix, uint16_t nearZ) const {
matrix::rotate_z(projMatrix, projMatrix, getAngle() + getNorthOrientationAngle());
- matrix::translate(projMatrix, projMatrix, pixel_x() - size.width / 2.0f,
- pixel_y() - size.height / 2.0f, 0);
+ const double dx = pixel_x() - size.width / 2.0f, dy = pixel_y() - size.height / 2.0f;
+ matrix::translate(projMatrix, projMatrix, dx, dy, 0);
+
+ if (axonometric) {
+ // mat[11] controls perspective
+ projMatrix[11] = 0;
+
+ // mat[8], mat[9] control x-skew, y-skew
+ projMatrix[8] = xSkew;
+ projMatrix[9] = ySkew;
+ }
matrix::scale(projMatrix, projMatrix, 1, 1,
1.0 / Projection::getMetersPerPixelAtLatitude(getLatLng(LatLng::Unwrapped).latitude(), getZoom()));
+
+ // Make a second projection matrix that is aligned to a pixel grid for rendering raster tiles.
+ // We're rounding the (floating point) x/y values to achieve to avoid rendering raster images to fractional
+ // coordinates. Additionally, we adjust by half a pixel in either direction in case that viewport dimension
+ // is an odd integer to preserve rendering to the pixel grid. We're rotating this shift based on the angle
+ // of the transformation so that 0°, 90°, 180°, and 270° rasters are crisp, and adjust the shift so that
+ // it is always <= 0.5 pixels.
+ if (aligned) {
+ const float xShift = float(size.width % 2) / 2, yShift = float(size.height % 2) / 2;
+ const double angleCos = std::cos(angle), angleSin = std::sin(angle);
+ double devNull;
+ const float dxa = -std::modf(dx, &devNull) + angleCos * xShift + angleSin * yShift;
+ const float dya = -std::modf(dy, &devNull) + angleCos * yShift + angleSin * xShift;
+ matrix::translate(projMatrix, projMatrix, dxa > 0.5 ? dxa - 1 : dxa, dya > 0.5 ? dya - 1 : dya, 0);
+ }
}
#pragma mark - Dimensions
diff --git a/src/mbgl/map/transform_state.hpp b/src/mbgl/map/transform_state.hpp
index 59522d89fd..451802034d 100644
--- a/src/mbgl/map/transform_state.hpp
+++ b/src/mbgl/map/transform_state.hpp
@@ -25,7 +25,7 @@ public:
// Matrix
void matrixFor(mat4&, const UnwrappedTileID&) const;
- void getProjMatrix(mat4& matrix, uint16_t nearZ = 1) const;
+ void getProjMatrix(mat4& matrix, uint16_t nearZ = 1, bool aligned = false) const;
// Dimensions
Size getSize() const;
@@ -134,6 +134,9 @@ private:
// `fov = 2 * arctan((height / 2) / (height * 1.5))`
double fov = 0.6435011087932844;
double pitch = 0.0;
+ double xSkew = 0.0;
+ double ySkew = 1.0;
+ bool axonometric = false;
// cache values for spherical mercator math
double Bc = Projection::worldSize(scale) / util::DEGREES_MAX;
diff --git a/src/mbgl/programs/attributes.hpp b/src/mbgl/programs/attributes.hpp
index d023ec7d83..c677c84d5d 100644
--- a/src/mbgl/programs/attributes.hpp
+++ b/src/mbgl/programs/attributes.hpp
@@ -28,8 +28,9 @@ MBGL_DEFINE_ATTRIBUTE(float, 3, a_projected_pos);
MBGL_DEFINE_ATTRIBUTE(int16_t, 2, a_label_pos);
MBGL_DEFINE_ATTRIBUTE(int16_t, 2, a_anchor_pos);
MBGL_DEFINE_ATTRIBUTE(uint16_t, 2, a_texture_pos);
-MBGL_DEFINE_ATTRIBUTE(int16_t, 3, a_normal);
-MBGL_DEFINE_ATTRIBUTE(uint16_t, 1, a_edgedistance);
+MBGL_DEFINE_ATTRIBUTE(int16_t, 4, a_normal_ed);
+MBGL_DEFINE_ATTRIBUTE(uint8_t, 1, a_fade_opacity);
+MBGL_DEFINE_ATTRIBUTE(uint8_t, 2, a_placed);
template <typename T, std::size_t N>
struct a_data {
@@ -141,5 +142,15 @@ struct a_halo_blur {
using Type = gl::Attribute<float, 1>;
};
+struct a_weight {
+ static auto name() { return "a_weight"; }
+ using Type = gl::Attribute<float, 1>;
+};
+
} // namespace attributes
+
+struct PositionOnlyLayoutAttributes : gl::Attributes<
+ attributes::a_pos>
+{};
+
} // namespace mbgl
diff --git a/src/mbgl/programs/background_program.cpp b/src/mbgl/programs/background_program.cpp
new file mode 100644
index 0000000000..52a9638d6b
--- /dev/null
+++ b/src/mbgl/programs/background_program.cpp
@@ -0,0 +1,47 @@
+#include <mbgl/programs/background_program.hpp>
+#include <mbgl/renderer/image_atlas.hpp>
+#include <mbgl/renderer/cross_faded_property_evaluator.hpp>
+#include <mbgl/tile/tile_id.hpp>
+#include <mbgl/map/transform_state.hpp>
+
+namespace mbgl {
+
+using namespace style;
+
+static_assert(sizeof(BackgroundLayoutVertex) == 4, "expected BackgroundLayoutVertex size");
+
+BackgroundPatternUniforms::Values
+BackgroundPatternUniforms::values(mat4 matrix,
+ float opacity,
+ Size atlasSize,
+ const ImagePosition& a,
+ const ImagePosition& b,
+ const Faded<std::string>& fading,
+ const UnwrappedTileID& tileID,
+ const TransformState& state)
+{
+ int32_t tileSizeAtNearestZoom = util::tileSize * state.zoomScale(state.getIntegerZoom() - tileID.canonical.z);
+ int32_t pixelX = tileSizeAtNearestZoom * (tileID.canonical.x + tileID.wrap * state.zoomScale(tileID.canonical.z));
+ int32_t pixelY = tileSizeAtNearestZoom * tileID.canonical.y;
+
+ return BackgroundPatternUniforms::Values {
+ uniforms::u_matrix::Value{ matrix },
+ uniforms::u_opacity::Value{ opacity },
+ uniforms::u_texsize::Value{ atlasSize },
+ uniforms::u_pattern_tl_a::Value{ a.tl() },
+ uniforms::u_pattern_br_a::Value{ a.br() },
+ uniforms::u_pattern_tl_b::Value{ b.tl() },
+ uniforms::u_pattern_br_b::Value{ b.br() },
+ uniforms::u_pattern_size_a::Value{ a.displaySize() },
+ uniforms::u_pattern_size_b::Value{ b.displaySize() },
+ uniforms::u_scale_a::Value{ fading.fromScale },
+ uniforms::u_scale_b::Value{ fading.toScale },
+ uniforms::u_mix::Value{ fading.t },
+ uniforms::u_image::Value{ 0 },
+ uniforms::u_pixel_coord_upper::Value{ std::array<float, 2> {{ float(pixelX >> 16), float(pixelY >> 16) }} },
+ uniforms::u_pixel_coord_lower::Value{ std::array<float, 2> {{ float(pixelX & 0xFFFF), float(pixelY & 0xFFFF) }} },
+ uniforms::u_tile_units_to_pixels::Value{ 1.0f / tileID.pixelsToTileUnits(1.0f, state.getIntegerZoom()) },
+ };
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/programs/background_program.hpp b/src/mbgl/programs/background_program.hpp
new file mode 100644
index 0000000000..b76318938c
--- /dev/null
+++ b/src/mbgl/programs/background_program.hpp
@@ -0,0 +1,83 @@
+#pragma once
+
+#include <mbgl/programs/program.hpp>
+#include <mbgl/programs/attributes.hpp>
+#include <mbgl/programs/uniforms.hpp>
+#include <mbgl/shaders/background.hpp>
+#include <mbgl/shaders/background_pattern.hpp>
+#include <mbgl/util/geometry.hpp>
+#include <mbgl/util/mat4.hpp>
+#include <mbgl/util/size.hpp>
+#include <mbgl/style/layers/background_layer_properties.hpp>
+
+#include <string>
+
+namespace mbgl {
+
+class ImagePosition;
+class UnwrappedTileID;
+class TransformState;
+template <class> class Faded;
+
+using BackgroundLayoutAttributes = PositionOnlyLayoutAttributes;
+
+struct BackgroundUniforms : gl::Uniforms<
+ uniforms::u_matrix,
+ uniforms::u_color,
+ uniforms::u_opacity>
+{};
+
+struct BackgroundPatternUniforms : gl::Uniforms<
+ uniforms::u_matrix,
+ uniforms::u_opacity,
+ uniforms::u_texsize,
+ uniforms::u_pattern_tl_a,
+ uniforms::u_pattern_br_a,
+ uniforms::u_pattern_tl_b,
+ uniforms::u_pattern_br_b,
+ uniforms::u_pattern_size_a,
+ uniforms::u_pattern_size_b,
+ uniforms::u_scale_a,
+ uniforms::u_scale_b,
+ uniforms::u_mix,
+ uniforms::u_image,
+ uniforms::u_pixel_coord_upper,
+ uniforms::u_pixel_coord_lower,
+ uniforms::u_tile_units_to_pixels>
+{
+ static Values values(mat4 matrix,
+ float opacity,
+ Size atlasSize,
+ const ImagePosition&,
+ const ImagePosition&,
+ const Faded<std::string>&,
+ const UnwrappedTileID&,
+ const TransformState&);
+};
+
+class BackgroundProgram : public Program<
+ shaders::background,
+ gl::Triangle,
+ BackgroundLayoutAttributes,
+ BackgroundUniforms,
+ style::Properties<>>
+{
+public:
+ using Program::Program;
+};
+
+class BackgroundPatternProgram : public Program<
+ shaders::background_pattern,
+ gl::Triangle,
+ BackgroundLayoutAttributes,
+ BackgroundPatternUniforms,
+ style::Properties<>>
+{
+public:
+ using Program::Program;
+};
+
+using BackgroundLayoutVertex = BackgroundProgram::LayoutVertex;
+using BackgroundAttributes = BackgroundProgram::Attributes;
+
+} // namespace mbgl
diff --git a/src/mbgl/programs/clipping_mask_program.hpp b/src/mbgl/programs/clipping_mask_program.hpp
new file mode 100644
index 0000000000..5dff4849fe
--- /dev/null
+++ b/src/mbgl/programs/clipping_mask_program.hpp
@@ -0,0 +1,26 @@
+#pragma once
+
+#include <mbgl/programs/program.hpp>
+#include <mbgl/programs/attributes.hpp>
+#include <mbgl/programs/uniforms.hpp>
+#include <mbgl/shaders/clipping_mask.hpp>
+#include <mbgl/style/properties.hpp>
+
+namespace mbgl {
+
+class ClippingMaskProgram : public Program<
+ shaders::clipping_mask,
+ gl::Triangle,
+ PositionOnlyLayoutAttributes,
+ gl::Uniforms<
+ uniforms::u_matrix>,
+ style::Properties<>>
+{
+public:
+ using Program::Program;
+};
+
+using ClippingMaskLayoutVertex = ClippingMaskProgram::LayoutVertex;
+using ClippingMaskAttributes = ClippingMaskProgram::Attributes;
+
+} // namespace mbgl
diff --git a/src/mbgl/programs/collision_box_program.hpp b/src/mbgl/programs/collision_box_program.hpp
index ba99e0c087..6e75adf36e 100644
--- a/src/mbgl/programs/collision_box_program.hpp
+++ b/src/mbgl/programs/collision_box_program.hpp
@@ -4,6 +4,7 @@
#include <mbgl/programs/attributes.hpp>
#include <mbgl/programs/uniforms.hpp>
#include <mbgl/shaders/collision_box.hpp>
+#include <mbgl/shaders/collision_circle.hpp>
#include <mbgl/style/properties.hpp>
#include <mbgl/util/geometry.hpp>
@@ -11,37 +12,34 @@
namespace mbgl {
-namespace uniforms {
-MBGL_DEFINE_UNIFORM_SCALAR(float, u_scale);
-MBGL_DEFINE_UNIFORM_SCALAR(float, u_maxzoom);
-} // namespace uniforms
-
-using CollisionBoxAttributes = gl::Attributes<
+using CollisionBoxLayoutAttributes = gl::Attributes<
attributes::a_pos,
attributes::a_anchor_pos,
- attributes::a_extrude,
- attributes::a_data<uint8_t, 2>>;
+ attributes::a_extrude>;
+
+struct CollisionBoxDynamicAttributes : gl::Attributes<attributes::a_placed> {
+ static Vertex vertex(bool placed, bool notUsed) {
+ return Vertex {
+ {{ static_cast<uint8_t>(placed), static_cast<uint8_t>(notUsed) }}
+ };
+ }
+};
class CollisionBoxProgram : public Program<
shaders::collision_box,
gl::Line,
- CollisionBoxAttributes,
+ gl::ConcatenateAttributes<CollisionBoxLayoutAttributes, CollisionBoxDynamicAttributes>,
gl::Uniforms<
uniforms::u_matrix,
- uniforms::u_scale,
- uniforms::u_zoom,
- uniforms::u_maxzoom,
- uniforms::u_collision_y_stretch,
- uniforms::u_camera_to_center_distance,
- uniforms::u_pitch,
- uniforms::u_fadetexture>,
+ uniforms::u_extrude_scale,
+ uniforms::u_camera_to_center_distance>,
style::Properties<>>
{
public:
using Program::Program;
- static LayoutVertex vertex(Point<float> a, Point<float> anchor, Point<float> o, float maxzoom, float placementZoom) {
- return LayoutVertex {
+ static CollisionBoxLayoutAttributes::Vertex vertex(Point<float> a, Point<float> anchor, Point<float> o) {
+ return CollisionBoxLayoutAttributes::Vertex {
{{
static_cast<int16_t>(a.x),
static_cast<int16_t>(a.y)
@@ -53,13 +51,132 @@ public:
{{
static_cast<int16_t>(::round(o.x)),
static_cast<int16_t>(::round(o.y))
+ }}
+ };
+ }
+
+ template <class DrawMode>
+ void draw(gl::Context& context,
+ DrawMode drawMode,
+ gl::DepthMode depthMode,
+ gl::StencilMode stencilMode,
+ gl::ColorMode colorMode,
+ const UniformValues& uniformValues,
+ const gl::VertexBuffer<CollisionBoxLayoutAttributes::Vertex>& layoutVertexBuffer,
+ const gl::VertexBuffer<CollisionBoxDynamicAttributes::Vertex>& dynamicVertexBuffer,
+ const gl::IndexBuffer<DrawMode>& indexBuffer,
+ const SegmentVector<Attributes>& segments,
+ const PaintPropertyBinders& paintPropertyBinders,
+ const typename PaintProperties::PossiblyEvaluated& currentProperties,
+ float currentZoom,
+ const std::string& layerID) {
+ typename AllUniforms::Values allUniformValues = uniformValues
+ .concat(paintPropertyBinders.uniformValues(currentZoom, currentProperties));
+
+ typename Attributes::Bindings allAttributeBindings = CollisionBoxLayoutAttributes::bindings(layoutVertexBuffer)
+ .concat(CollisionBoxDynamicAttributes::bindings(dynamicVertexBuffer))
+ .concat(paintPropertyBinders.attributeBindings(currentProperties));
+
+ assert(layoutVertexBuffer.vertexCount == dynamicVertexBuffer.vertexCount);
+
+ for (auto& segment : segments) {
+ auto vertexArrayIt = segment.vertexArrays.find(layerID);
+
+ if (vertexArrayIt == segment.vertexArrays.end()) {
+ vertexArrayIt = segment.vertexArrays.emplace(layerID, context.createVertexArray()).first;
+ }
+
+ program.draw(
+ context,
+ std::move(drawMode),
+ std::move(depthMode),
+ std::move(stencilMode),
+ std::move(colorMode),
+ allUniformValues,
+ vertexArrayIt->second,
+ Attributes::offsetBindings(allAttributeBindings, segment.vertexOffset),
+ indexBuffer,
+ segment.indexOffset,
+ segment.indexLength);
+ }
+ }
+};
+
+
+class CollisionCircleProgram : public Program<
+ shaders::collision_circle,
+ gl::Triangle,
+ gl::ConcatenateAttributes<CollisionBoxLayoutAttributes, CollisionBoxDynamicAttributes>,
+ gl::Uniforms<
+ uniforms::u_matrix,
+ uniforms::u_extrude_scale,
+ uniforms::u_overscale_factor,
+ uniforms::u_camera_to_center_distance>,
+ style::Properties<>>
+{
+public:
+ using Program::Program;
+
+ static CollisionBoxLayoutAttributes::Vertex vertex(Point<float> a, Point<float> anchor, Point<float> o) {
+ return CollisionBoxLayoutAttributes::Vertex {
+ {{
+ static_cast<int16_t>(a.x),
+ static_cast<int16_t>(a.y)
+ }},
+ {{
+ static_cast<int16_t>(anchor.x),
+ static_cast<int16_t>(anchor.y)
}},
{{
- static_cast<uint8_t>(maxzoom * 10),
- static_cast<uint8_t>(placementZoom * 10)
+ static_cast<int16_t>(::round(o.x)),
+ static_cast<int16_t>(::round(o.y))
}}
};
}
+
+ template <class DrawMode>
+ void draw(gl::Context& context,
+ DrawMode drawMode,
+ gl::DepthMode depthMode,
+ gl::StencilMode stencilMode,
+ gl::ColorMode colorMode,
+ const UniformValues& uniformValues,
+ const gl::VertexBuffer<CollisionBoxLayoutAttributes::Vertex>& layoutVertexBuffer,
+ const gl::VertexBuffer<CollisionBoxDynamicAttributes::Vertex>& dynamicVertexBuffer,
+ const gl::IndexBuffer<DrawMode>& indexBuffer,
+ const SegmentVector<Attributes>& segments,
+ const PaintPropertyBinders& paintPropertyBinders,
+ const typename PaintProperties::PossiblyEvaluated& currentProperties,
+ float currentZoom,
+ const std::string& layerID) {
+ typename AllUniforms::Values allUniformValues = uniformValues
+ .concat(paintPropertyBinders.uniformValues(currentZoom, currentProperties));
+
+ typename Attributes::Bindings allAttributeBindings = CollisionBoxLayoutAttributes::bindings(layoutVertexBuffer)
+ .concat(CollisionBoxDynamicAttributes::bindings(dynamicVertexBuffer))
+ .concat(paintPropertyBinders.attributeBindings(currentProperties));
+
+ for (auto& segment : segments) {
+ auto vertexArrayIt = segment.vertexArrays.find(layerID);
+
+ if (vertexArrayIt == segment.vertexArrays.end()) {
+ vertexArrayIt = segment.vertexArrays.emplace(layerID, context.createVertexArray()).first;
+ }
+
+ program.draw(
+ context,
+ std::move(drawMode),
+ std::move(depthMode),
+ std::move(stencilMode),
+ std::move(colorMode),
+ allUniformValues,
+ vertexArrayIt->second,
+ Attributes::offsetBindings(allAttributeBindings, segment.vertexOffset),
+ indexBuffer,
+ segment.indexOffset,
+ segment.indexLength);
+ }
+ }
};
using CollisionBoxVertex = CollisionBoxProgram::LayoutVertex;
diff --git a/src/mbgl/programs/fill_extrusion_program.hpp b/src/mbgl/programs/fill_extrusion_program.hpp
index 820670068e..c499e9ef2d 100644
--- a/src/mbgl/programs/fill_extrusion_program.hpp
+++ b/src/mbgl/programs/fill_extrusion_program.hpp
@@ -30,8 +30,7 @@ MBGL_DEFINE_UNIFORM_SCALAR(float, u_height_factor);
struct FillExtrusionLayoutAttributes : gl::Attributes<
attributes::a_pos,
- attributes::a_normal,
- attributes::a_edgedistance>
+ attributes::a_normal_ed>
{};
struct FillExtrusionUniforms : gl::Uniforms<
@@ -100,12 +99,9 @@ public:
// We pack a bool (`t`) into the x component indicating whether it is an upper or lower vertex
static_cast<int16_t>(floor(nx * factor) * 2 + t),
static_cast<int16_t>(ny * factor * 2),
- static_cast<int16_t>(nz * factor * 2)
-
- }},
- {{
+ static_cast<int16_t>(nz * factor * 2),
// The edgedistance attribute is used for wrapping fill_extrusion patterns
- e
+ static_cast<int16_t>(e)
}}
};
}
diff --git a/src/mbgl/programs/fill_program.hpp b/src/mbgl/programs/fill_program.hpp
index 2dfeea3279..ac478250fc 100644
--- a/src/mbgl/programs/fill_program.hpp
+++ b/src/mbgl/programs/fill_program.hpp
@@ -21,9 +21,7 @@ class UnwrappedTileID;
class TransformState;
template <class> class Faded;
-struct FillLayoutAttributes : gl::Attributes<
- attributes::a_pos>
-{};
+using FillLayoutAttributes = PositionOnlyLayoutAttributes;
struct FillUniforms : gl::Uniforms<
uniforms::u_matrix,
diff --git a/src/mbgl/programs/heatmap_program.cpp b/src/mbgl/programs/heatmap_program.cpp
new file mode 100644
index 0000000000..67f84fbd52
--- /dev/null
+++ b/src/mbgl/programs/heatmap_program.cpp
@@ -0,0 +1,7 @@
+#include <mbgl/programs/heatmap_program.hpp>
+
+namespace mbgl {
+
+static_assert(sizeof(HeatmapLayoutVertex) == 4, "expected HeatmapLayoutVertex size");
+
+} // namespace mbgl
diff --git a/src/mbgl/programs/heatmap_program.hpp b/src/mbgl/programs/heatmap_program.hpp
new file mode 100644
index 0000000000..2d9b80404f
--- /dev/null
+++ b/src/mbgl/programs/heatmap_program.hpp
@@ -0,0 +1,49 @@
+#pragma once
+
+#include <mbgl/programs/program.hpp>
+#include <mbgl/programs/attributes.hpp>
+#include <mbgl/programs/uniforms.hpp>
+#include <mbgl/shaders/heatmap.hpp>
+#include <mbgl/util/geometry.hpp>
+#include <mbgl/style/layers/heatmap_layer_properties.hpp>
+
+namespace mbgl {
+
+namespace uniforms {
+MBGL_DEFINE_UNIFORM_SCALAR(float, u_intensity);
+} // namespace uniforms
+
+class HeatmapProgram : public Program<
+ shaders::heatmap,
+ gl::Triangle,
+ gl::Attributes<
+ attributes::a_pos>,
+ gl::Uniforms<
+ uniforms::u_intensity,
+ uniforms::u_matrix,
+ uniforms::heatmap::u_extrude_scale>,
+ style::HeatmapPaintProperties>
+{
+public:
+ using Program::Program;
+
+ /*
+ * @param {number} x vertex position
+ * @param {number} y vertex position
+ * @param {number} ex extrude normal
+ * @param {number} ey extrude normal
+ */
+ static LayoutVertex vertex(Point<int16_t> p, float ex, float ey) {
+ return LayoutVertex {
+ {{
+ static_cast<int16_t>((p.x * 2) + ((ex + 1) / 2)),
+ static_cast<int16_t>((p.y * 2) + ((ey + 1) / 2))
+ }}
+ };
+ }
+};
+
+using HeatmapLayoutVertex = HeatmapProgram::LayoutVertex;
+using HeatmapAttributes = HeatmapProgram::Attributes;
+
+} // namespace mbgl
diff --git a/src/mbgl/programs/heatmap_texture_program.cpp b/src/mbgl/programs/heatmap_texture_program.cpp
new file mode 100644
index 0000000000..3b0e24eab8
--- /dev/null
+++ b/src/mbgl/programs/heatmap_texture_program.cpp
@@ -0,0 +1,7 @@
+#include <mbgl/programs/heatmap_texture_program.hpp>
+
+namespace mbgl {
+
+static_assert(sizeof(HeatmapTextureLayoutVertex) == 4, "expected HeatmapTextureLayoutVertex size");
+
+} // namespace mbgl
diff --git a/src/mbgl/programs/heatmap_texture_program.hpp b/src/mbgl/programs/heatmap_texture_program.hpp
new file mode 100644
index 0000000000..7afe8060d0
--- /dev/null
+++ b/src/mbgl/programs/heatmap_texture_program.hpp
@@ -0,0 +1,43 @@
+#pragma once
+
+#include <mbgl/programs/program.hpp>
+#include <mbgl/programs/attributes.hpp>
+#include <mbgl/programs/uniforms.hpp>
+#include <mbgl/shaders/heatmap_texture.hpp>
+#include <mbgl/style/properties.hpp>
+#include <mbgl/util/geometry.hpp>
+
+namespace mbgl {
+
+namespace uniforms {
+MBGL_DEFINE_UNIFORM_SCALAR(gl::TextureUnit, u_color_ramp);
+} // namespace uniforms
+
+class HeatmapTextureProgram : public Program<
+ shaders::heatmap_texture,
+ gl::Triangle,
+ gl::Attributes<attributes::a_pos>,
+ gl::Uniforms<
+ uniforms::u_matrix,
+ uniforms::u_world,
+ uniforms::u_image,
+ uniforms::u_color_ramp,
+ uniforms::u_opacity>,
+ style::Properties<>> {
+public:
+ using Program::Program;
+
+ static LayoutVertex layoutVertex(Point<int16_t> p) {
+ return LayoutVertex{
+ {{
+ p.x,
+ p.y
+ }}
+ };
+ }
+};
+
+using HeatmapTextureLayoutVertex = HeatmapTextureProgram::LayoutVertex;
+using HeatmapTextureAttributes = HeatmapTextureProgram::Attributes;
+
+} // namespace mbgl
diff --git a/src/mbgl/programs/hillshade_prepare_program.cpp b/src/mbgl/programs/hillshade_prepare_program.cpp
new file mode 100644
index 0000000000..0c0446d3f5
--- /dev/null
+++ b/src/mbgl/programs/hillshade_prepare_program.cpp
@@ -0,0 +1,7 @@
+#include <mbgl/programs/hillshade_prepare_program.hpp>
+
+namespace mbgl {
+
+static_assert(sizeof(HillshadePrepareLayoutVertex) == 8, "expected HillshadeLayoutVertex size");
+
+} // namespace mbgl
diff --git a/src/mbgl/programs/hillshade_prepare_program.hpp b/src/mbgl/programs/hillshade_prepare_program.hpp
new file mode 100644
index 0000000000..76638afddd
--- /dev/null
+++ b/src/mbgl/programs/hillshade_prepare_program.hpp
@@ -0,0 +1,49 @@
+#pragma once
+
+#include <mbgl/programs/program.hpp>
+#include <mbgl/programs/attributes.hpp>
+#include <mbgl/programs/uniforms.hpp>
+#include <mbgl/shaders/hillshade_prepare.hpp>
+#include <mbgl/util/geometry.hpp>
+
+namespace mbgl {
+
+namespace uniforms {
+MBGL_DEFINE_UNIFORM_VECTOR(uint16_t, 2, u_dimension);
+MBGL_DEFINE_UNIFORM_SCALAR(float, u_maxzoom);
+} // namespace uniforms
+
+class HillshadePrepareProgram : public Program<
+ shaders::hillshade_prepare,
+ gl::Triangle,
+ gl::Attributes<
+ attributes::a_pos,
+ attributes::a_texture_pos>,
+ gl::Uniforms<
+ uniforms::u_matrix,
+ uniforms::u_dimension,
+ uniforms::u_zoom,
+ uniforms::u_maxzoom,
+ uniforms::u_image>,
+ style::Properties<>> {
+public:
+ using Program::Program;
+
+ static LayoutVertex layoutVertex(Point<int16_t> p, Point<uint16_t> t) {
+ return LayoutVertex {
+ {{
+ p.x,
+ p.y
+ }},
+ {{
+ t.x,
+ t.y
+ }}
+ };
+ }
+};
+
+using HillshadePrepareLayoutVertex = HillshadePrepareProgram::LayoutVertex;
+using HillshadePrepareAttributes = HillshadePrepareProgram::Attributes;
+
+} // namespace mbgl
diff --git a/src/mbgl/programs/hillshade_program.cpp b/src/mbgl/programs/hillshade_program.cpp
new file mode 100644
index 0000000000..f054ad4b74
--- /dev/null
+++ b/src/mbgl/programs/hillshade_program.cpp
@@ -0,0 +1,7 @@
+#include <mbgl/programs/hillshade_program.hpp>
+
+namespace mbgl {
+
+static_assert(sizeof(HillshadeLayoutVertex) == 8, "expected HillshadeLayoutVertex size");
+
+} // namespace mbgl
diff --git a/src/mbgl/programs/hillshade_program.hpp b/src/mbgl/programs/hillshade_program.hpp
new file mode 100644
index 0000000000..5f9b4d1c2f
--- /dev/null
+++ b/src/mbgl/programs/hillshade_program.hpp
@@ -0,0 +1,55 @@
+#pragma once
+
+#include <mbgl/programs/program.hpp>
+#include <mbgl/programs/attributes.hpp>
+#include <mbgl/programs/uniforms.hpp>
+#include <mbgl/shaders/hillshade.hpp>
+#include <mbgl/util/geometry.hpp>
+#include <mbgl/style/layers/hillshade_layer_properties.hpp>
+
+namespace mbgl {
+
+namespace uniforms {
+MBGL_DEFINE_UNIFORM_SCALAR(Color, u_shadow);
+MBGL_DEFINE_UNIFORM_SCALAR(Color, u_highlight);
+MBGL_DEFINE_UNIFORM_SCALAR(Color, u_accent);
+MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_light);
+MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_latrange);
+} // namespace uniforms
+
+class HillshadeProgram : public Program<
+ shaders::hillshade,
+ gl::Triangle,
+ gl::Attributes<
+ attributes::a_pos,
+ attributes::a_texture_pos>,
+ gl::Uniforms<
+ uniforms::u_matrix,
+ uniforms::u_image,
+ uniforms::u_highlight,
+ uniforms::u_shadow,
+ uniforms::u_accent,
+ uniforms::u_light,
+ uniforms::u_latrange>,
+ style::HillshadePaintProperties>{
+public:
+ using Program::Program;
+
+ static LayoutVertex layoutVertex(Point<int16_t> p, Point<uint16_t> t) {
+ return LayoutVertex {
+ {{
+ p.x,
+ p.y
+ }},
+ {{
+ t.x,
+ t.y
+ }}
+ };
+ }
+};
+
+using HillshadeLayoutVertex = HillshadeProgram::LayoutVertex;
+using HillshadeAttributes = HillshadeProgram::Attributes;
+
+} // namespace mbgl
diff --git a/src/mbgl/programs/programs.hpp b/src/mbgl/programs/programs.hpp
index 37ced32745..b703323d9c 100644
--- a/src/mbgl/programs/programs.hpp
+++ b/src/mbgl/programs/programs.hpp
@@ -1,9 +1,15 @@
#pragma once
+#include <mbgl/programs/background_program.hpp>
#include <mbgl/programs/circle_program.hpp>
+#include <mbgl/programs/clipping_mask_program.hpp>
#include <mbgl/programs/extrusion_texture_program.hpp>
#include <mbgl/programs/fill_program.hpp>
#include <mbgl/programs/fill_extrusion_program.hpp>
+#include <mbgl/programs/heatmap_program.hpp>
+#include <mbgl/programs/heatmap_texture_program.hpp>
+#include <mbgl/programs/hillshade_program.hpp>
+#include <mbgl/programs/hillshade_prepare_program.hpp>
#include <mbgl/programs/line_program.hpp>
#include <mbgl/programs/raster_program.hpp>
#include <mbgl/programs/symbol_program.hpp>
@@ -16,7 +22,9 @@ namespace mbgl {
class Programs {
public:
Programs(gl::Context& context, const ProgramParameters& programParameters)
- : circle(context, programParameters),
+ : background(context, programParameters),
+ backgroundPattern(context, programParameters),
+ circle(context, programParameters),
extrusionTexture(context, programParameters),
fill(context, programParameters),
fillExtrusion(context, programParameters),
@@ -24,6 +32,10 @@ public:
fillPattern(context, programParameters),
fillOutline(context, programParameters),
fillOutlinePattern(context, programParameters),
+ heatmap(context, programParameters),
+ heatmapTexture(context, programParameters),
+ hillshade(context, programParameters),
+ hillshadePrepare(context, programParameters),
line(context, programParameters),
lineSDF(context, programParameters),
linePattern(context, programParameters),
@@ -32,9 +44,13 @@ public:
symbolIconSDF(context, programParameters),
symbolGlyph(context, programParameters),
debug(context, programParameters),
- collisionBox(context, programParameters) {
+ collisionBox(context, programParameters),
+ collisionCircle(context, programParameters),
+ clippingMask(context, programParameters) {
}
+ BackgroundProgram background;
+ BackgroundPatternProgram backgroundPattern;
ProgramMap<CircleProgram> circle;
ExtrusionTextureProgram extrusionTexture;
ProgramMap<FillProgram> fill;
@@ -43,6 +59,10 @@ public:
ProgramMap<FillPatternProgram> fillPattern;
ProgramMap<FillOutlineProgram> fillOutline;
ProgramMap<FillOutlinePatternProgram> fillOutlinePattern;
+ ProgramMap<HeatmapProgram> heatmap;
+ HeatmapTextureProgram heatmapTexture;
+ HillshadeProgram hillshade;
+ HillshadePrepareProgram hillshadePrepare;
ProgramMap<LineProgram> line;
ProgramMap<LineSDFProgram> lineSDF;
ProgramMap<LinePatternProgram> linePattern;
@@ -53,6 +73,8 @@ public:
DebugProgram debug;
CollisionBoxProgram collisionBox;
+ CollisionCircleProgram collisionCircle;
+ ClippingMaskProgram clippingMask;
};
} // namespace mbgl
diff --git a/src/mbgl/programs/symbol_program.cpp b/src/mbgl/programs/symbol_program.cpp
index 58174ff8a7..84a7a53f1d 100644
--- a/src/mbgl/programs/symbol_program.cpp
+++ b/src/mbgl/programs/symbol_program.cpp
@@ -37,6 +37,7 @@ Values makeValues(const bool isText,
const bool alongLine,
const RenderTile& tile,
const TransformState& state,
+ const float symbolFadeChange,
Args&&... args) {
std::array<float, 2> extrudeScale;
@@ -82,9 +83,8 @@ Values makeValues(const bool isText,
uniforms::u_extrude_scale::Value{ extrudeScale },
uniforms::u_texsize::Value{ texsize },
uniforms::u_texture::Value{ 0 },
- uniforms::u_fadetexture::Value{ 1 },
+ uniforms::u_fade_change::Value{ symbolFadeChange },
uniforms::u_is_text::Value{ isText },
- uniforms::u_collision_y_stretch::Value{ tile.tile.yStretch() },
uniforms::u_camera_to_center_distance::Value{ state.getCameraToCenterDistance() },
uniforms::u_pitch::Value{ state.getPitch() },
uniforms::u_pitch_with_map::Value{ pitchWithMap },
@@ -102,7 +102,8 @@ SymbolIconProgram::uniformValues(const bool isText,
const std::array<float, 2>& pixelsToGLUnits,
const bool alongLine,
const RenderTile& tile,
- const TransformState& state)
+ const TransformState& state,
+ const float symbolFadeChange)
{
return makeValues<SymbolIconProgram::UniformValues>(
isText,
@@ -111,7 +112,8 @@ SymbolIconProgram::uniformValues(const bool isText,
pixelsToGLUnits,
alongLine,
tile,
- state
+ state,
+ symbolFadeChange
);
}
@@ -124,6 +126,7 @@ typename SymbolSDFProgram<PaintProperties>::UniformValues SymbolSDFProgram<Paint
const bool alongLine,
const RenderTile& tile,
const TransformState& state,
+ const float symbolFadeChange,
const SymbolSDFPart part)
{
const float gammaScale = (values.pitchAlignment == AlignmentType::Map
@@ -138,6 +141,7 @@ typename SymbolSDFProgram<PaintProperties>::UniformValues SymbolSDFProgram<Paint
alongLine,
tile,
state,
+ symbolFadeChange,
uniforms::u_gamma_scale::Value{ gammaScale },
uniforms::u_is_halo::Value{ part == SymbolSDFPart::Halo }
);
diff --git a/src/mbgl/programs/symbol_program.hpp b/src/mbgl/programs/symbol_program.hpp
index a7abf94f56..a14afac702 100644
--- a/src/mbgl/programs/symbol_program.hpp
+++ b/src/mbgl/programs/symbol_program.hpp
@@ -75,18 +75,24 @@ struct SymbolLayoutAttributes : gl::Attributes<
};
struct SymbolDynamicLayoutAttributes : gl::Attributes<attributes::a_projected_pos> {
- static Vertex vertex(Point<float> anchorPoint, float labelAngle, float labelminzoom) {
+ static Vertex vertex(Point<float> anchorPoint, float labelAngle) {
return Vertex {
{{
anchorPoint.x,
anchorPoint.y,
- static_cast<float>(mbgl::attributes::packUint8Pair(
- static_cast<uint8_t>(std::fmod(labelAngle + 2 * M_PI, 2 * M_PI) / (2 * M_PI) * 255),
- static_cast<uint8_t>(labelminzoom * 10)))
+ labelAngle
}}
};
}
};
+
+struct SymbolOpacityAttributes : gl::Attributes<attributes::a_fade_opacity> {
+ static Vertex vertex(bool placed, float opacity) {
+ return Vertex {
+ {{ static_cast<uint8_t>((static_cast<uint8_t>(opacity * 127) << 1) | static_cast<uint8_t>(placed)) }}
+ };
+ }
+};
struct ZoomEvaluatedSize {
bool isZoomConstant;
@@ -128,23 +134,6 @@ public:
}
};
-// Return the smallest range of stops that covers the interval [lowerZoom, upperZoom]
-template <class Stops>
-Range<float> getCoveringStops(Stops s, float lowerZoom, float upperZoom) {
- assert(!s.stops.empty());
- auto minIt = s.stops.lower_bound(lowerZoom);
- auto maxIt = s.stops.lower_bound(upperZoom);
-
- // lower_bound yields first element >= lowerZoom, but we want the *last*
- // element <= lowerZoom, so if we found a stop > lowerZoom, back up by one.
- if (minIt != s.stops.begin() && minIt != s.stops.end() && minIt->first > lowerZoom) {
- minIt--;
- }
- return Range<float> {
- minIt == s.stops.end() ? s.stops.rbegin()->first : minIt->first,
- maxIt == s.stops.end() ? s.stops.rbegin()->first : maxIt->first
- };
-}
class ConstantSymbolSizeBinder final : public SymbolSizeBinder {
public:
@@ -155,19 +144,12 @@ public:
: layoutSize(defaultValue) {}
ConstantSymbolSizeBinder(const float tileZoom, const style::CameraFunction<float>& function_, const float /*defaultValue*/)
- : layoutSize(function_.evaluate(tileZoom + 1)) {
- function_.stops.match(
- [&] (const style::ExponentialStops<float>& stops) {
- const auto& zoomLevels = getCoveringStops(stops, tileZoom, tileZoom + 1);
- coveringRanges = std::make_tuple(
- zoomLevels,
- Range<float> { function_.evaluate(zoomLevels.min), function_.evaluate(zoomLevels.max) }
- );
- functionInterpolationBase = stops.base;
- },
- [&] (const style::IntervalStops<float>&) {
- function = function_;
- }
+ : layoutSize(function_.evaluate(tileZoom + 1)),
+ function(function_) {
+ const Range<float> zoomLevels = function_.getCoveringStops(tileZoom, tileZoom + 1);
+ coveringRanges = std::make_tuple(
+ zoomLevels,
+ Range<float> { function_.evaluate(zoomLevels.min), function_.evaluate(zoomLevels.max) }
);
}
@@ -185,7 +167,7 @@ public:
const Range<float>& zoomLevels = std::get<0>(*coveringRanges);
const Range<float>& sizeLevels = std::get<1>(*coveringRanges);
float t = util::clamp(
- util::interpolationFactor(*functionInterpolationBase, zoomLevels, currentZoom),
+ function->interpolationFactor(zoomLevels, currentZoom),
0.0f, 1.0f
);
size = sizeLevels.min + t * (sizeLevels.max - sizeLevels.min);
@@ -198,10 +180,7 @@ public:
}
float layoutSize;
- // used for exponential functions
optional<std::tuple<Range<float>, Range<float>>> coveringRanges;
- optional<float> functionInterpolationBase;
- // used for interval functions
optional<style::CameraFunction<float>> function;
};
@@ -226,7 +205,7 @@ public:
return { true, false, unused, unused, unused };
}
- const style::SourceFunction<float>& function;
+ style::SourceFunction<float> function;
const float defaultValue;
};
@@ -237,9 +216,7 @@ public:
: function(function_),
defaultValue(defaultValue_),
layoutZoom(tileZoom + 1),
- coveringZoomStops(function.stops.match(
- [&] (const auto& stops) {
- return getCoveringStops(stops, tileZoom, tileZoom + 1); }))
+ coveringZoomStops(function.getCoveringStops(tileZoom, tileZoom + 1))
{}
Range<float> getVertexSizeData(const GeometryTileFeature& feature) override {
@@ -251,7 +228,7 @@ public:
ZoomEvaluatedSize evaluateForZoom(float currentZoom) const override {
float sizeInterpolationT = util::clamp(
- util::interpolationFactor(1.0f, coveringZoomStops, currentZoom),
+ function.interpolationFactor(coveringZoomStops, currentZoom),
0.0f, 1.0f
);
@@ -259,7 +236,7 @@ public:
return { false, false, sizeInterpolationT, unused, unused };
}
- const style::CompositeFunction<float>& function;
+ style::CompositeFunction<float> function;
const float defaultValue;
float layoutZoom;
Range<float> coveringZoomStops;
@@ -276,7 +253,7 @@ public:
using LayoutAttributes = LayoutAttrs;
using LayoutVertex = typename LayoutAttributes::Vertex;
- using LayoutAndSizeAttributes = gl::ConcatenateAttributes<LayoutAttributes, SymbolDynamicLayoutAttributes>;
+ using LayoutAndSizeAttributes = gl::ConcatenateAttributes<LayoutAttributes, gl::ConcatenateAttributes<SymbolDynamicLayoutAttributes, SymbolOpacityAttributes>>;
using PaintProperties = PaintProps;
using PaintPropertyBinders = typename PaintProperties::Binders;
@@ -310,6 +287,7 @@ public:
const UniformValues& uniformValues,
const gl::VertexBuffer<LayoutVertex>& layoutVertexBuffer,
const gl::VertexBuffer<SymbolDynamicLayoutAttributes::Vertex>& dynamicLayoutVertexBuffer,
+ const gl::VertexBuffer<SymbolOpacityAttributes::Vertex>& opacityVertexBuffer,
const SymbolSizeBinder& symbolSizeBinder,
const gl::IndexBuffer<DrawMode>& indexBuffer,
const SegmentVector<Attributes>& segments,
@@ -323,8 +301,12 @@ public:
typename Attributes::Bindings allAttributeBindings = LayoutAttributes::bindings(layoutVertexBuffer)
.concat(SymbolDynamicLayoutAttributes::bindings(dynamicLayoutVertexBuffer))
+ .concat(SymbolOpacityAttributes::bindings(opacityVertexBuffer))
.concat(paintPropertyBinders.attributeBindings(currentProperties));
+ assert(layoutVertexBuffer.vertexCount == dynamicLayoutVertexBuffer.vertexCount &&
+ layoutVertexBuffer.vertexCount == opacityVertexBuffer.vertexCount);
+
for (auto& segment : segments) {
auto vertexArrayIt = segment.vertexArrays.find(layerID);
@@ -359,9 +341,8 @@ class SymbolIconProgram : public SymbolProgram<
uniforms::u_extrude_scale,
uniforms::u_texsize,
uniforms::u_texture,
- uniforms::u_fadetexture,
+ uniforms::u_fade_change,
uniforms::u_is_text,
- uniforms::u_collision_y_stretch,
uniforms::u_camera_to_center_distance,
uniforms::u_pitch,
uniforms::u_pitch_with_map,
@@ -379,7 +360,8 @@ public:
const std::array<float, 2>& pixelsToGLUnits,
const bool alongLine,
const RenderTile&,
- const TransformState&);
+ const TransformState&,
+ const float symbolFadeChange);
};
enum class SymbolSDFPart {
@@ -399,9 +381,8 @@ class SymbolSDFProgram : public SymbolProgram<
uniforms::u_extrude_scale,
uniforms::u_texsize,
uniforms::u_texture,
- uniforms::u_fadetexture,
+ uniforms::u_fade_change,
uniforms::u_is_text,
- uniforms::u_collision_y_stretch,
uniforms::u_camera_to_center_distance,
uniforms::u_pitch,
uniforms::u_pitch_with_map,
@@ -423,9 +404,8 @@ public:
uniforms::u_extrude_scale,
uniforms::u_texsize,
uniforms::u_texture,
- uniforms::u_fadetexture,
+ uniforms::u_fade_change,
uniforms::u_is_text,
- uniforms::u_collision_y_stretch,
uniforms::u_camera_to_center_distance,
uniforms::u_pitch,
uniforms::u_pitch_with_map,
@@ -449,6 +429,7 @@ public:
const bool alongLine,
const RenderTile&,
const TransformState&,
+ const float SymbolFadeChange,
const SymbolSDFPart);
};
diff --git a/src/mbgl/programs/uniforms.hpp b/src/mbgl/programs/uniforms.hpp
index 285d243251..071a27c808 100644
--- a/src/mbgl/programs/uniforms.hpp
+++ b/src/mbgl/programs/uniforms.hpp
@@ -36,9 +36,15 @@ MBGL_DEFINE_UNIFORM_SCALAR(Size, u_world);
MBGL_DEFINE_UNIFORM_SCALAR(Size, u_texsize);
MBGL_DEFINE_UNIFORM_SCALAR(bool, u_pitch_with_map);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_camera_to_center_distance);
+MBGL_DEFINE_UNIFORM_SCALAR(float, u_fade_change);
+MBGL_DEFINE_UNIFORM_SCALAR(float, u_weight);
MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_extrude_scale);
+namespace heatmap {
+MBGL_DEFINE_UNIFORM_SCALAR(float, u_extrude_scale);
+} // namespace heatmap
+
MBGL_DEFINE_UNIFORM_VECTOR(uint16_t, 2, u_pattern_tl_a);
MBGL_DEFINE_UNIFORM_VECTOR(uint16_t, 2, u_pattern_br_a);
MBGL_DEFINE_UNIFORM_VECTOR(uint16_t, 2, u_pattern_tl_b);
@@ -54,6 +60,7 @@ MBGL_DEFINE_UNIFORM_SCALAR(gl::TextureUnit, u_fadetexture);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_scale_a);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_scale_b);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_tile_units_to_pixels);
+MBGL_DEFINE_UNIFORM_SCALAR(float, u_overscale_factor);
} // namespace uniforms
} // namespace mbgl
diff --git a/src/mbgl/renderer/buckets/circle_bucket.cpp b/src/mbgl/renderer/buckets/circle_bucket.cpp
index 04126990b3..c442b661de 100644
--- a/src/mbgl/renderer/buckets/circle_bucket.cpp
+++ b/src/mbgl/renderer/buckets/circle_bucket.cpp
@@ -49,7 +49,7 @@ void CircleBucket::addFeature(const GeometryTileFeature& feature,
// Do not include points that are outside the tile boundaries.
// Include all points in Still mode. You need to include points from
// neighbouring tiles so that they are not clipped at tile boundaries.
- if ((mode != MapMode::Still) &&
+ if ((mode == MapMode::Continuous) &&
(x < 0 || x >= util::EXTENT || y < 0 || y >= util::EXTENT)) continue;
if (segments.empty() || segments.back().vertexLength + vertexLength > std::numeric_limits<uint16_t>::max()) {
@@ -108,8 +108,9 @@ float CircleBucket::getQueryRadius(const RenderLayer& layer) const {
auto circleLayer = layer.as<RenderCircleLayer>();
float radius = get<CircleRadius>(*circleLayer, paintPropertyBinders);
+ float stroke = get<CircleStrokeWidth>(*circleLayer, paintPropertyBinders);
auto translate = circleLayer->evaluated.get<CircleTranslate>();
- return radius + util::length(translate[0], translate[1]);
+ return radius + stroke + util::length(translate[0], translate[1]);
}
} // namespace mbgl
diff --git a/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp b/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp
index 7f53326fe1..5e2c937091 100644
--- a/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp
+++ b/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp
@@ -101,13 +101,17 @@ void FillExtrusionBucket::addFeature(const GeometryTileFeature& feature,
const auto d2 = convertPoint<double>(p2);
const Point<double> perp = util::unit(util::perp(d1 - d2));
+ const auto dist = util::dist<int16_t>(d1, d2);
+ if (dist > std::numeric_limits<int16_t>::max()) {
+ edgeDistance = 0;
+ }
vertices.emplace_back(
FillExtrusionProgram::layoutVertex(p1, perp.x, perp.y, 0, 0, edgeDistance));
vertices.emplace_back(
FillExtrusionProgram::layoutVertex(p1, perp.x, perp.y, 0, 1, edgeDistance));
- edgeDistance += util::dist<int16_t>(d1, d2);
+ edgeDistance += dist;
vertices.emplace_back(
FillExtrusionProgram::layoutVertex(p2, perp.x, perp.y, 0, 0, edgeDistance));
diff --git a/src/mbgl/renderer/buckets/heatmap_bucket.cpp b/src/mbgl/renderer/buckets/heatmap_bucket.cpp
new file mode 100644
index 0000000000..a185e04ad2
--- /dev/null
+++ b/src/mbgl/renderer/buckets/heatmap_bucket.cpp
@@ -0,0 +1,98 @@
+#include <mbgl/renderer/buckets/heatmap_bucket.hpp>
+#include <mbgl/renderer/bucket_parameters.hpp>
+#include <mbgl/programs/heatmap_program.hpp>
+#include <mbgl/style/layers/heatmap_layer_impl.hpp>
+#include <mbgl/renderer/layers/render_heatmap_layer.hpp>
+#include <mbgl/util/constants.hpp>
+#include <mbgl/util/math.hpp>
+
+namespace mbgl {
+
+using namespace style;
+
+HeatmapBucket::HeatmapBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers)
+ : mode(parameters.mode) {
+ for (const auto& layer : layers) {
+ paintPropertyBinders.emplace(
+ std::piecewise_construct,
+ std::forward_as_tuple(layer->getID()),
+ std::forward_as_tuple(
+ layer->as<RenderHeatmapLayer>()->evaluated,
+ parameters.tileID.overscaledZ));
+ }
+}
+
+void HeatmapBucket::upload(gl::Context& context) {
+ vertexBuffer = context.createVertexBuffer(std::move(vertices));
+ indexBuffer = context.createIndexBuffer(std::move(triangles));
+
+ for (auto& pair : paintPropertyBinders) {
+ pair.second.upload(context);
+ }
+
+ uploaded = true;
+}
+
+bool HeatmapBucket::hasData() const {
+ return !segments.empty();
+}
+
+void HeatmapBucket::addFeature(const GeometryTileFeature& feature,
+ const GeometryCollection& geometry) {
+ constexpr const uint16_t vertexLength = 4;
+
+ for (auto& points : geometry) {
+ for(auto& point : points) {
+ auto x = point.x;
+ auto y = point.y;
+
+ // Do not include points that are outside the tile boundaries.
+ // Include all points in Still mode. You need to include points from
+ // neighbouring tiles so that they are not clipped at tile boundaries.
+ if ((mode == MapMode::Continuous) &&
+ (x < 0 || x >= util::EXTENT || y < 0 || y >= util::EXTENT)) continue;
+
+ if (segments.empty() || segments.back().vertexLength + vertexLength > std::numeric_limits<uint16_t>::max()) {
+ // Move to a new segments because the old one can't hold the geometry.
+ segments.emplace_back(vertices.vertexSize(), triangles.indexSize());
+ }
+
+ // this geometry will be of the Point type, and we'll derive
+ // two triangles from it.
+ //
+ // ┌─────────┐
+ // │ 4 3 │
+ // │ │
+ // │ 1 2 │
+ // └─────────┘
+ //
+ vertices.emplace_back(HeatmapProgram::vertex(point, -1, -1)); // 1
+ vertices.emplace_back(HeatmapProgram::vertex(point, 1, -1)); // 2
+ vertices.emplace_back(HeatmapProgram::vertex(point, 1, 1)); // 3
+ vertices.emplace_back(HeatmapProgram::vertex(point, -1, 1)); // 4
+
+ auto& segment = segments.back();
+ assert(segment.vertexLength <= std::numeric_limits<uint16_t>::max());
+ uint16_t index = segment.vertexLength;
+
+ // 1, 2, 3
+ // 1, 4, 3
+ triangles.emplace_back(index, index + 1, index + 2);
+ triangles.emplace_back(index, index + 3, index + 2);
+
+ segment.vertexLength += vertexLength;
+ segment.indexLength += 6;
+ }
+ }
+
+ for (auto& pair : paintPropertyBinders) {
+ pair.second.populateVertexVectors(feature, vertices.vertexSize());
+ }
+}
+
+float HeatmapBucket::getQueryRadius(const RenderLayer& layer) const {
+ (void)layer;
+ return 0;
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/buckets/heatmap_bucket.hpp b/src/mbgl/renderer/buckets/heatmap_bucket.hpp
new file mode 100644
index 0000000000..3b9f1edb81
--- /dev/null
+++ b/src/mbgl/renderer/buckets/heatmap_bucket.hpp
@@ -0,0 +1,40 @@
+#pragma once
+
+#include <mbgl/renderer/bucket.hpp>
+#include <mbgl/map/mode.hpp>
+#include <mbgl/tile/geometry_tile_data.hpp>
+#include <mbgl/gl/vertex_buffer.hpp>
+#include <mbgl/gl/index_buffer.hpp>
+#include <mbgl/programs/segment.hpp>
+#include <mbgl/programs/heatmap_program.hpp>
+#include <mbgl/style/layers/heatmap_layer_properties.hpp>
+
+namespace mbgl {
+
+class BucketParameters;
+
+class HeatmapBucket : public Bucket {
+public:
+ HeatmapBucket(const BucketParameters&, const std::vector<const RenderLayer*>&);
+
+ void addFeature(const GeometryTileFeature&,
+ const GeometryCollection&) override;
+ bool hasData() const override;
+
+ void upload(gl::Context&) override;
+
+ float getQueryRadius(const RenderLayer&) const override;
+
+ gl::VertexVector<HeatmapLayoutVertex> vertices;
+ gl::IndexVector<gl::Triangles> triangles;
+ SegmentVector<HeatmapAttributes> segments;
+
+ optional<gl::VertexBuffer<HeatmapLayoutVertex>> vertexBuffer;
+ optional<gl::IndexBuffer<gl::Triangles>> indexBuffer;
+
+ std::map<std::string, HeatmapProgram::PaintPropertyBinders> paintPropertyBinders;
+
+ const MapMode mode;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/buckets/hillshade_bucket.cpp b/src/mbgl/renderer/buckets/hillshade_bucket.cpp
new file mode 100644
index 0000000000..00b9536894
--- /dev/null
+++ b/src/mbgl/renderer/buckets/hillshade_bucket.cpp
@@ -0,0 +1,113 @@
+#include <mbgl/renderer/buckets/hillshade_bucket.hpp>
+#include <mbgl/renderer/layers/render_hillshade_layer.hpp>
+#include <mbgl/programs/hillshade_program.hpp>
+#include <mbgl/programs/hillshade_prepare_program.hpp>
+#include <mbgl/gl/context.hpp>
+
+namespace mbgl {
+
+using namespace style;
+
+HillshadeBucket::HillshadeBucket(PremultipliedImage&& image_, Tileset::DEMEncoding encoding): demdata(image_, encoding) {
+}
+
+HillshadeBucket::HillshadeBucket(DEMData&& demdata_) : demdata(std::move(demdata_)) {
+}
+
+const DEMData& HillshadeBucket::getDEMData() const {
+ return demdata;
+}
+
+DEMData& HillshadeBucket::getDEMData() {
+ return demdata;
+}
+
+void HillshadeBucket::upload(gl::Context& context) {
+ if (!hasData()) {
+ return;
+ }
+
+
+ const PremultipliedImage* image = demdata.getImage();
+ dem = context.createTexture(*image);
+
+ if (!segments.empty()) {
+ vertexBuffer = context.createVertexBuffer(std::move(vertices));
+ indexBuffer = context.createIndexBuffer(std::move(indices));
+ }
+ uploaded = true;
+}
+
+void HillshadeBucket::clear() {
+ vertexBuffer = {};
+ indexBuffer = {};
+ segments.clear();
+ vertices.clear();
+ indices.clear();
+
+ uploaded = false;
+}
+
+void HillshadeBucket::setMask(TileMask&& mask_) {
+ if (mask == mask_) {
+ return;
+ }
+
+ mask = std::move(mask_);
+ clear();
+
+ if (mask == TileMask{ { 0, 0, 0 } }) {
+ // We want to render the full tile, and keeping the segments/vertices/indices empty means
+ // using the global shared buffers for covering the entire tile.
+ return;
+ }
+
+ // Create a new segment so that we will upload (empty) buffers even when there is nothing to
+ // draw for this tile.
+ segments.emplace_back(0, 0);
+
+ constexpr const uint16_t vertexLength = 4;
+
+ // Create the vertex buffer for the specified tile mask.
+ for (const auto& id : mask) {
+ // Create a quad for every masked tile.
+ const int32_t vertexExtent = util::EXTENT >> id.z;
+
+ const Point<int16_t> tlVertex = { static_cast<int16_t>(id.x * vertexExtent),
+ static_cast<int16_t>(id.y * vertexExtent) };
+ const Point<int16_t> brVertex = { static_cast<int16_t>(tlVertex.x + vertexExtent),
+ static_cast<int16_t>(tlVertex.y + vertexExtent) };
+
+ if (segments.back().vertexLength + vertexLength > std::numeric_limits<uint16_t>::max()) {
+ // Move to a new segments because the old one can't hold the geometry.
+ segments.emplace_back(vertices.vertexSize(), indices.indexSize());
+ }
+
+ vertices.emplace_back(
+ HillshadeProgram::layoutVertex({ tlVertex.x, tlVertex.y }, { static_cast<uint16_t>(tlVertex.x), static_cast<uint16_t>(tlVertex.y) }));
+ vertices.emplace_back(
+ HillshadeProgram::layoutVertex({ brVertex.x, tlVertex.y }, { static_cast<uint16_t>(brVertex.x), static_cast<uint16_t>(tlVertex.y) }));
+ vertices.emplace_back(
+ HillshadeProgram::layoutVertex({ tlVertex.x, brVertex.y }, { static_cast<uint16_t>(tlVertex.x), static_cast<uint16_t>(brVertex.y) }));
+ vertices.emplace_back(
+ HillshadeProgram::layoutVertex({ brVertex.x, brVertex.y }, { static_cast<uint16_t>(brVertex.x), static_cast<uint16_t>(brVertex.y) }));
+
+ auto& segment = segments.back();
+ assert(segment.vertexLength <= std::numeric_limits<uint16_t>::max());
+ const uint16_t offset = segment.vertexLength;
+
+ // 0, 1, 2
+ // 1, 2, 3
+ indices.emplace_back(offset, offset + 1, offset + 2);
+ indices.emplace_back(offset + 1, offset + 2, offset + 3);
+
+ segment.vertexLength += vertexLength;
+ segment.indexLength += 6;
+ }
+}
+
+bool HillshadeBucket::hasData() const {
+ return demdata.getImage()->valid();
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/buckets/hillshade_bucket.hpp b/src/mbgl/renderer/buckets/hillshade_bucket.hpp
new file mode 100644
index 0000000000..5335f7ceda
--- /dev/null
+++ b/src/mbgl/renderer/buckets/hillshade_bucket.hpp
@@ -0,0 +1,59 @@
+#pragma once
+
+#include <mbgl/gl/index_buffer.hpp>
+#include <mbgl/gl/texture.hpp>
+#include <mbgl/gl/vertex_buffer.hpp>
+#include <mbgl/programs/hillshade_program.hpp>
+#include <mbgl/programs/hillshade_prepare_program.hpp>
+#include <mbgl/renderer/bucket.hpp>
+#include <mbgl/renderer/tile_mask.hpp>
+#include <mbgl/geometry/dem_data.hpp>
+#include <mbgl/util/tileset.hpp>
+#include <mbgl/util/image.hpp>
+#include <mbgl/util/mat4.hpp>
+#include <mbgl/util/optional.hpp>
+
+namespace mbgl {
+
+class HillshadeBucket : public Bucket {
+public:
+ HillshadeBucket(PremultipliedImage&&, Tileset::DEMEncoding encoding);
+ HillshadeBucket(std::shared_ptr<PremultipliedImage>, Tileset::DEMEncoding encoding);
+ HillshadeBucket(DEMData&&);
+
+
+ void upload(gl::Context&) override;
+ bool hasData() const override;
+
+ void clear();
+ void setMask(TileMask&&);
+
+ optional<gl::Texture> dem;
+ optional<gl::Texture> texture;
+
+ TileMask mask{ { 0, 0, 0 } };
+
+ const DEMData& getDEMData() const;
+ DEMData& getDEMData();
+
+ bool isPrepared() const {
+ return prepared;
+ }
+
+ void setPrepared (bool preparedState) {
+ prepared = preparedState;
+ }
+
+ // Raster-DEM Tile Sources use the default buffers from Painter
+ gl::VertexVector<HillshadeLayoutVertex> vertices;
+ gl::IndexVector<gl::Triangles> indices;
+ SegmentVector<HillshadeAttributes> segments;
+
+ optional<gl::VertexBuffer<HillshadeLayoutVertex>> vertexBuffer;
+ optional<gl::IndexBuffer<gl::Triangles>> indexBuffer;
+private:
+ DEMData demdata;
+ bool prepared = false;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/buckets/symbol_bucket.cpp b/src/mbgl/renderer/buckets/symbol_bucket.cpp
index a3f71f1f6e..60e8a0b504 100644
--- a/src/mbgl/renderer/buckets/symbol_bucket.cpp
+++ b/src/mbgl/renderer/buckets/symbol_bucket.cpp
@@ -16,10 +16,14 @@ SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated layo
const style::DataDrivenPropertyValue<float>& iconSize,
float zoom,
bool sdfIcons_,
- bool iconsNeedLinear_)
+ bool iconsNeedLinear_,
+ bool sortFeaturesByY_,
+ const std::vector<SymbolInstance>&& symbolInstances_)
: layout(std::move(layout_)),
sdfIcons(sdfIcons_),
iconsNeedLinear(iconsNeedLinear_ || iconSize.isDataDriven() || !iconSize.isZoomConstant()),
+ sortFeaturesByY(sortFeaturesByY_),
+ symbolInstances(std::move(symbolInstances_)),
textSizeBinder(SymbolSizeBinder::create(zoom, textSize, TextSize::defaultValue())),
iconSizeBinder(SymbolSizeBinder::create(zoom, iconSize, IconSize::defaultValue())) {
@@ -36,28 +40,84 @@ SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated layo
void SymbolBucket::upload(gl::Context& context) {
if (hasTextData()) {
- text.vertexBuffer = context.createVertexBuffer(std::move(text.vertices));
- text.dynamicVertexBuffer = context.createVertexBuffer(std::move(text.dynamicVertices), gl::BufferUsage::StreamDraw);
- text.indexBuffer = context.createIndexBuffer(std::move(text.triangles));
+ if (!staticUploaded) {
+ text.indexBuffer = context.createIndexBuffer(std::move(text.triangles), sortFeaturesByY ? gl::BufferUsage::StreamDraw : gl::BufferUsage::StaticDraw);
+ text.vertexBuffer = context.createVertexBuffer(std::move(text.vertices));
+ } else if (!sortUploaded) {
+ context.updateIndexBuffer(*text.indexBuffer, std::move(text.triangles));
+ }
+
+ if (!dynamicUploaded) {
+ text.dynamicVertexBuffer = context.createVertexBuffer(std::move(text.dynamicVertices), gl::BufferUsage::StreamDraw);
+ }
+ if (!placementChangesUploaded) {
+ if (!text.opacityVertexBuffer) {
+ text.opacityVertexBuffer = context.createVertexBuffer(std::move(text.opacityVertices), gl::BufferUsage::StreamDraw);
+ } else {
+ context.updateVertexBuffer(*text.opacityVertexBuffer, std::move(text.opacityVertices));
+ }
+ }
}
if (hasIconData()) {
- icon.vertexBuffer = context.createVertexBuffer(std::move(icon.vertices));
- icon.dynamicVertexBuffer = context.createVertexBuffer(std::move(icon.dynamicVertices), gl::BufferUsage::StreamDraw);
- icon.indexBuffer = context.createIndexBuffer(std::move(icon.triangles));
+ if (!staticUploaded) {
+ icon.indexBuffer = context.createIndexBuffer(std::move(icon.triangles), sortFeaturesByY ? gl::BufferUsage::StreamDraw : gl::BufferUsage::StaticDraw);
+ icon.vertexBuffer = context.createVertexBuffer(std::move(icon.vertices));
+ } else if (!sortUploaded) {
+ context.updateIndexBuffer(*icon.indexBuffer, std::move(icon.triangles));
+ }
+ if (!dynamicUploaded) {
+ icon.dynamicVertexBuffer = context.createVertexBuffer(std::move(icon.dynamicVertices), gl::BufferUsage::StreamDraw);
+ }
+ if (!placementChangesUploaded) {
+ if (!icon.opacityVertexBuffer) {
+ icon.opacityVertexBuffer = context.createVertexBuffer(std::move(icon.opacityVertices), gl::BufferUsage::StreamDraw);
+ } else {
+ context.updateVertexBuffer(*icon.opacityVertexBuffer, std::move(icon.opacityVertices));
+ }
+ }
}
- if (!collisionBox.vertices.empty()) {
- collisionBox.vertexBuffer = context.createVertexBuffer(std::move(collisionBox.vertices));
- collisionBox.indexBuffer = context.createIndexBuffer(std::move(collisionBox.lines));
+ if (hasCollisionBoxData()) {
+ if (!staticUploaded) {
+ collisionBox.indexBuffer = context.createIndexBuffer(std::move(collisionBox.lines));
+ collisionBox.vertexBuffer = context.createVertexBuffer(std::move(collisionBox.vertices));
+ }
+ if (!placementChangesUploaded) {
+ if (!collisionBox.dynamicVertexBuffer) {
+ collisionBox.dynamicVertexBuffer = context.createVertexBuffer(std::move(collisionBox.dynamicVertices), gl::BufferUsage::StreamDraw);
+ } else {
+ context.updateVertexBuffer(*collisionBox.dynamicVertexBuffer, std::move(collisionBox.dynamicVertices));
+ }
+ }
}
-
- for (auto& pair : paintPropertyBinders) {
- pair.second.first.upload(context);
- pair.second.second.upload(context);
+
+ if (hasCollisionCircleData()) {
+ if (!staticUploaded) {
+ collisionCircle.indexBuffer = context.createIndexBuffer(std::move(collisionCircle.triangles));
+ collisionCircle.vertexBuffer = context.createVertexBuffer(std::move(collisionCircle.vertices));
+ }
+ if (!placementChangesUploaded) {
+ if (!collisionCircle.dynamicVertexBuffer) {
+ collisionCircle.dynamicVertexBuffer = context.createVertexBuffer(std::move(collisionCircle.dynamicVertices), gl::BufferUsage::StreamDraw);
+ } else {
+ context.updateVertexBuffer(*collisionCircle.dynamicVertexBuffer, std::move(collisionCircle.dynamicVertices));
+ }
+ }
}
+ if (!staticUploaded) {
+ for (auto& pair : paintPropertyBinders) {
+ pair.second.first.upload(context);
+ pair.second.second.upload(context);
+ }
+ }
+
uploaded = true;
+ staticUploaded = true;
+ placementChangesUploaded = true;
+ dynamicUploaded = true;
+ sortUploaded = true;
}
bool SymbolBucket::hasData() const {
@@ -76,4 +136,83 @@ bool SymbolBucket::hasCollisionBoxData() const {
return !collisionBox.segments.empty();
}
+bool SymbolBucket::hasCollisionCircleData() const {
+ return !collisionCircle.segments.empty();
+}
+
+void SymbolBucket::updateOpacity() {
+ placementChangesUploaded = false;
+ uploaded = false;
+}
+
+void addPlacedSymbol(gl::IndexVector<gl::Triangles>& triangles, const PlacedSymbol& placedSymbol) {
+ auto endIndex = placedSymbol.vertexStartIndex + placedSymbol.glyphOffsets.size() * 4;
+ for (auto vertexIndex = placedSymbol.vertexStartIndex; vertexIndex < endIndex; vertexIndex += 4) {
+ triangles.emplace_back(vertexIndex + 0, vertexIndex + 1, vertexIndex + 2);
+ triangles.emplace_back(vertexIndex + 1, vertexIndex + 2, vertexIndex + 3);
+ }
+}
+
+void SymbolBucket::sortFeatures(const float angle) {
+ if (!sortFeaturesByY) {
+ return;
+ }
+
+ if (sortedAngle && *sortedAngle == angle) {
+ return;
+ }
+
+ sortedAngle = angle;
+
+ // The current approach to sorting doesn't sort across segments so don't try.
+ // Sorting within segments separately seemed not to be worth the complexity.
+ if (text.segments.size() > 1 || icon.segments.size() > 1) {
+ return;
+ }
+
+ sortUploaded = false;
+ uploaded = false;
+
+ // If the symbols are allowed to overlap sort them by their vertical screen position.
+ // The index array buffer is rewritten to reference the (unchanged) vertices in the
+ // sorted order.
+
+ // To avoid sorting the actual symbolInstance array we sort an array of indexes.
+ std::vector<size_t> symbolInstanceIndexes;
+ symbolInstanceIndexes.reserve(symbolInstances.size());
+ for (size_t i = 0; i < symbolInstances.size(); i++) {
+ symbolInstanceIndexes.push_back(i);
+ }
+
+ const float sin = std::sin(angle);
+ const float cos = std::cos(angle);
+
+ std::sort(symbolInstanceIndexes.begin(), symbolInstanceIndexes.end(), [sin, cos, this](size_t &aIndex, size_t &bIndex) {
+ const SymbolInstance& a = symbolInstances[aIndex];
+ const SymbolInstance& b = symbolInstances[bIndex];
+ const int32_t aRotated = sin * a.anchor.point.x + cos * a.anchor.point.y;
+ const int32_t bRotated = sin * b.anchor.point.x + cos * b.anchor.point.y;
+ return aRotated != bRotated ?
+ aRotated < bRotated :
+ a.index > b.index;
+ });
+
+ text.triangles.clear();
+ icon.triangles.clear();
+
+ for (auto i : symbolInstanceIndexes) {
+ const SymbolInstance& symbolInstance = symbolInstances[i];
+
+ if (symbolInstance.placedTextIndex) {
+ addPlacedSymbol(text.triangles, text.placedSymbols[*symbolInstance.placedTextIndex]);
+ }
+ if (symbolInstance.placedVerticalTextIndex) {
+ addPlacedSymbol(text.triangles, text.placedSymbols[*symbolInstance.placedVerticalTextIndex]);
+ }
+ if (symbolInstance.placedIconIndex) {
+ addPlacedSymbol(icon.triangles, icon.placedSymbols[*symbolInstance.placedIconIndex]);
+ }
+ }
+}
+
} // namespace mbgl
diff --git a/src/mbgl/renderer/buckets/symbol_bucket.hpp b/src/mbgl/renderer/buckets/symbol_bucket.hpp
index 32f976bcb2..ed8afb052c 100644
--- a/src/mbgl/renderer/buckets/symbol_bucket.hpp
+++ b/src/mbgl/renderer/buckets/symbol_bucket.hpp
@@ -10,6 +10,7 @@
#include <mbgl/text/glyph_range.hpp>
#include <mbgl/style/layers/symbol_layer_properties.hpp>
#include <mbgl/layout/symbol_feature.hpp>
+#include <mbgl/layout/symbol_instance.hpp>
#include <vector>
@@ -18,18 +19,22 @@ namespace mbgl {
class PlacedSymbol {
public:
PlacedSymbol(Point<float> anchorPoint_, uint16_t segment_, float lowerSize_, float upperSize_,
- std::array<float, 2> lineOffset_, float placementZoom_, bool useVerticalMode_, GeometryCoordinates line_) :
+ std::array<float, 2> lineOffset_, WritingModeType writingModes_, GeometryCoordinates line_, std::vector<float> tileDistances_) :
anchorPoint(anchorPoint_), segment(segment_), lowerSize(lowerSize_), upperSize(upperSize_),
- lineOffset(lineOffset_), placementZoom(placementZoom_), useVerticalMode(useVerticalMode_), line(std::move(line_)) {}
+ lineOffset(lineOffset_), writingModes(writingModes_), line(std::move(line_)), tileDistances(std::move(tileDistances_)), hidden(false), vertexStartIndex(0)
+ {
+ }
Point<float> anchorPoint;
uint16_t segment;
float lowerSize;
float upperSize;
std::array<float, 2> lineOffset;
- float placementZoom;
- bool useVerticalMode;
+ WritingModeType writingModes;
GeometryCoordinates line;
+ std::vector<float> tileDistances;
std::vector<float> glyphOffsets;
+ bool hidden;
+ size_t vertexStartIndex;
};
class SymbolBucket : public Bucket {
@@ -40,17 +45,33 @@ public:
const style::DataDrivenPropertyValue<float>& iconSize,
float zoom,
bool sdfIcons,
- bool iconsNeedLinear);
+ bool iconsNeedLinear,
+ bool sortFeaturesByY,
+ const std::vector<SymbolInstance>&&);
void upload(gl::Context&) override;
bool hasData() const override;
bool hasTextData() const;
bool hasIconData() const;
bool hasCollisionBoxData() const;
+ bool hasCollisionCircleData() const;
+
+ void updateOpacity();
+ void sortFeatures(const float angle);
const style::SymbolLayoutProperties::PossiblyEvaluated layout;
const bool sdfIcons;
const bool iconsNeedLinear;
+ const bool sortFeaturesByY;
+
+ optional<float> sortedAngle;
+
+ bool staticUploaded = false;
+ bool placementChangesUploaded = false;
+ bool dynamicUploaded = false;
+ bool sortUploaded = false;
+
+ std::vector<SymbolInstance> symbolInstances;
std::map<std::string, std::pair<
SymbolIconProgram::PaintPropertyBinders,
@@ -61,12 +82,14 @@ public:
struct TextBuffer {
gl::VertexVector<SymbolLayoutVertex> vertices;
gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex> dynamicVertices;
+ gl::VertexVector<SymbolOpacityAttributes::Vertex> opacityVertices;
gl::IndexVector<gl::Triangles> triangles;
SegmentVector<SymbolTextAttributes> segments;
std::vector<PlacedSymbol> placedSymbols;
optional<gl::VertexBuffer<SymbolLayoutVertex>> vertexBuffer;
optional<gl::VertexBuffer<SymbolDynamicLayoutAttributes::Vertex>> dynamicVertexBuffer;
+ optional<gl::VertexBuffer<SymbolOpacityAttributes::Vertex>> opacityVertexBuffer;
optional<gl::IndexBuffer<gl::Triangles>> indexBuffer;
} text;
@@ -75,6 +98,7 @@ public:
struct IconBuffer {
gl::VertexVector<SymbolLayoutVertex> vertices;
gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex> dynamicVertices;
+ gl::VertexVector<SymbolOpacityAttributes::Vertex> opacityVertices;
gl::IndexVector<gl::Triangles> triangles;
SegmentVector<SymbolIconAttributes> segments;
std::vector<PlacedSymbol> placedSymbols;
@@ -82,18 +106,31 @@ public:
optional<gl::VertexBuffer<SymbolLayoutVertex>> vertexBuffer;
optional<gl::VertexBuffer<SymbolDynamicLayoutAttributes::Vertex>> dynamicVertexBuffer;
+ optional<gl::VertexBuffer<SymbolOpacityAttributes::Vertex>> opacityVertexBuffer;
optional<gl::IndexBuffer<gl::Triangles>> indexBuffer;
} icon;
- struct CollisionBoxBuffer {
- gl::VertexVector<CollisionBoxVertex> vertices;
- gl::IndexVector<gl::Lines> lines;
- SegmentVector<CollisionBoxAttributes> segments;
+ struct CollisionBuffer {
+ gl::VertexVector<CollisionBoxLayoutAttributes::Vertex> vertices;
+ gl::VertexVector<CollisionBoxDynamicAttributes::Vertex> dynamicVertices;
+ SegmentVector<CollisionBoxProgram::Attributes> segments;
- optional<gl::VertexBuffer<CollisionBoxVertex>> vertexBuffer;
- optional<gl::VertexBuffer<SymbolDynamicLayoutAttributes::Vertex>> dynamicVertexBuffer;
+ optional<gl::VertexBuffer<CollisionBoxLayoutAttributes::Vertex>> vertexBuffer;
+ optional<gl::VertexBuffer<CollisionBoxDynamicAttributes::Vertex>> dynamicVertexBuffer;
+ };
+
+ struct CollisionBoxBuffer : public CollisionBuffer {
+ gl::IndexVector<gl::Lines> lines;
optional<gl::IndexBuffer<gl::Lines>> indexBuffer;
} collisionBox;
+
+ struct CollisionCircleBuffer : public CollisionBuffer {
+ gl::IndexVector<gl::Triangles> triangles;
+ optional<gl::IndexBuffer<gl::Triangles>> indexBuffer;
+ } collisionCircle;
+
+ uint32_t bucketInstanceId = 0;
+ bool justReloaded = false;
};
} // namespace mbgl
diff --git a/src/mbgl/renderer/frame_history.cpp b/src/mbgl/renderer/frame_history.cpp
deleted file mode 100644
index de153b6963..0000000000
--- a/src/mbgl/renderer/frame_history.cpp
+++ /dev/null
@@ -1,81 +0,0 @@
-#include <mbgl/renderer/frame_history.hpp>
-#include <mbgl/math/minmax.hpp>
-#include <mbgl/gl/context.hpp>
-
-#include <cassert>
-
-namespace mbgl {
-
-FrameHistory::FrameHistory() {
- changeOpacities.fill(0);
- opacities.fill(0);
-}
-
-void FrameHistory::record(const TimePoint& now, float zoom, const Duration& duration) {
-
- int16_t zoomIndex = std::floor(zoom * 10.0);
-
- if (firstFrame) {
- changeTimes.fill(now);
-
- for (int16_t z = 0; z <= zoomIndex; z++) {
- opacities.data[z] = 255u;
- }
- firstFrame = false;
- }
-
- if (zoomIndex < previousZoomIndex) {
- for (int16_t z = zoomIndex + 1; z <= previousZoomIndex; z++) {
- changeTimes[z] = now;
- changeOpacities[z] = opacities.data[z];
- }
- } else {
- for (int16_t z = zoomIndex; z > previousZoomIndex; z--) {
- changeTimes[z] = now;
- changeOpacities[z] = opacities.data[z];
- }
- }
-
- for (int16_t z = 0; z <= 255; z++) {
- const std::chrono::duration<float> timeDiff = now - changeTimes[z];
- const int32_t opacityChange = (duration == Milliseconds(0) ? 1 : (timeDiff / duration)) * 255;
- const uint8_t opacity = z <= zoomIndex
- ? util::min(255, changeOpacities[z] + opacityChange)
- : util::max(0, changeOpacities[z] - opacityChange);
- if (opacities.data[z] != opacity) {
- opacities.data[z] = opacity;
- dirty = true;
- }
- }
-
- if (zoomIndex != previousZoomIndex) {
- previousZoomIndex = zoomIndex;
- previousTime = now;
- }
-
- time = now;
-}
-
-bool FrameHistory::needsAnimation(const Duration& duration) const {
- return (time - previousTime) < duration;
-}
-
-void FrameHistory::upload(gl::Context& context, uint32_t unit) {
- if (!texture) {
- texture = context.createTexture(opacities, unit);
- } else if (dirty) {
- context.updateTexture(*texture, opacities, unit);
- }
- dirty = false;
-}
-
-void FrameHistory::bind(gl::Context& context, uint32_t unit) {
- upload(context, unit);
- context.bindTexture(*texture, unit);
-}
-
-bool FrameHistory::isVisible(const float zoom) const {
- return opacities.data[std::floor(zoom * 10)] != 0;
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/frame_history.hpp b/src/mbgl/renderer/frame_history.hpp
deleted file mode 100644
index 75a8b60a71..0000000000
--- a/src/mbgl/renderer/frame_history.hpp
+++ /dev/null
@@ -1,41 +0,0 @@
-#pragma once
-
-#include <array>
-
-#include <mbgl/util/platform.hpp>
-#include <mbgl/gl/texture.hpp>
-#include <mbgl/util/chrono.hpp>
-#include <mbgl/util/image.hpp>
-#include <mbgl/util/optional.hpp>
-
-namespace mbgl {
-
-namespace gl {
-class Context;
-} // namespace gl
-
-class FrameHistory {
-public:
- FrameHistory();
- void record(const TimePoint&, float zoom, const Duration&);
-
- bool needsAnimation(const Duration&) const;
- void bind(gl::Context&, uint32_t);
- void upload(gl::Context&, uint32_t);
- bool isVisible(const float zoom) const;
-
-private:
- std::array<TimePoint, 256> changeTimes;
- std::array<uint8_t, 256> changeOpacities;
- AlphaImage opacities{ { 256, 1 } };
-
- int16_t previousZoomIndex = 0;
- TimePoint previousTime;
- TimePoint time;
- bool firstFrame = true;
- bool dirty = true;
-
- mbgl::optional<gl::Texture> texture;
-};
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/layers/render_background_layer.cpp b/src/mbgl/renderer/layers/render_background_layer.cpp
index 9fddba3f74..aebc4cc9aa 100644
--- a/src/mbgl/renderer/layers/render_background_layer.cpp
+++ b/src/mbgl/renderer/layers/render_background_layer.cpp
@@ -5,7 +5,7 @@
#include <mbgl/renderer/image_manager.hpp>
#include <mbgl/renderer/render_static_data.hpp>
#include <mbgl/programs/programs.hpp>
-#include <mbgl/programs/fill_program.hpp>
+#include <mbgl/programs/background_program.hpp>
#include <mbgl/util/tile_cover.hpp>
namespace mbgl {
@@ -46,12 +46,8 @@ void RenderBackgroundLayer::render(PaintParameters& parameters, RenderSource*) {
// Note that for bottommost layers without a pattern, the background color is drawn with
// glClear rather than this method.
- style::FillPaintProperties::PossiblyEvaluated properties;
- properties.get<FillPattern>() = evaluated.get<BackgroundPattern>();
- properties.get<FillOpacity>() = { evaluated.get<BackgroundOpacity>() };
- properties.get<FillColor>() = { evaluated.get<BackgroundColor>() };
-
- const FillProgram::PaintPropertyBinders paintAttibuteData(properties, 0);
+ const Properties<>::PossiblyEvaluated properties;
+ const BackgroundProgram::PaintPropertyBinders paintAttributeData(properties, 0);
if (!evaluated.get<BackgroundPattern>().to.empty()) {
optional<ImagePosition> imagePosA = parameters.imageManager.getPattern(evaluated.get<BackgroundPattern>().from);
@@ -63,15 +59,15 @@ void RenderBackgroundLayer::render(PaintParameters& parameters, RenderSource*) {
parameters.imageManager.bind(parameters.context, 0);
for (const auto& tileID : util::tileCover(parameters.state, parameters.state.getIntegerZoom())) {
- parameters.programs.fillPattern.get(properties).draw(
+ parameters.programs.backgroundPattern.draw(
parameters.context,
gl::Triangles(),
parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly),
gl::StencilMode::disabled(),
parameters.colorModeForRenderPass(),
- FillPatternUniforms::values(
+ BackgroundPatternUniforms::values(
parameters.matrixForTile(tileID),
- parameters.context.viewport.getCurrentValue().size,
+ evaluated.get<BackgroundOpacity>(),
parameters.imageManager.getPixelSize(),
*imagePosA,
*imagePosB,
@@ -82,7 +78,7 @@ void RenderBackgroundLayer::render(PaintParameters& parameters, RenderSource*) {
parameters.staticData.tileVertexBuffer,
parameters.staticData.quadTriangleIndexBuffer,
parameters.staticData.tileTriangleSegments,
- paintAttibuteData,
+ paintAttributeData,
properties,
parameters.state.getZoom(),
getID()
@@ -90,20 +86,21 @@ void RenderBackgroundLayer::render(PaintParameters& parameters, RenderSource*) {
}
} else {
for (const auto& tileID : util::tileCover(parameters.state, parameters.state.getIntegerZoom())) {
- parameters.programs.fill.get(properties).draw(
+ parameters.programs.background.draw(
parameters.context,
gl::Triangles(),
parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly),
gl::StencilMode::disabled(),
parameters.colorModeForRenderPass(),
- FillProgram::UniformValues {
+ BackgroundProgram::UniformValues {
uniforms::u_matrix::Value{ parameters.matrixForTile(tileID) },
- uniforms::u_world::Value{ parameters.context.viewport.getCurrentValue().size },
+ uniforms::u_color::Value{ evaluated.get<BackgroundColor>() },
+ uniforms::u_opacity::Value{ evaluated.get<BackgroundOpacity>() },
},
parameters.staticData.tileVertexBuffer,
parameters.staticData.quadTriangleIndexBuffer,
parameters.staticData.tileTriangleSegments,
- paintAttibuteData,
+ paintAttributeData,
properties,
parameters.state.getZoom(),
getID()
diff --git a/src/mbgl/renderer/layers/render_circle_layer.cpp b/src/mbgl/renderer/layers/render_circle_layer.cpp
index e7b022f3ee..6092ff5452 100644
--- a/src/mbgl/renderer/layers/render_circle_layer.cpp
+++ b/src/mbgl/renderer/layers/render_circle_layer.cpp
@@ -63,7 +63,7 @@ void RenderCircleLayer::render(PaintParameters& parameters, RenderSource*) {
parameters.context,
gl::Triangles(),
parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly),
- parameters.mapMode == MapMode::Still
+ parameters.mapMode != MapMode::Continuous
? parameters.stencilModeForClipping(tile.clip)
: gl::StencilMode::disabled(),
parameters.colorModeForRenderPass(),
@@ -108,13 +108,12 @@ bool RenderCircleLayer::queryIntersectsFeature(
bearing,
pixelsToTileUnits);
- // Evaluate function
- auto circleRadius = evaluated.get<style::CircleRadius>()
- .evaluate(feature, zoom, style::CircleRadius::defaultValue())
- * pixelsToTileUnits;
+ // Evaluate functions
+ auto radius = evaluated.evaluate<style::CircleRadius>(zoom, feature) * pixelsToTileUnits;
+ auto stroke = evaluated.evaluate<style::CircleStrokeWidth>(zoom, feature) * pixelsToTileUnits;
// Test intersection
- return util::polygonIntersectsBufferedMultiPoint(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries(), circleRadius);
+ return util::polygonIntersectsBufferedMultiPoint(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries(), radius + stroke);
}
} // namespace mbgl
diff --git a/src/mbgl/renderer/layers/render_heatmap_layer.cpp b/src/mbgl/renderer/layers/render_heatmap_layer.cpp
new file mode 100644
index 0000000000..0f9e3239ef
--- /dev/null
+++ b/src/mbgl/renderer/layers/render_heatmap_layer.cpp
@@ -0,0 +1,178 @@
+#include <mbgl/renderer/layers/render_heatmap_layer.hpp>
+#include <mbgl/renderer/buckets/heatmap_bucket.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/renderer/render_static_data.hpp>
+#include <mbgl/programs/programs.hpp>
+#include <mbgl/programs/heatmap_program.hpp>
+#include <mbgl/tile/tile.hpp>
+#include <mbgl/style/layers/heatmap_layer.hpp>
+#include <mbgl/style/layers/heatmap_layer_impl.hpp>
+#include <mbgl/geometry/feature_index.hpp>
+#include <mbgl/util/math.hpp>
+#include <mbgl/util/intersection_tests.hpp>
+
+namespace mbgl {
+
+using namespace style;
+
+RenderHeatmapLayer::RenderHeatmapLayer(Immutable<style::HeatmapLayer::Impl> _impl)
+ : RenderLayer(style::LayerType::Heatmap, _impl),
+ unevaluated(impl().paint.untransitioned()), colorRamp({256, 1}) {
+}
+
+const style::HeatmapLayer::Impl& RenderHeatmapLayer::impl() const {
+ return static_cast<const style::HeatmapLayer::Impl&>(*baseImpl);
+}
+
+std::unique_ptr<Bucket> RenderHeatmapLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const {
+ return std::make_unique<HeatmapBucket>(parameters, layers);
+}
+
+void RenderHeatmapLayer::transition(const TransitionParameters& parameters) {
+ unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated));
+}
+
+void RenderHeatmapLayer::evaluate(const PropertyEvaluationParameters& parameters) {
+ evaluated = unevaluated.evaluate(parameters);
+
+ passes = (evaluated.get<style::HeatmapOpacity>() > 0)
+ ? (RenderPass::Translucent | RenderPass::Pass3D)
+ : RenderPass::None;
+}
+
+bool RenderHeatmapLayer::hasTransition() const {
+ return unevaluated.hasTransition();
+}
+
+void RenderHeatmapLayer::render(PaintParameters& parameters, RenderSource*) {
+ if (parameters.pass == RenderPass::Opaque) {
+ return;
+ }
+
+ if (parameters.pass == RenderPass::Pass3D) {
+ const auto& viewportSize = parameters.staticData.backendSize;
+ const auto size = Size{viewportSize.width / 4, viewportSize.height / 4};
+
+ if (!renderTexture || renderTexture->getSize() != size) {
+ if (parameters.context.supportsHalfFloatTextures) {
+ renderTexture = OffscreenTexture(parameters.context, size, gl::TextureType::HalfFloat);
+
+ try {
+ renderTexture->bind();
+ } catch (const std::runtime_error& ex) {
+ // can't render to a half-float texture; falling back to unsigned byte one
+ renderTexture = nullopt;
+ parameters.context.supportsHalfFloatTextures = false;
+ }
+ }
+
+ if (!renderTexture) {
+ renderTexture = OffscreenTexture(parameters.context, size, gl::TextureType::UnsignedByte);
+ renderTexture->bind();
+ }
+
+ } else {
+ renderTexture->bind();
+ }
+
+ if (!colorRampTexture) {
+ colorRampTexture = parameters.context.createTexture(colorRamp, 1, gl::TextureType::UnsignedByte);
+ }
+
+ parameters.context.clear(Color{ 0.0f, 0.0f, 0.0f, 1.0f }, {}, {});
+
+ for (const RenderTile& tile : renderTiles) {
+ assert(dynamic_cast<HeatmapBucket*>(tile.tile.getBucket(*baseImpl)));
+ HeatmapBucket& bucket = *reinterpret_cast<HeatmapBucket*>(tile.tile.getBucket(*baseImpl));
+
+ const auto extrudeScale = tile.id.pixelsToTileUnits(1, parameters.state.getZoom());
+
+ const auto stencilMode = parameters.mapMode != MapMode::Continuous
+ ? parameters.stencilModeForClipping(tile.clip)
+ : gl::StencilMode::disabled();
+
+ parameters.programs.heatmap.get(evaluated).draw(
+ parameters.context,
+ gl::Triangles(),
+ parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly),
+ stencilMode,
+ gl::ColorMode::additive(),
+ HeatmapProgram::UniformValues {
+ uniforms::u_intensity::Value{evaluated.get<style::HeatmapIntensity>()},
+ uniforms::u_matrix::Value{tile.matrix},
+ uniforms::heatmap::u_extrude_scale::Value{extrudeScale}
+ },
+ *bucket.vertexBuffer,
+ *bucket.indexBuffer,
+ bucket.segments,
+ bucket.paintPropertyBinders.at(getID()),
+ evaluated,
+ parameters.state.getZoom(),
+ getID()
+ );
+ }
+
+ } else if (parameters.pass == RenderPass::Translucent) {
+ parameters.context.bindTexture(renderTexture->getTexture(), 0, gl::TextureFilter::Linear);
+ parameters.context.bindTexture(*colorRampTexture, 1, gl::TextureFilter::Linear);
+
+ const auto& size = parameters.staticData.backendSize;
+
+ mat4 viewportMat;
+ matrix::ortho(viewportMat, 0, size.width, size.height, 0, 0, 1);
+
+ const Properties<>::PossiblyEvaluated properties;
+
+ parameters.programs.heatmapTexture.draw(
+ parameters.context, gl::Triangles(), gl::DepthMode::disabled(),
+ gl::StencilMode::disabled(), parameters.colorModeForRenderPass(),
+ HeatmapTextureProgram::UniformValues{
+ uniforms::u_matrix::Value{ viewportMat }, uniforms::u_world::Value{ size },
+ uniforms::u_image::Value{ 0 },
+ uniforms::u_color_ramp::Value{ 1 },
+ uniforms::u_opacity::Value{ evaluated.get<HeatmapOpacity>() } },
+ parameters.staticData.extrusionTextureVertexBuffer,
+ parameters.staticData.quadTriangleIndexBuffer,
+ parameters.staticData.extrusionTextureSegments,
+ HeatmapTextureProgram::PaintPropertyBinders{ properties, 0 }, properties,
+ parameters.state.getZoom(), getID());
+ }
+}
+
+void RenderHeatmapLayer::updateColorRamp() {
+ auto colorValue = unevaluated.get<HeatmapColor>().getValue();
+ if (colorValue.isUndefined()) {
+ colorValue = HeatmapLayer::getDefaultHeatmapColor();
+ }
+
+ const auto length = colorRamp.bytes();
+
+ for (uint32_t i = 0; i < length; i += 4) {
+ const auto color = colorValue.evaluate(static_cast<double>(i) / length);
+ colorRamp.data[i + 0] = std::floor(color.r * 255);
+ colorRamp.data[i + 1] = std::floor(color.g * 255);
+ colorRamp.data[i + 2] = std::floor(color.b * 255);
+ colorRamp.data[i + 3] = std::floor(color.a * 255);
+ }
+
+ if (colorRampTexture) {
+ colorRampTexture = nullopt;
+ }
+}
+
+bool RenderHeatmapLayer::queryIntersectsFeature(
+ const GeometryCoordinates& queryGeometry,
+ const GeometryTileFeature& feature,
+ const float zoom,
+ const float bearing,
+ const float pixelsToTileUnits) const {
+ (void) queryGeometry;
+ (void) feature;
+ (void) zoom;
+ (void) bearing;
+ (void) pixelsToTileUnits;
+ return false;
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/layers/render_heatmap_layer.hpp b/src/mbgl/renderer/layers/render_heatmap_layer.hpp
new file mode 100644
index 0000000000..3f0b1f91b4
--- /dev/null
+++ b/src/mbgl/renderer/layers/render_heatmap_layer.hpp
@@ -0,0 +1,48 @@
+#pragma once
+
+#include <mbgl/renderer/render_layer.hpp>
+#include <mbgl/style/layers/heatmap_layer_impl.hpp>
+#include <mbgl/style/layers/heatmap_layer_properties.hpp>
+#include <mbgl/util/optional.hpp>
+#include <mbgl/util/offscreen_texture.hpp>
+
+namespace mbgl {
+
+class RenderHeatmapLayer: public RenderLayer {
+public:
+ RenderHeatmapLayer(Immutable<style::HeatmapLayer::Impl>);
+ ~RenderHeatmapLayer() final = default;
+
+ void transition(const TransitionParameters&) override;
+ void evaluate(const PropertyEvaluationParameters&) override;
+ bool hasTransition() const override;
+ void render(PaintParameters&, RenderSource*) override;
+
+ bool queryIntersectsFeature(
+ const GeometryCoordinates&,
+ const GeometryTileFeature&,
+ const float,
+ const float,
+ const float) const override;
+
+ void updateColorRamp();
+
+ std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override;
+
+ // Paint properties
+ style::HeatmapPaintProperties::Unevaluated unevaluated;
+ style::HeatmapPaintProperties::PossiblyEvaluated evaluated;
+
+ const style::HeatmapLayer::Impl& impl() const;
+
+ PremultipliedImage colorRamp;
+ optional<OffscreenTexture> renderTexture;
+ optional<gl::Texture> colorRampTexture;
+};
+
+template <>
+inline bool RenderLayer::is<RenderHeatmapLayer>() const {
+ return type == style::LayerType::Heatmap;
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/layers/render_hillshade_layer.cpp b/src/mbgl/renderer/layers/render_hillshade_layer.cpp
new file mode 100644
index 0000000000..bcfd4ffe99
--- /dev/null
+++ b/src/mbgl/renderer/layers/render_hillshade_layer.cpp
@@ -0,0 +1,164 @@
+#include <mbgl/renderer/layers/render_hillshade_layer.hpp>
+#include <mbgl/renderer/buckets/hillshade_bucket.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/renderer/sources/render_raster_dem_source.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/renderer/render_static_data.hpp>
+#include <mbgl/programs/programs.hpp>
+#include <mbgl/programs/hillshade_program.hpp>
+#include <mbgl/programs/hillshade_prepare_program.hpp>
+#include <mbgl/tile/tile.hpp>
+#include <mbgl/style/layers/hillshade_layer_impl.hpp>
+#include <mbgl/util/geo.hpp>
+#include <mbgl/util/offscreen_texture.hpp>
+
+namespace mbgl {
+
+using namespace style;
+RenderHillshadeLayer::RenderHillshadeLayer(Immutable<style::HillshadeLayer::Impl> _impl)
+ : RenderLayer(style::LayerType::Hillshade, _impl),
+ unevaluated(impl().paint.untransitioned()) {
+}
+
+const style::HillshadeLayer::Impl& RenderHillshadeLayer::impl() const {
+ return static_cast<const style::HillshadeLayer::Impl&>(*baseImpl);
+}
+
+std::unique_ptr<Bucket> RenderHillshadeLayer::createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const {
+ assert(false);
+ return nullptr;
+}
+
+const std::array<float, 2> RenderHillshadeLayer::getLatRange(const UnwrappedTileID& id) {
+ const LatLng latlng0 = LatLng(id);
+ const LatLng latlng1 = LatLng(UnwrappedTileID(id.canonical.z, id.canonical.x, id.canonical.y + 1));
+ return {{ (float)latlng0.latitude(), (float)latlng1.latitude() }};
+}
+
+const std::array<float, 2> RenderHillshadeLayer::getLight(const PaintParameters& parameters){
+ float azimuthal = evaluated.get<HillshadeIlluminationDirection>() * util::DEG2RAD;
+ if (evaluated.get<HillshadeIlluminationAnchor>() == HillshadeIlluminationAnchorType::Viewport) azimuthal = azimuthal - parameters.state.getAngle();
+ return {{evaluated.get<HillshadeExaggeration>(), azimuthal}};
+}
+
+void RenderHillshadeLayer::transition(const TransitionParameters& parameters) {
+ unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated));
+}
+
+void RenderHillshadeLayer::evaluate(const PropertyEvaluationParameters& parameters) {
+ evaluated = unevaluated.evaluate(parameters);
+ passes = (evaluated.get<style::HillshadeExaggeration >() > 0)
+ ? (RenderPass::Translucent | RenderPass::Pass3D)
+ : RenderPass::None;
+}
+
+bool RenderHillshadeLayer::hasTransition() const {
+ return unevaluated.hasTransition();
+}
+
+void RenderHillshadeLayer::render(PaintParameters& parameters, RenderSource* src) {
+ if (parameters.pass != RenderPass::Translucent && parameters.pass != RenderPass::Pass3D)
+ return;
+
+ RenderRasterDEMSource* demsrc = dynamic_cast<RenderRasterDEMSource*>(src);
+ const uint8_t TERRAIN_RGB_MAXZOOM = 15;
+ const uint8_t maxzoom = demsrc != nullptr ? demsrc->getMaxZoom() : TERRAIN_RGB_MAXZOOM;
+
+ auto draw = [&] (const mat4& matrix,
+ const auto& vertexBuffer,
+ const auto& indexBuffer,
+ const auto& segments,
+ const UnwrappedTileID& id) {
+ parameters.programs.hillshade.draw(
+ parameters.context,
+ gl::Triangles(),
+ parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly),
+ gl::StencilMode::disabled(),
+ parameters.colorModeForRenderPass(),
+ HillshadeProgram::UniformValues {
+ uniforms::u_matrix::Value{ matrix },
+ uniforms::u_image::Value{ 0 },
+ uniforms::u_highlight::Value{ evaluated.get<HillshadeHighlightColor>() },
+ uniforms::u_shadow::Value{ evaluated.get<HillshadeShadowColor>() },
+ uniforms::u_accent::Value{ evaluated.get<HillshadeAccentColor>() },
+ uniforms::u_light::Value{ getLight(parameters) },
+ uniforms::u_latrange::Value{ getLatRange(id) },
+ },
+ vertexBuffer,
+ indexBuffer,
+ segments,
+ HillshadeProgram::PaintPropertyBinders { evaluated, 0 },
+ evaluated,
+ parameters.state.getZoom(),
+ getID()
+ );
+ };
+
+ mat4 mat;
+ matrix::ortho(mat, 0, util::EXTENT, -util::EXTENT, 0, 0, 1);
+ matrix::translate(mat, mat, 0, -util::EXTENT, 0);
+
+ for (const RenderTile& tile : renderTiles) {
+ assert(dynamic_cast<HillshadeBucket*>(tile.tile.getBucket(*baseImpl)));
+ HillshadeBucket& bucket = *reinterpret_cast<HillshadeBucket*>(tile.tile.getBucket(*baseImpl));
+ if (!bucket.hasData()){
+ continue;
+ }
+
+ if (!bucket.isPrepared() && parameters.pass == RenderPass::Pass3D) {
+ const uint16_t tilesize = bucket.getDEMData().dim;
+ OffscreenTexture view(parameters.context, { tilesize, tilesize });
+ view.bind();
+
+ parameters.context.bindTexture(*bucket.dem, 0, gl::TextureFilter::Nearest, gl::TextureMipMap::No, gl::TextureWrap::Clamp, gl::TextureWrap::Clamp);
+ const Properties<>::PossiblyEvaluated properties;
+
+ parameters.programs.hillshadePrepare.draw(
+ parameters.context,
+ gl::Triangles(),
+ parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly),
+ gl::StencilMode::disabled(),
+ parameters.colorModeForRenderPass(),
+ HillshadePrepareProgram::UniformValues {
+ uniforms::u_matrix::Value { mat },
+ uniforms::u_dimension::Value { {{uint16_t(tilesize * 2), uint16_t(tilesize * 2) }} },
+ uniforms::u_zoom::Value{ float(tile.id.canonical.z) },
+ uniforms::u_maxzoom::Value{ float(maxzoom) },
+ uniforms::u_image::Value{ 0 }
+ },
+ parameters.staticData.rasterVertexBuffer,
+ parameters.staticData.quadTriangleIndexBuffer,
+ parameters.staticData.rasterSegments,
+ HillshadePrepareProgram::PaintPropertyBinders { properties, 0 },
+ properties,
+ parameters.state.getZoom(),
+ getID()
+ );
+ bucket.texture = std::move(view.getTexture());
+ bucket.setPrepared(true);
+ } else if (parameters.pass == RenderPass::Translucent) {
+ assert(bucket.texture);
+ parameters.context.bindTexture(*bucket.texture, 0, gl::TextureFilter::Linear, gl::TextureMipMap::No, gl::TextureWrap::Clamp, gl::TextureWrap::Clamp);
+
+ if (bucket.vertexBuffer && bucket.indexBuffer && !bucket.segments.empty()) {
+ // Draw only the parts of the tile that aren't drawn by another tile in the layer.
+ draw(parameters.matrixForTile(tile.id, true),
+ *bucket.vertexBuffer,
+ *bucket.indexBuffer,
+ bucket.segments,
+ tile.id);
+ } else {
+ // Draw the full tile.
+ draw(parameters.matrixForTile(tile.id, true),
+ parameters.staticData.rasterVertexBuffer,
+ parameters.staticData.quadTriangleIndexBuffer,
+ parameters.staticData.rasterSegments,
+ tile.id);
+ }
+ }
+
+
+ }
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/layers/render_hillshade_layer.hpp b/src/mbgl/renderer/layers/render_hillshade_layer.hpp
new file mode 100644
index 0000000000..13093ee7ef
--- /dev/null
+++ b/src/mbgl/renderer/layers/render_hillshade_layer.hpp
@@ -0,0 +1,38 @@
+#pragma once
+
+#include <mbgl/renderer/render_layer.hpp>
+#include <mbgl/style/layers/hillshade_layer_impl.hpp>
+#include <mbgl/style/layers/hillshade_layer_properties.hpp>
+#include <mbgl/tile/tile_id.hpp>
+
+namespace mbgl {
+
+class RenderHillshadeLayer: public RenderLayer {
+public:
+ RenderHillshadeLayer(Immutable<style::HillshadeLayer::Impl>);
+ ~RenderHillshadeLayer() final = default;
+
+ void transition(const TransitionParameters&) override;
+ void evaluate(const PropertyEvaluationParameters&) override;
+ bool hasTransition() const override;
+
+ void render(PaintParameters&, RenderSource* src) override;
+
+ std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override;
+
+ // Paint properties
+ style::HillshadePaintProperties::Unevaluated unevaluated;
+ style::HillshadePaintProperties::PossiblyEvaluated evaluated;
+
+ const style::HillshadeLayer::Impl& impl() const;
+private:
+ const std::array<float, 2> getLatRange(const UnwrappedTileID& id);
+ const std::array<float, 2> getLight(const PaintParameters& parameters);
+};
+
+template <>
+inline bool RenderLayer::is<RenderHillshadeLayer>() const {
+ return type == style::LayerType::Hillshade;
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/layers/render_raster_layer.cpp b/src/mbgl/renderer/layers/render_raster_layer.cpp
index 06616d90e5..b41b2ac560 100644
--- a/src/mbgl/renderer/layers/render_raster_layer.cpp
+++ b/src/mbgl/renderer/layers/render_raster_layer.cpp
@@ -137,13 +137,13 @@ void RenderRasterLayer::render(PaintParameters& parameters, RenderSource* source
if (bucket.vertexBuffer && bucket.indexBuffer && !bucket.segments.empty()) {
// Draw only the parts of the tile that aren't drawn by another tile in the layer.
- draw(tile.matrix,
+ draw(parameters.matrixForTile(tile.id, true),
*bucket.vertexBuffer,
*bucket.indexBuffer,
bucket.segments);
} else {
// Draw the full tile.
- draw(tile.matrix,
+ draw(parameters.matrixForTile(tile.id, true),
parameters.staticData.rasterVertexBuffer,
parameters.staticData.quadTriangleIndexBuffer,
parameters.staticData.rasterSegments);
diff --git a/src/mbgl/renderer/layers/render_symbol_layer.cpp b/src/mbgl/renderer/layers/render_symbol_layer.cpp
index 1376e8a3d8..9e493003c0 100644
--- a/src/mbgl/renderer/layers/render_symbol_layer.cpp
+++ b/src/mbgl/renderer/layers/render_symbol_layer.cpp
@@ -4,7 +4,6 @@
#include <mbgl/renderer/property_evaluation_parameters.hpp>
#include <mbgl/renderer/render_tile.hpp>
#include <mbgl/renderer/paint_parameters.hpp>
-#include <mbgl/renderer/frame_history.hpp>
#include <mbgl/text/glyph_atlas.hpp>
#include <mbgl/programs/programs.hpp>
#include <mbgl/programs/symbol_program.hpp>
@@ -81,8 +80,6 @@ void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) {
const auto& layout = bucket.layout;
- parameters.frameHistory.bind(parameters.context, 1);
-
auto draw = [&] (auto& program,
auto&& uniformValues,
const auto& buffers,
@@ -91,22 +88,18 @@ void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) {
const auto& binders,
const auto& paintProperties)
{
- // We clip symbols to their tile extent in still mode.
- const bool needsClipping = parameters.mapMode == MapMode::Still;
-
program.get(paintProperties).draw(
parameters.context,
gl::Triangles(),
values_.pitchAlignment == AlignmentType::Map
? parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly)
: gl::DepthMode::disabled(),
- needsClipping
- ? parameters.stencilModeForClipping(tile.clip)
- : gl::StencilMode::disabled(),
+ gl::StencilMode::disabled(),
parameters.colorModeForRenderPass(),
std::move(uniformValues),
*buffers.vertexBuffer,
*buffers.dynamicVertexBuffer,
+ *buffers.opacityVertexBuffer,
*symbolSizeBinder,
*buffers.indexBuffer,
buffers.segments,
@@ -134,8 +127,7 @@ void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) {
values,
tile,
*bucket.iconSizeBinder,
- parameters.state,
- parameters.frameHistory);
+ parameters.state);
parameters.context.updateVertexBuffer(*bucket.icon.dynamicVertexBuffer, std::move(bucket.icon.dynamicVertices));
}
@@ -152,7 +144,7 @@ void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) {
if (bucket.sdfIcons) {
if (values.hasHalo) {
draw(parameters.programs.symbolIconSDF,
- SymbolSDFIconProgram::uniformValues(false, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, SymbolSDFPart::Halo),
+ SymbolSDFIconProgram::uniformValues(false, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Halo),
bucket.icon,
bucket.iconSizeBinder,
values,
@@ -162,7 +154,7 @@ void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) {
if (values.hasFill) {
draw(parameters.programs.symbolIconSDF,
- SymbolSDFIconProgram::uniformValues(false, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, SymbolSDFPart::Fill),
+ SymbolSDFIconProgram::uniformValues(false, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Fill),
bucket.icon,
bucket.iconSizeBinder,
values,
@@ -171,7 +163,7 @@ void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) {
}
} else {
draw(parameters.programs.symbolIcon,
- SymbolIconProgram::uniformValues(false, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state),
+ SymbolIconProgram::uniformValues(false, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange),
bucket.icon,
bucket.iconSizeBinder,
values,
@@ -196,8 +188,7 @@ void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) {
values,
tile,
*bucket.textSizeBinder,
- parameters.state,
- parameters.frameHistory);
+ parameters.state);
parameters.context.updateVertexBuffer(*bucket.text.dynamicVertexBuffer, std::move(bucket.text.dynamicVertices));
}
@@ -206,7 +197,7 @@ void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) {
if (values.hasHalo) {
draw(parameters.programs.symbolGlyph,
- SymbolSDFTextProgram::uniformValues(true, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, SymbolSDFPart::Halo),
+ SymbolSDFTextProgram::uniformValues(true, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Halo),
bucket.text,
bucket.textSizeBinder,
values,
@@ -216,7 +207,7 @@ void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) {
if (values.hasFill) {
draw(parameters.programs.symbolGlyph,
- SymbolSDFTextProgram::uniformValues(true, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, SymbolSDFPart::Fill),
+ SymbolSDFTextProgram::uniformValues(true, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Fill),
bucket.text,
bucket.textSizeBinder,
values,
@@ -229,23 +220,27 @@ void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) {
static const style::Properties<>::PossiblyEvaluated properties {};
static const CollisionBoxProgram::PaintPropertyBinders paintAttributeData(properties, 0);
+ auto pixelRatio = tile.id.pixelsToTileUnits(1, parameters.state.getZoom());
+ auto scale = std::pow(2.0f, float(parameters.state.getZoom() - tile.tile.id.overscaledZ));
+ std::array<float,2> extrudeScale =
+ {{
+ parameters.pixelsToGLUnits[0] / (pixelRatio * scale),
+ parameters.pixelsToGLUnits[1] / (pixelRatio * scale)
+
+ }};
parameters.programs.collisionBox.draw(
parameters.context,
gl::Lines { 1.0f },
gl::DepthMode::disabled(),
- parameters.stencilModeForClipping(tile.clip),
+ gl::StencilMode::disabled(),
parameters.colorModeForRenderPass(),
CollisionBoxProgram::UniformValues {
uniforms::u_matrix::Value{ tile.matrix },
- uniforms::u_scale::Value{ std::pow(2.0f, float(parameters.state.getZoom() - tile.tile.id.overscaledZ)) },
- uniforms::u_zoom::Value{ float(parameters.state.getZoom() * 10) },
- uniforms::u_maxzoom::Value{ float((tile.id.canonical.z + 1) * 10) },
- uniforms::u_collision_y_stretch::Value{ tile.tile.yStretch() },
- uniforms::u_camera_to_center_distance::Value{ parameters.state.getCameraToCenterDistance() },
- uniforms::u_pitch::Value{ parameters.state.getPitch() },
- uniforms::u_fadetexture::Value{ 1 }
+ uniforms::u_extrude_scale::Value{ extrudeScale },
+ uniforms::u_camera_to_center_distance::Value{ parameters.state.getCameraToCenterDistance() }
},
*bucket.collisionBox.vertexBuffer,
+ *bucket.collisionBox.dynamicVertexBuffer,
*bucket.collisionBox.indexBuffer,
bucket.collisionBox.segments,
paintAttributeData,
@@ -254,6 +249,42 @@ void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) {
getID()
);
}
+ if (bucket.hasCollisionCircleData()) {
+ static const style::Properties<>::PossiblyEvaluated properties {};
+ static const CollisionBoxProgram::PaintPropertyBinders paintAttributeData(properties, 0);
+
+ auto pixelRatio = tile.id.pixelsToTileUnits(1, parameters.state.getZoom());
+ auto scale = std::pow(2.0f, float(parameters.state.getZoom() - tile.tile.id.overscaledZ));
+ std::array<float,2> extrudeScale =
+ {{
+ parameters.pixelsToGLUnits[0] / (pixelRatio * scale),
+ parameters.pixelsToGLUnits[1] / (pixelRatio * scale)
+
+ }};
+
+ parameters.programs.collisionCircle.draw(
+ parameters.context,
+ gl::Triangles(),
+ gl::DepthMode::disabled(),
+ gl::StencilMode::disabled(),
+ parameters.colorModeForRenderPass(),
+ CollisionCircleProgram::UniformValues {
+ uniforms::u_matrix::Value{ tile.matrix },
+ uniforms::u_extrude_scale::Value{ extrudeScale },
+ uniforms::u_overscale_factor::Value{ float(tile.tile.id.overscaleFactor()) },
+ uniforms::u_camera_to_center_distance::Value{ parameters.state.getCameraToCenterDistance() }
+ },
+ *bucket.collisionCircle.vertexBuffer,
+ *bucket.collisionCircle.dynamicVertexBuffer,
+ *bucket.collisionCircle.indexBuffer,
+ bucket.collisionCircle.segments,
+ paintAttributeData,
+ properties,
+ parameters.state.getZoom(),
+ getID()
+ );
+
+ }
}
}
diff --git a/src/mbgl/renderer/paint_parameters.cpp b/src/mbgl/renderer/paint_parameters.cpp
index 299db844bc..a7f621eb61 100644
--- a/src/mbgl/renderer/paint_parameters.cpp
+++ b/src/mbgl/renderer/paint_parameters.cpp
@@ -12,7 +12,6 @@ PaintParameters::PaintParameters(gl::Context& context_,
const UpdateParameters& updateParameters,
const EvaluatedLight& evaluatedLight_,
RenderStaticData& staticData_,
- FrameHistory& frameHistory_,
ImageManager& imageManager_,
LineAtlas& lineAtlas_)
: context(context_),
@@ -20,7 +19,6 @@ PaintParameters::PaintParameters(gl::Context& context_,
state(updateParameters.transformState),
evaluatedLight(evaluatedLight_),
staticData(staticData_),
- frameHistory(frameHistory_),
imageManager(imageManager_),
lineAtlas(lineAtlas_),
mapMode(updateParameters.mode),
@@ -37,6 +35,10 @@ PaintParameters::PaintParameters(gl::Context& context_,
// Update the default matrices to the current viewport dimensions.
state.getProjMatrix(projMatrix);
+ // Also compute a projection matrix that aligns with the current pixel grid, taking into account
+ // odd viewport sizes.
+ state.getProjMatrix(alignedProjMatrix, 1, true);
+
// Calculate a second projection matrix with the near plane clipped to 100 so as
// not to waste lots of depth buffer precision on very close empty space, for layer
// types (fill-extrusion) that use the depth buffer to emulate real-world space.
@@ -49,10 +51,10 @@ PaintParameters::PaintParameters(gl::Context& context_,
}
}
-mat4 PaintParameters::matrixForTile(const UnwrappedTileID& tileID) {
+mat4 PaintParameters::matrixForTile(const UnwrappedTileID& tileID, bool aligned) const {
mat4 matrix;
state.matrixFor(matrix, tileID);
- matrix::multiply(matrix, projMatrix, matrix);
+ matrix::multiply(matrix, aligned ? alignedProjMatrix : projMatrix, matrix);
return matrix;
}
diff --git a/src/mbgl/renderer/paint_parameters.hpp b/src/mbgl/renderer/paint_parameters.hpp
index 4a2c2c6f12..41f46ae34e 100644
--- a/src/mbgl/renderer/paint_parameters.hpp
+++ b/src/mbgl/renderer/paint_parameters.hpp
@@ -2,6 +2,7 @@
#include <mbgl/renderer/render_pass.hpp>
#include <mbgl/renderer/render_light.hpp>
+#include <mbgl/renderer/mode.hpp>
#include <mbgl/map/mode.hpp>
#include <mbgl/gl/depth_mode.hpp>
#include <mbgl/gl/stencil_mode.hpp>
@@ -16,7 +17,6 @@ namespace mbgl {
class RendererBackend;
class UpdateParameters;
class RenderStaticData;
-class FrameHistory;
class Programs;
class TransformState;
class ImageManager;
@@ -32,7 +32,6 @@ public:
const UpdateParameters&,
const EvaluatedLight&,
RenderStaticData&,
- FrameHistory&,
ImageManager&,
LineAtlas&);
@@ -43,7 +42,6 @@ public:
const EvaluatedLight& evaluatedLight;
RenderStaticData& staticData;
- FrameHistory& frameHistory;
ImageManager& imageManager;
LineAtlas& lineAtlas;
@@ -64,15 +62,18 @@ public:
gl::StencilMode stencilModeForClipping(const ClipID&) const;
gl::ColorMode colorModeForRenderPass() const;
- mat4 matrixForTile(const UnwrappedTileID&);
+ mat4 matrixForTile(const UnwrappedTileID&, bool aligned = false) const;
mat4 projMatrix;
+ mat4 alignedProjMatrix;
mat4 nearClippedProjMatrix;
int numSublayers = 3;
uint32_t currentLayer;
float depthRangeSize;
const float depthEpsilon = 1.0f / (1 << 16);
+
+ float symbolFadeChange;
};
} // namespace mbgl
diff --git a/src/mbgl/renderer/paint_property_binder.hpp b/src/mbgl/renderer/paint_property_binder.hpp
index 652948c8df..3a49882f12 100644
--- a/src/mbgl/renderer/paint_property_binder.hpp
+++ b/src/mbgl/renderer/paint_property_binder.hpp
@@ -190,11 +190,11 @@ public:
CompositeFunctionPaintPropertyBinder(style::CompositeFunction<T> function_, float zoom, T defaultValue_)
: function(std::move(function_)),
defaultValue(std::move(defaultValue_)),
- rangeOfCoveringRanges(function.rangeOfCoveringRanges({zoom, zoom + 1})) {
+ zoomRange({zoom, zoom + 1}) {
}
void populateVertexVector(const GeometryTileFeature& feature, std::size_t length) override {
- Range<T> range = function.evaluate(rangeOfCoveringRanges, feature, defaultValue);
+ Range<T> range = function.evaluate(zoomRange, feature, defaultValue);
this->statistics.add(range.min);
this->statistics.add(range.max);
AttributeValue value = zoomInterpolatedAttributeValue(
@@ -219,9 +219,9 @@ public:
float interpolationFactor(float currentZoom) const override {
if (function.useIntegerZoom) {
- return util::interpolationFactor(1.0f, { rangeOfCoveringRanges.min.zoom, rangeOfCoveringRanges.max.zoom }, std::floor(currentZoom));
+ return function.interpolationFactor(zoomRange, std::floor(currentZoom));
} else {
- return util::interpolationFactor(1.0f, { rangeOfCoveringRanges.min.zoom, rangeOfCoveringRanges.max.zoom }, currentZoom);
+ return function.interpolationFactor(zoomRange, currentZoom);
}
}
@@ -237,8 +237,7 @@ public:
private:
style::CompositeFunction<T> function;
T defaultValue;
- using CoveringRanges = typename style::CompositeFunction<T>::CoveringRanges;
- Range<CoveringRanges> rangeOfCoveringRanges;
+ Range<float> zoomRange;
gl::VertexVector<Vertex> vertexVector;
optional<gl::VertexBuffer<Vertex>> vertexBuffer;
};
diff --git a/src/mbgl/renderer/render_layer.cpp b/src/mbgl/renderer/render_layer.cpp
index eb2b74ffe0..bcdc175f14 100644
--- a/src/mbgl/renderer/render_layer.cpp
+++ b/src/mbgl/renderer/render_layer.cpp
@@ -4,9 +4,11 @@
#include <mbgl/renderer/layers/render_custom_layer.hpp>
#include <mbgl/renderer/layers/render_fill_extrusion_layer.hpp>
#include <mbgl/renderer/layers/render_fill_layer.hpp>
+#include <mbgl/renderer/layers/render_hillshade_layer.hpp>
#include <mbgl/renderer/layers/render_line_layer.hpp>
#include <mbgl/renderer/layers/render_raster_layer.hpp>
#include <mbgl/renderer/layers/render_symbol_layer.hpp>
+#include <mbgl/renderer/layers/render_heatmap_layer.hpp>
#include <mbgl/style/types.hpp>
#include <mbgl/renderer/render_tile.hpp>
@@ -26,12 +28,16 @@ std::unique_ptr<RenderLayer> RenderLayer::create(Immutable<Layer::Impl> impl) {
return std::make_unique<RenderSymbolLayer>(staticImmutableCast<SymbolLayer::Impl>(impl));
case LayerType::Raster:
return std::make_unique<RenderRasterLayer>(staticImmutableCast<RasterLayer::Impl>(impl));
+ case LayerType::Hillshade:
+ return std::make_unique<RenderHillshadeLayer>(staticImmutableCast<HillshadeLayer::Impl>(impl));
case LayerType::Background:
return std::make_unique<RenderBackgroundLayer>(staticImmutableCast<BackgroundLayer::Impl>(impl));
case LayerType::Custom:
return std::make_unique<RenderCustomLayer>(staticImmutableCast<CustomLayer::Impl>(impl));
case LayerType::FillExtrusion:
return std::make_unique<RenderFillExtrusionLayer>(staticImmutableCast<FillExtrusionLayer::Impl>(impl));
+ case LayerType::Heatmap:
+ return std::make_unique<RenderHeatmapLayer>(staticImmutableCast<HeatmapLayer::Impl>(impl));
}
// Not reachable, but placate GCC.
diff --git a/src/mbgl/renderer/render_layer.hpp b/src/mbgl/renderer/render_layer.hpp
index dfc6bcf2fd..55831cb72c 100644
--- a/src/mbgl/renderer/render_layer.hpp
+++ b/src/mbgl/renderer/render_layer.hpp
@@ -82,13 +82,16 @@ public:
friend std::string layoutKey(const RenderLayer&);
protected:
+ // renderTiles are exposed directly to CrossTileSymbolIndex and Placement so they
+ // can update opacities in the symbol buckets immediately before rendering
+ friend class CrossTileSymbolIndex;
+ friend class Placement;
+ // Stores current set of tiles to be rendered for this layer.
+ std::vector<std::reference_wrapper<RenderTile>> renderTiles;
+
// Stores what render passes this layer is currently enabled for. This depends on the
// evaluated StyleProperties object and is updated accordingly.
RenderPass passes = RenderPass::None;
-
- //Stores current set of tiles to be rendered for this layer.
- std::vector<std::reference_wrapper<RenderTile>> renderTiles;
-
};
} // namespace mbgl
diff --git a/src/mbgl/renderer/render_source.cpp b/src/mbgl/renderer/render_source.cpp
index 7723a1c7ca..d160eb16e3 100644
--- a/src/mbgl/renderer/render_source.cpp
+++ b/src/mbgl/renderer/render_source.cpp
@@ -2,10 +2,12 @@
#include <mbgl/renderer/render_source_observer.hpp>
#include <mbgl/renderer/sources/render_geojson_source.hpp>
#include <mbgl/renderer/sources/render_raster_source.hpp>
+#include <mbgl/renderer/sources/render_raster_dem_source.hpp>
#include <mbgl/renderer/sources/render_vector_source.hpp>
#include <mbgl/renderer/tile_parameters.hpp>
#include <mbgl/annotation/render_annotation_source.hpp>
#include <mbgl/renderer/sources/render_image_source.hpp>
+#include <mbgl/renderer/sources/render_custom_geometry_source.hpp>
#include <mbgl/tile/tile.hpp>
namespace mbgl {
@@ -18,6 +20,8 @@ std::unique_ptr<RenderSource> RenderSource::create(Immutable<Source::Impl> impl)
return std::make_unique<RenderVectorSource>(staticImmutableCast<VectorSource::Impl>(impl));
case SourceType::Raster:
return std::make_unique<RenderRasterSource>(staticImmutableCast<RasterSource::Impl>(impl));
+ case SourceType::RasterDEM:
+ return std::make_unique<RenderRasterDEMSource>(staticImmutableCast<RasterSource::Impl>(impl));
case SourceType::GeoJSON:
return std::make_unique<RenderGeoJSONSource>(staticImmutableCast<GeoJSONSource::Impl>(impl));
case SourceType::Video:
@@ -27,6 +31,8 @@ std::unique_ptr<RenderSource> RenderSource::create(Immutable<Source::Impl> impl)
return std::make_unique<RenderAnnotationSource>(staticImmutableCast<AnnotationSource::Impl>(impl));
case SourceType::Image:
return std::make_unique<RenderImageSource>(staticImmutableCast<ImageSource::Impl>(impl));
+ case SourceType::CustomVector:
+ return std::make_unique<RenderCustomGeometrySource>(staticImmutableCast<CustomGeometrySource::Impl>(impl));
}
// Not reachable, but placate GCC.
diff --git a/src/mbgl/renderer/render_source.hpp b/src/mbgl/renderer/render_source.hpp
index 8293923ff6..53519c763e 100644
--- a/src/mbgl/renderer/render_source.hpp
+++ b/src/mbgl/renderer/render_source.hpp
@@ -24,6 +24,7 @@ class SourceQueryOptions;
class Tile;
class RenderSourceObserver;
class TileParameters;
+class CollisionIndex;
class RenderSource : protected TileObserver {
public:
@@ -63,12 +64,13 @@ public:
queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
const std::vector<const RenderLayer*>& layers,
- const RenderedQueryOptions& options) const = 0;
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) const = 0;
virtual std::vector<Feature>
querySourceFeatures(const SourceQueryOptions&) const = 0;
- virtual void onLowMemory() = 0;
+ virtual void reduceMemoryUse() = 0;
virtual void dumpDebugLogs() const = 0;
@@ -82,7 +84,7 @@ protected:
bool enabled = false;
- void onTileChanged(Tile&) final;
+ void onTileChanged(Tile&) override;
void onTileError(Tile&, std::exception_ptr) final;
};
diff --git a/src/mbgl/renderer/render_static_data.cpp b/src/mbgl/renderer/render_static_data.cpp
index ccf239e643..0b3937ded0 100644
--- a/src/mbgl/renderer/render_static_data.cpp
+++ b/src/mbgl/renderer/render_static_data.cpp
@@ -3,12 +3,12 @@
namespace mbgl {
-static gl::VertexVector<FillLayoutVertex> tileVertices() {
- gl::VertexVector<FillLayoutVertex> result;
- result.emplace_back(FillProgram::layoutVertex({ 0, 0 }));
- result.emplace_back(FillProgram::layoutVertex({ util::EXTENT, 0 }));
- result.emplace_back(FillProgram::layoutVertex({ 0, util::EXTENT }));
- result.emplace_back(FillProgram::layoutVertex({ util::EXTENT, util::EXTENT }));
+static gl::VertexVector<PositionOnlyLayoutAttributes::Vertex> tileVertices() {
+ gl::VertexVector<PositionOnlyLayoutAttributes::Vertex> result;
+ result.emplace_back(PositionOnlyLayoutAttributes::Vertex({{{ 0, 0 }}}));
+ result.emplace_back(PositionOnlyLayoutAttributes::Vertex({{{ util::EXTENT, 0 }}}));
+ result.emplace_back(PositionOnlyLayoutAttributes::Vertex({{{ 0, util::EXTENT }}}));
+ result.emplace_back(PositionOnlyLayoutAttributes::Vertex({{{ util::EXTENT, util::EXTENT }}}));
return result;
}
diff --git a/src/mbgl/renderer/render_static_data.hpp b/src/mbgl/renderer/render_static_data.hpp
index cf58c31f4d..c2b54f3815 100644
--- a/src/mbgl/renderer/render_static_data.hpp
+++ b/src/mbgl/renderer/render_static_data.hpp
@@ -13,14 +13,14 @@ class RenderStaticData {
public:
RenderStaticData(gl::Context&, float pixelRatio, const optional<std::string>& programCacheDir);
- gl::VertexBuffer<FillLayoutVertex> tileVertexBuffer;
+ gl::VertexBuffer<PositionOnlyLayoutAttributes::Vertex> tileVertexBuffer;
gl::VertexBuffer<RasterLayoutVertex> rasterVertexBuffer;
gl::VertexBuffer<ExtrusionTextureLayoutVertex> extrusionTextureVertexBuffer;
gl::IndexBuffer<gl::Triangles> quadTriangleIndexBuffer;
gl::IndexBuffer<gl::LineStrip> tileBorderIndexBuffer;
- SegmentVector<FillAttributes> tileTriangleSegments;
+ SegmentVector<BackgroundAttributes> tileTriangleSegments;
SegmentVector<DebugAttributes> tileBorderSegments;
SegmentVector<RasterAttributes> rasterSegments;
SegmentVector<ExtrusionTextureAttributes> extrusionTextureSegments;
diff --git a/src/mbgl/renderer/render_tile.cpp b/src/mbgl/renderer/render_tile.cpp
index 8df31f8d7c..35b34833e4 100644
--- a/src/mbgl/renderer/render_tile.cpp
+++ b/src/mbgl/renderer/render_tile.cpp
@@ -72,7 +72,7 @@ void RenderTile::finishRender(PaintParameters& parameters) {
return;
static const style::Properties<>::PossiblyEvaluated properties {};
- static const DebugProgram::PaintPropertyBinders paintAttibuteData(properties, 0);
+ static const DebugProgram::PaintPropertyBinders paintAttributeData(properties, 0);
if (parameters.debugOptions & (MapDebugOptions::Timestamps | MapDebugOptions::ParseStatus)) {
if (!tile.debugBucket || tile.debugBucket->renderable != tile.isRenderable() ||
@@ -98,7 +98,7 @@ void RenderTile::finishRender(PaintParameters& parameters) {
*tile.debugBucket->vertexBuffer,
*tile.debugBucket->indexBuffer,
tile.debugBucket->segments,
- paintAttibuteData,
+ paintAttributeData,
properties,
parameters.state.getZoom(),
"debug"
@@ -117,7 +117,7 @@ void RenderTile::finishRender(PaintParameters& parameters) {
*tile.debugBucket->vertexBuffer,
*tile.debugBucket->indexBuffer,
tile.debugBucket->segments,
- paintAttibuteData,
+ paintAttributeData,
properties,
parameters.state.getZoom(),
"debug"
@@ -138,7 +138,7 @@ void RenderTile::finishRender(PaintParameters& parameters) {
parameters.staticData.tileVertexBuffer,
parameters.staticData.tileBorderIndexBuffer,
parameters.staticData.tileBorderSegments,
- paintAttibuteData,
+ paintAttributeData,
properties,
parameters.state.getZoom(),
"debug"
diff --git a/src/mbgl/renderer/renderer.cpp b/src/mbgl/renderer/renderer.cpp
index 6d086c70b1..1d2f2bb522 100644
--- a/src/mbgl/renderer/renderer.cpp
+++ b/src/mbgl/renderer/renderer.cpp
@@ -94,9 +94,9 @@ void Renderer::dumpDebugLogs() {
impl->dumDebugLogs();
}
-void Renderer::onLowMemory() {
+void Renderer::reduceMemoryUse() {
BackendScope guard { impl->backend };
- impl->onLowMemory();
+ impl->reduceMemoryUse();
}
} // namespace mbgl
diff --git a/src/mbgl/renderer/renderer_backend.cpp b/src/mbgl/renderer/renderer_backend.cpp
index 159ef432b3..22d263313c 100644
--- a/src/mbgl/renderer/renderer_backend.cpp
+++ b/src/mbgl/renderer/renderer_backend.cpp
@@ -16,7 +16,7 @@ gl::Context& RendererBackend::getContext() {
context = std::make_unique<gl::Context>();
context->enableDebugging();
context->initializeExtensions(
- std::bind(&RendererBackend::initializeExtension, this, std::placeholders::_1));
+ std::bind(&RendererBackend::getExtensionFunctionPointer, this, std::placeholders::_1));
});
return *context;
}
diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp
index 3a7afdb03d..2ac714e122 100644
--- a/src/mbgl/renderer/renderer_impl.cpp
+++ b/src/mbgl/renderer/renderer_impl.cpp
@@ -14,6 +14,8 @@
#include <mbgl/renderer/layers/render_background_layer.hpp>
#include <mbgl/renderer/layers/render_custom_layer.hpp>
#include <mbgl/renderer/layers/render_fill_extrusion_layer.hpp>
+#include <mbgl/renderer/layers/render_heatmap_layer.hpp>
+#include <mbgl/renderer/layers/render_hillshade_layer.hpp>
#include <mbgl/renderer/style_diff.hpp>
#include <mbgl/renderer/query.hpp>
#include <mbgl/renderer/backend_scope.hpp>
@@ -57,7 +59,8 @@ Renderer::Impl::Impl(RendererBackend& backend_,
, imageImpls(makeMutable<std::vector<Immutable<style::Image::Impl>>>())
, sourceImpls(makeMutable<std::vector<Immutable<style::Source::Impl>>>())
, layerImpls(makeMutable<std::vector<Immutable<style::Layer::Impl>>>())
- , renderLight(makeMutable<Light::Impl>()) {
+ , renderLight(makeMutable<Light::Impl>())
+ , placement(std::make_unique<Placement>(TransformState{}, MapMode::Static)) {
glyphManager->setObserver(this);
}
@@ -81,7 +84,7 @@ void Renderer::Impl::setObserver(RendererObserver* observer_) {
}
void Renderer::Impl::render(const UpdateParameters& updateParameters) {
- if (updateParameters.mode == MapMode::Still) {
+ if (updateParameters.mode != MapMode::Continuous) {
// Don't load/render anyting in still mode until explicitly requested.
if (!updateParameters.stillImageRequest) {
return;
@@ -183,6 +186,10 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) {
if (layerAdded || layerChanged) {
layer.transition(transitionParameters);
+
+ if (layer.is<RenderHeatmapLayer>()) {
+ layer.as<RenderHeatmapLayer>()->updateColorRamp();
+ }
}
if (layerAdded || layerChanged || zoomChanged || layer.hasTransition()) {
@@ -253,13 +260,12 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) {
updateParameters,
renderLight.getEvaluated(),
*staticData,
- frameHistory,
*imageManager,
*lineAtlas
};
bool loaded = updateParameters.styleLoaded && isLoaded();
- if (updateParameters.mode == MapMode::Still && !loaded) {
+ if (updateParameters.mode != MapMode::Continuous && !loaded) {
return;
}
@@ -289,7 +295,11 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) {
RenderLayer* layer = getRenderLayer(layerImpl->id);
assert(layer);
- if (!parameters.staticData.has3D && layer->is<RenderFillExtrusionLayer>()) {
+ if (!parameters.staticData.has3D && (
+ layer->is<RenderFillExtrusionLayer>() ||
+ layer->is<RenderHillshadeLayer>() ||
+ layer->is<RenderHeatmapLayer>())) {
+
parameters.staticData.has3D = true;
}
@@ -299,7 +309,9 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) {
if (const RenderBackgroundLayer* background = layer->as<RenderBackgroundLayer>()) {
const BackgroundPaintProperties::PossiblyEvaluated& paint = background->evaluated;
- if (layerImpl.get() == layerImpls->at(0).get() && paint.get<BackgroundPattern>().from.empty()) {
+ if (parameters.contextMode == GLContextMode::Unique
+ && layerImpl.get() == layerImpls->at(0).get()
+ && paint.get<BackgroundPattern>().from.empty()) {
// This is a solid background. We can use glClear().
backgroundColor = paint.get<BackgroundColor>() * paint.get<BackgroundOpacity>();
} else {
@@ -333,11 +345,15 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) {
auto par = util::rotate(pa, parameters.state.getAngle());
auto pbr = util::rotate(pb, parameters.state.getAngle());
- return std::tie(par.y, par.x) < std::tie(pbr.y, pbr.x);
+ return std::tie(b.id.canonical.z, par.y, par.x) < std::tie(a.id.canonical.z, pbr.y, pbr.x);
});
} else {
std::sort(sortedTiles.begin(), sortedTiles.end(),
[](const auto& a, const auto& b) { return a.get().id < b.get().id; });
+ // Don't render non-symbol layers for tiles that we're only holding on to for symbol fading
+ sortedTiles.erase(std::remove_if(sortedTiles.begin(), sortedTiles.end(),
+ [](const auto& tile) { return tile.get().tile.holdForFade(); }),
+ sortedTiles.end());
}
std::vector<std::reference_wrapper<RenderTile>> sortedTilesForInsertion;
@@ -347,35 +363,13 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) {
continue;
}
- // We're not clipping symbol layers, so when we have both parents and children of symbol
- // layers, we drop all children in favor of their parent to avoid duplicate labels.
- // See https://github.com/mapbox/mapbox-gl-native/issues/2482
- if (symbolLayer) {
- bool skip = false;
- // Look back through the buckets we decided to render to find out whether there is
- // already a bucket from this layer that is a parent of this tile. Tiles are ordered
- // by zoom level when we obtain them from getTiles().
- for (auto it = sortedTilesForInsertion.rbegin();
- it != sortedTilesForInsertion.rend(); ++it) {
- if (tile.tile.id.isChildOf(it->get().tile.id)) {
- skip = true;
- break;
- }
- }
- if (skip) {
- continue;
- }
- }
-
auto bucket = tile.tile.getBucket(*layer->baseImpl);
if (bucket) {
sortedTilesForInsertion.emplace_back(tile);
tile.used = true;
- // We only need clipping when we're _not_ drawing a symbol layer. The only exception
- // for symbol layers is when we're rendering still images. See render_symbol_layer.cpp
- // for the exception we make there.
- if (!symbolLayer || parameters.mapMode == MapMode::Still) {
+ // We only need clipping when we're _not_ drawing a symbol layer.
+ if (!symbolLayer) {
tile.needsClipping = true;
}
}
@@ -384,9 +378,54 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) {
order.emplace_back(RenderItem { *layer, source });
}
- frameHistory.record(parameters.timePoint,
- parameters.state.getZoom(),
- parameters.mapMode == MapMode::Continuous ? util::DEFAULT_TRANSITION_DURATION : Milliseconds(0));
+ bool symbolBucketsChanged = false;
+ if (parameters.mapMode != MapMode::Continuous) {
+ // TODO: Think about right way for symbol index to handle still rendering
+ crossTileSymbolIndex.reset();
+ }
+ for (auto it = order.rbegin(); it != order.rend(); ++it) {
+ if (it->layer.is<RenderSymbolLayer>()) {
+ if (crossTileSymbolIndex.addLayer(*it->layer.as<RenderSymbolLayer>())) symbolBucketsChanged = true;
+ }
+ }
+
+ bool placementChanged = false;
+ if (!placement->stillRecent(parameters.timePoint)) {
+ auto newPlacement = std::make_unique<Placement>(parameters.state, parameters.mapMode);
+ std::set<std::string> usedSymbolLayers;
+ for (auto it = order.rbegin(); it != order.rend(); ++it) {
+ if (it->layer.is<RenderSymbolLayer>()) {
+ usedSymbolLayers.insert(it->layer.getID());
+ newPlacement->placeLayer(*it->layer.as<RenderSymbolLayer>(), parameters.projMatrix, parameters.debugOptions & MapDebugOptions::Collision);
+ }
+ }
+
+ placementChanged = newPlacement->commit(*placement, parameters.timePoint);
+ // commitFeatureIndexes depends on the assumption that no new FeatureIndex has been loaded since placement
+ // started. If we violate this assumption, then we need to either make CollisionIndex completely independendent of
+ // FeatureIndex, or find a way for its entries to point to multiple FeatureIndexes.
+ commitFeatureIndexes();
+ crossTileSymbolIndex.pruneUnusedLayers(usedSymbolLayers);
+ if (placementChanged || symbolBucketsChanged) {
+ placement = std::move(newPlacement);
+ }
+
+ placement->setRecent(parameters.timePoint);
+
+ updateFadingTiles();
+ } else {
+ placement->setStale();
+ }
+
+ parameters.symbolFadeChange = placement->symbolFadeChange(parameters.timePoint);
+
+ if (placementChanged || symbolBucketsChanged) {
+ for (auto it = order.rbegin(); it != order.rend(); ++it) {
+ if (it->layer.is<RenderSymbolLayer>()) {
+ placement->updateLayerOpacities(*it->layer.as<RenderSymbolLayer>());
+ }
+ }
+ }
// - UPLOAD PASS -------------------------------------------------------------------------------
// Uploads all required buffers and images before we do any actual rendering.
@@ -395,8 +434,7 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) {
parameters.imageManager.upload(parameters.context, 0);
parameters.lineAtlas.upload(parameters.context, 0);
- parameters.frameHistory.upload(parameters.context, 0);
-
+
// Update all clipping IDs + upload buckets.
for (const auto& entry : renderSources) {
if (entry.second->isEnabled()) {
@@ -435,13 +473,17 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) {
// Renders the backdrop of the OpenGL view. This also paints in areas where we don't have any
// tiles whatsoever.
{
+ using namespace gl::value;
+
MBGL_DEBUG_GROUP(parameters.context, "clear");
parameters.backend.bind();
- parameters.context.clear((parameters.debugOptions & MapDebugOptions::Overdraw)
- ? Color::black()
- : backgroundColor,
- 1.0f,
- 0);
+ if (parameters.debugOptions & MapDebugOptions::Overdraw) {
+ parameters.context.clear(Color::black(), ClearDepth::Default, ClearStencil::Default);
+ } else if (parameters.contextMode == GLContextMode::Shared) {
+ parameters.context.clear({}, ClearDepth::Default, ClearStencil::Default);
+ } else {
+ parameters.context.clear(backgroundColor, ClearDepth::Default, ClearStencil::Default);
+ }
}
// - CLIPPING MASKS ----------------------------------------------------------------------------
@@ -449,11 +491,11 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) {
{
MBGL_DEBUG_GROUP(parameters.context, "clipping masks");
- static const style::FillPaintProperties::PossiblyEvaluated properties {};
- static const FillProgram::PaintPropertyBinders paintAttibuteData(properties, 0);
+ static const Properties<>::PossiblyEvaluated properties {};
+ static const ClippingMaskProgram::PaintPropertyBinders paintAttributeData(properties, 0);
for (const auto& clipID : parameters.clipIDGenerator.getClipIDs()) {
- parameters.staticData.programs.fill.get(properties).draw(
+ parameters.staticData.programs.clippingMask.draw(
parameters.context,
gl::Triangles(),
gl::DepthMode::disabled(),
@@ -466,14 +508,13 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) {
gl::StencilMode::Replace
},
gl::ColorMode::disabled(),
- FillProgram::UniformValues {
+ ClippingMaskProgram::UniformValues {
uniforms::u_matrix::Value{ parameters.matrixForTile(clipID.first) },
- uniforms::u_world::Value{ parameters.context.viewport.getCurrentValue().size },
},
parameters.staticData.tileVertexBuffer,
parameters.staticData.quadTriangleIndexBuffer,
parameters.staticData.tileTriangleSegments,
- paintAttibuteData,
+ paintAttributeData,
properties,
parameters.state.getZoom(),
"clipping"
@@ -602,7 +643,7 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) {
observer->onDidFinishRenderingFrame(
loaded ? RendererObserver::RenderMode::Full : RendererObserver::RenderMode::Partial,
- updateParameters.mode == MapMode::Continuous && (hasTransitions() || frameHistory.needsAnimation(util::DEFAULT_TRANSITION_DURATION))
+ updateParameters.mode == MapMode::Continuous && hasTransitions(parameters.timePoint)
);
if (!loaded) {
@@ -642,7 +683,7 @@ std::vector<Feature> Renderer::Impl::queryRenderedFeatures(const ScreenLineStrin
std::unordered_map<std::string, std::vector<Feature>> resultsByLayer;
for (const auto& sourceID : sourceIDs) {
if (RenderSource* renderSource = getRenderSource(sourceID)) {
- auto sourceResults = renderSource->queryRenderedFeatures(geometry, transformState, layers, options);
+ auto sourceResults = renderSource->queryRenderedFeatures(geometry, transformState, layers, options, placement->getCollisionIndex());
std::move(sourceResults.begin(), sourceResults.end(), std::inserter(resultsByLayer, resultsByLayer.begin()));
}
}
@@ -690,12 +731,12 @@ std::vector<Feature> Renderer::Impl::querySourceFeatures(const std::string& sour
return source->querySourceFeatures(options);
}
-void Renderer::Impl::onLowMemory() {
+void Renderer::Impl::reduceMemoryUse() {
assert(BackendScope::exists());
- backend.getContext().performCleanup();
for (const auto& entry : renderSources) {
- entry.second->onLowMemory();
+ entry.second->reduceMemoryUse();
}
+ backend.getContext().performCleanup();
observer->onInvalidate();
}
@@ -722,7 +763,7 @@ RenderSource* Renderer::Impl::getRenderSource(const std::string& id) const {
return it != renderSources.end() ? it->second.get() : nullptr;
}
-bool Renderer::Impl::hasTransitions() const {
+bool Renderer::Impl::hasTransitions(TimePoint timePoint) const {
if (renderLight.hasTransition()) {
return true;
}
@@ -733,9 +774,39 @@ bool Renderer::Impl::hasTransitions() const {
}
}
+ if (placement->hasTransitions(timePoint)) {
+ return true;
+ }
+
+ if (fadingTiles) {
+ return true;
+ }
+
return false;
}
+void Renderer::Impl::commitFeatureIndexes() {
+ for (auto& source : renderSources) {
+ for (auto& renderTile : source.second->getRenderTiles()) {
+ Tile& tile = renderTile.get().tile;
+ tile.commitFeatureIndex();
+ }
+ }
+}
+
+void Renderer::Impl::updateFadingTiles() {
+ fadingTiles = false;
+ for (auto& source : renderSources) {
+ for (auto& renderTile : source.second->getRenderTiles()) {
+ Tile& tile = renderTile.get().tile;
+ if (tile.holdForFade()) {
+ fadingTiles = true;
+ tile.performedFadePlacement();
+ }
+ }
+ }
+}
+
bool Renderer::Impl::isLoaded() const {
for (const auto& entry: renderSources) {
if (!entry.second->isLoaded()) {
diff --git a/src/mbgl/renderer/renderer_impl.hpp b/src/mbgl/renderer/renderer_impl.hpp
index a199cec4d0..4675ac79ea 100644
--- a/src/mbgl/renderer/renderer_impl.hpp
+++ b/src/mbgl/renderer/renderer_impl.hpp
@@ -1,16 +1,17 @@
#pragma once
+#include <mbgl/renderer/mode.hpp>
#include <mbgl/renderer/renderer.hpp>
#include <mbgl/renderer/render_source_observer.hpp>
#include <mbgl/renderer/render_light.hpp>
-#include <mbgl/renderer/frame_history.hpp>
#include <mbgl/style/image.hpp>
#include <mbgl/style/source.hpp>
#include <mbgl/style/layer.hpp>
#include <mbgl/map/transform_state.hpp>
#include <mbgl/map/zoom_history.hpp>
-#include <mbgl/map/mode.hpp>
+#include <mbgl/text/cross_tile_symbol_index.hpp>
#include <mbgl/text/glyph_manager_observer.hpp>
+#include <mbgl/text/placement.hpp>
#include <memory>
#include <string>
@@ -31,6 +32,7 @@ class Scheduler;
class GlyphManager;
class ImageManager;
class LineAtlas;
+class CrossTileSymbolIndex;
class Renderer::Impl : public GlyphManagerObserver,
public RenderSourceObserver{
@@ -51,12 +53,12 @@ public:
std::vector<Feature> querySourceFeatures(const std::string& sourceID, const SourceQueryOptions&) const;
std::vector<Feature> queryShapeAnnotations(const ScreenLineString&) const;
- void onLowMemory();
+ void reduceMemoryUse();
void dumDebugLogs();
private:
bool isLoaded() const;
- bool hasTransitions() const;
+ bool hasTransitions(TimePoint) const;
RenderSource* getRenderSource(const std::string& id) const;
@@ -72,6 +74,9 @@ private:
void onTileChanged(RenderSource&, const OverscaledTileID&) override;
void onTileError(RenderSource&, const OverscaledTileID&, std::exception_ptr) override;
+ void commitFeatureIndexes();
+ void updateFadingTiles();
+
friend class Renderer;
RendererBackend& backend;
@@ -91,7 +96,6 @@ private:
};
RenderState renderState = RenderState::Never;
- FrameHistory frameHistory;
ZoomHistory zoomHistory;
TransformState transformState;
@@ -108,7 +112,11 @@ private:
std::unordered_map<std::string, std::unique_ptr<RenderLayer>> renderLayers;
RenderLight renderLight;
+ CrossTileSymbolIndex crossTileSymbolIndex;
+ std::unique_ptr<Placement> placement;
+
bool contextLost = false;
+ bool fadingTiles = false;
};
} // namespace mbgl
diff --git a/src/mbgl/renderer/sources/render_custom_geometry_source.cpp b/src/mbgl/renderer/sources/render_custom_geometry_source.cpp
new file mode 100644
index 0000000000..057ad5a4a7
--- /dev/null
+++ b/src/mbgl/renderer/sources/render_custom_geometry_source.cpp
@@ -0,0 +1,87 @@
+#include <mbgl/renderer/sources/render_custom_geometry_source.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/tile/custom_geometry_tile.hpp>
+
+#include <mbgl/algorithm/generate_clip_ids.hpp>
+#include <mbgl/algorithm/generate_clip_ids_impl.hpp>
+
+namespace mbgl {
+
+using namespace style;
+
+RenderCustomGeometrySource::RenderCustomGeometrySource(Immutable<style::CustomGeometrySource::Impl> impl_)
+ : RenderSource(impl_) {
+ tilePyramid.setObserver(this);
+}
+
+const style::CustomGeometrySource::Impl& RenderCustomGeometrySource::impl() const {
+ return static_cast<const style::CustomGeometrySource::Impl&>(*baseImpl);
+}
+
+bool RenderCustomGeometrySource::isLoaded() const {
+ return tilePyramid.isLoaded();
+}
+
+void RenderCustomGeometrySource::update(Immutable<style::Source::Impl> baseImpl_,
+ const std::vector<Immutable<Layer::Impl>>& layers,
+ const bool needsRendering,
+ const bool needsRelayout,
+ const TileParameters& parameters) {
+ std::swap(baseImpl, baseImpl_);
+
+ enabled = needsRendering;
+
+ auto tileLoader = impl().getTileLoader();
+ if (!tileLoader) {
+ return;
+ }
+
+ tilePyramid.update(layers,
+ needsRendering,
+ needsRelayout,
+ parameters,
+ SourceType::CustomVector,
+ util::tileSize,
+ impl().getZoomRange(),
+ {},
+ [&] (const OverscaledTileID& tileID) {
+ return std::make_unique<CustomGeometryTile>(tileID, impl().id, parameters, impl().getTileOptions(), *tileLoader);
+ });
+}
+
+void RenderCustomGeometrySource::startRender(PaintParameters& parameters) {
+ parameters.clipIDGenerator.update(tilePyramid.getRenderTiles());
+ tilePyramid.startRender(parameters);
+}
+
+void RenderCustomGeometrySource::finishRender(PaintParameters& parameters) {
+ tilePyramid.finishRender(parameters);
+}
+
+std::vector<std::reference_wrapper<RenderTile>> RenderCustomGeometrySource::getRenderTiles() {
+ return tilePyramid.getRenderTiles();
+}
+
+std::unordered_map<std::string, std::vector<Feature>>
+RenderCustomGeometrySource::queryRenderedFeatures(const ScreenLineString& geometry,
+ const TransformState& transformState,
+ const std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) const {
+ return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, collisionIndex);
+}
+
+std::vector<Feature> RenderCustomGeometrySource::querySourceFeatures(const SourceQueryOptions& options) const {
+ return tilePyramid.querySourceFeatures(options);
+}
+
+void RenderCustomGeometrySource::reduceMemoryUse() {
+ tilePyramid.reduceMemoryUse();
+}
+
+void RenderCustomGeometrySource::dumpDebugLogs() const {
+ tilePyramid.dumpDebugLogs();
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/sources/render_custom_geometry_source.hpp b/src/mbgl/renderer/sources/render_custom_geometry_source.hpp
new file mode 100644
index 0000000000..033d731029
--- /dev/null
+++ b/src/mbgl/renderer/sources/render_custom_geometry_source.hpp
@@ -0,0 +1,50 @@
+#pragma once
+
+#include <mbgl/renderer/render_source.hpp>
+#include <mbgl/renderer/tile_pyramid.hpp>
+#include <mbgl/style/sources/custom_geometry_source_impl.hpp>
+
+namespace mbgl {
+
+class RenderCustomGeometrySource : public RenderSource {
+public:
+ RenderCustomGeometrySource(Immutable<style::CustomGeometrySource::Impl>);
+
+ bool isLoaded() const final;
+
+ void update(Immutable<style::Source::Impl>,
+ const std::vector<Immutable<style::Layer::Impl>>&,
+ bool needsRendering,
+ bool needsRelayout,
+ const TileParameters&) final;
+
+ void startRender(PaintParameters&) final;
+ void finishRender(PaintParameters&) final;
+
+ std::vector<std::reference_wrapper<RenderTile>> getRenderTiles() final;
+
+ std::unordered_map<std::string, std::vector<Feature>>
+ queryRenderedFeatures(const ScreenLineString& geometry,
+ const TransformState& transformState,
+ const std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) const final;
+
+ std::vector<Feature>
+ querySourceFeatures(const SourceQueryOptions&) const final;
+
+ void reduceMemoryUse() final;
+ void dumpDebugLogs() const final;
+
+private:
+ const style::CustomGeometrySource::Impl& impl() const;
+
+ TilePyramid tilePyramid;
+};
+
+template <>
+inline bool RenderSource::is<RenderCustomGeometrySource>() const {
+ return baseImpl->type == style::SourceType::CustomVector;
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/sources/render_geojson_source.cpp b/src/mbgl/renderer/sources/render_geojson_source.cpp
index c13cd49f46..cbf4db70b5 100644
--- a/src/mbgl/renderer/sources/render_geojson_source.cpp
+++ b/src/mbgl/renderer/sources/render_geojson_source.cpp
@@ -85,16 +85,17 @@ std::unordered_map<std::string, std::vector<Feature>>
RenderGeoJSONSource::queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
const std::vector<const RenderLayer*>& layers,
- const RenderedQueryOptions& options) const {
- return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options);
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) const {
+ return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, collisionIndex);
}
std::vector<Feature> RenderGeoJSONSource::querySourceFeatures(const SourceQueryOptions& options) const {
return tilePyramid.querySourceFeatures(options);
}
-void RenderGeoJSONSource::onLowMemory() {
- tilePyramid.onLowMemory();
+void RenderGeoJSONSource::reduceMemoryUse() {
+ tilePyramid.reduceMemoryUse();
}
void RenderGeoJSONSource::dumpDebugLogs() const {
diff --git a/src/mbgl/renderer/sources/render_geojson_source.hpp b/src/mbgl/renderer/sources/render_geojson_source.hpp
index 72ab4879ef..72fccbd043 100644
--- a/src/mbgl/renderer/sources/render_geojson_source.hpp
+++ b/src/mbgl/renderer/sources/render_geojson_source.hpp
@@ -31,12 +31,13 @@ public:
queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
const std::vector<const RenderLayer*>& layers,
- const RenderedQueryOptions& options) const final;
+ const RenderedQueryOptions& options,
+ const CollisionIndex&) const final;
std::vector<Feature>
querySourceFeatures(const SourceQueryOptions&) const final;
- void onLowMemory() final;
+ void reduceMemoryUse() final;
void dumpDebugLogs() const final;
private:
@@ -48,7 +49,7 @@ private:
template <>
inline bool RenderSource::is<RenderGeoJSONSource>() const {
- return baseImpl->type == SourceType::GeoJSON;
+ return baseImpl->type == style::SourceType::GeoJSON;
}
} // namespace mbgl
diff --git a/src/mbgl/renderer/sources/render_image_source.cpp b/src/mbgl/renderer/sources/render_image_source.cpp
index 9140e01711..31a5916a34 100644
--- a/src/mbgl/renderer/sources/render_image_source.cpp
+++ b/src/mbgl/renderer/sources/render_image_source.cpp
@@ -41,7 +41,7 @@ void RenderImageSource::startRender(PaintParameters& parameters) {
mat4 matrix;
matrix::identity(matrix);
parameters.state.matrixFor(matrix, tileIds[i]);
- matrix::multiply(matrix, parameters.projMatrix, matrix);
+ matrix::multiply(matrix, parameters.alignedProjMatrix, matrix);
matrices.push_back(matrix);
}
@@ -56,7 +56,7 @@ void RenderImageSource::finishRender(PaintParameters& parameters) {
}
static const style::Properties<>::PossiblyEvaluated properties {};
- static const DebugProgram::PaintPropertyBinders paintAttibuteData(properties, 0);
+ static const DebugProgram::PaintPropertyBinders paintAttributeData(properties, 0);
for (auto matrix : matrices) {
parameters.programs.debug.draw(
@@ -72,7 +72,7 @@ void RenderImageSource::finishRender(PaintParameters& parameters) {
parameters.staticData.tileVertexBuffer,
parameters.staticData.tileBorderIndexBuffer,
parameters.staticData.tileBorderSegments,
- paintAttibuteData,
+ paintAttributeData,
properties,
parameters.state.getZoom(),
"debug"
@@ -84,7 +84,8 @@ std::unordered_map<std::string, std::vector<Feature>>
RenderImageSource::queryRenderedFeatures(const ScreenLineString&,
const TransformState&,
const std::vector<const RenderLayer*>&,
- const RenderedQueryOptions&) const {
+ const RenderedQueryOptions&,
+ const CollisionIndex&) const {
return std::unordered_map<std::string, std::vector<Feature>> {};
}
@@ -113,44 +114,43 @@ void RenderImageSource::update(Immutable<style::Source::Impl> baseImpl_,
return;
}
- auto size = transformState.getSize();
- const double viewportHeight = size.height;
-
- // Compute the screen coordinates at wrap=0 for the given LatLng
- ScreenCoordinate nePixel = { -INFINITY, -INFINITY };
- ScreenCoordinate swPixel = { INFINITY, INFINITY };
-
+ // Compute the z0 tile coordinates for the given LatLngs
+ TileCoordinatePoint nePoint = { -INFINITY, -INFINITY };
+ TileCoordinatePoint swPoint = { INFINITY, INFINITY };
+ std::vector<TileCoordinatePoint> tileCoordinates;
for (LatLng latLng : coords) {
- ScreenCoordinate pixel = transformState.latLngToScreenCoordinate(latLng);
- swPixel.x = std::min(swPixel.x, pixel.x);
- nePixel.x = std::max(nePixel.x, pixel.x);
- swPixel.y = std::min(swPixel.y, viewportHeight - pixel.y);
- nePixel.y = std::max(nePixel.y, viewportHeight - pixel.y);
- }
- const double width = nePixel.x - swPixel.x;
- const double height = nePixel.y - swPixel.y;
+ auto point = TileCoordinate::fromLatLng(0, latLng).p;
+ tileCoordinates.push_back(point);
+ swPoint.x = std::min(swPoint.x, point.x);
+ nePoint.x = std::max(nePoint.x, point.x);
+ swPoint.y = std::min(swPoint.y, point.y);
+ nePoint.y = std::max(nePoint.y, point.y);
+ }
- // Don't bother drawing the ImageSource unless it occupies >4 screen pixels
- enabled = (width * height > 4);
+ // Calculate the optimum zoom level to determine the tile ids to use for transforms
+ auto dx = nePoint.x - swPoint.x;
+ auto dy = nePoint.y - swPoint.y;
+ auto dMax = std::max(dx, dy);
+ double zoom = std::max(0.0, std::floor(-util::log2(dMax)));
+
+ // Only enable if the long side of the image is > 2 pixels. Resulting in a
+ // display of at least 2 x 1 px image
+ // A tile coordinate unit represents the length of one tile (tileSize) at a given zoom.
+ // To convert a tile coordinate to pixels, multiply by tileSize.
+ // Here dMax is in z0 tile units, so we also scale by 2^z to match current zoom.
+ enabled = dMax * std::pow(2.0, transformState.getZoom()) * util::tileSize > 2.0;
if (!enabled) {
return;
}
- // Calculate the optimum zoom level to determine the tile ids to use for transforms
- double minScale = INFINITY;
- double scaleX = double(size.width) / width;
- double scaleY = double(size.height) / height;
- minScale = util::min(scaleX, scaleY);
- double zoom = transformState.getZoom() + util::log2(minScale);
- zoom = std::floor(util::clamp(zoom, transformState.getMinZoom(), transformState.getMaxZoom()));
auto imageBounds = LatLngBounds::hull(coords[0], coords[1]);
imageBounds.extend(coords[2]);
imageBounds.extend(coords[3]);
auto tileCover = util::tileCover(imageBounds, zoom);
tileIds.clear();
tileIds.push_back(tileCover[0]);
- bool hasVisibleTile = false;
+ bool hasVisibleTile = false;
// Add additional wrapped tile ids if neccessary
auto idealTiles = util::tileCover(transformState, transformState.getZoom());
for (auto tile : idealTiles) {
@@ -176,9 +176,8 @@ void RenderImageSource::update(Immutable<style::Source::Impl> baseImpl_,
// Calculate Geometry Coordinates based on tile cover at ideal zoom
GeometryCoordinates geomCoords;
- for (auto latLng : coords) {
- auto tc = TileCoordinate::fromLatLng(0, latLng);
- auto gc = TileCoordinate::toGeometryCoordinate(tileIds[0], tc.p);
+ for (auto tileCoords : tileCoordinates) {
+ auto gc = TileCoordinate::toGeometryCoordinate(tileIds[0], tileCoords);
geomCoords.push_back(gc);
}
if (!bucket) {
diff --git a/src/mbgl/renderer/sources/render_image_source.hpp b/src/mbgl/renderer/sources/render_image_source.hpp
index 7b69d09fa7..85ee0ace11 100644
--- a/src/mbgl/renderer/sources/render_image_source.hpp
+++ b/src/mbgl/renderer/sources/render_image_source.hpp
@@ -32,11 +32,12 @@ public:
queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
const std::vector<const RenderLayer*>& layers,
- const RenderedQueryOptions& options) const final;
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) const final;
std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const final;
- void onLowMemory() final {
+ void reduceMemoryUse() final {
}
void dumpDebugLogs() const final;
@@ -52,7 +53,7 @@ private:
template <>
inline bool RenderSource::is<RenderImageSource>() const {
- return baseImpl->type == SourceType::Image;
+ return baseImpl->type == style::SourceType::Image;
}
} // namespace mbgl
diff --git a/src/mbgl/renderer/sources/render_raster_dem_source.cpp b/src/mbgl/renderer/sources/render_raster_dem_source.cpp
new file mode 100644
index 0000000000..b3153622c3
--- /dev/null
+++ b/src/mbgl/renderer/sources/render_raster_dem_source.cpp
@@ -0,0 +1,166 @@
+#include <mbgl/renderer/sources/render_raster_dem_source.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/tile/raster_dem_tile.hpp>
+#include <mbgl/algorithm/update_tile_masks.hpp>
+#include <mbgl/geometry/dem_data.hpp>
+#include <mbgl/renderer/buckets/hillshade_bucket.hpp>
+#include <iostream>
+
+namespace mbgl {
+
+using namespace style;
+
+RenderRasterDEMSource::RenderRasterDEMSource(Immutable<style::RasterSource::Impl> impl_)
+ : RenderSource(impl_) {
+ tilePyramid.setObserver(this);
+}
+
+const style::RasterSource::Impl& RenderRasterDEMSource::impl() const {
+ return static_cast<const style::RasterSource::Impl&>(*baseImpl);
+}
+
+bool RenderRasterDEMSource::isLoaded() const {
+ return tilePyramid.isLoaded();
+}
+
+void RenderRasterDEMSource::update(Immutable<style::Source::Impl> baseImpl_,
+ const std::vector<Immutable<Layer::Impl>>& layers,
+ const bool needsRendering,
+ const bool needsRelayout,
+ const TileParameters& parameters) {
+ std::swap(baseImpl, baseImpl_);
+
+ enabled = needsRendering;
+
+ optional<Tileset> _tileset = impl().getTileset();
+
+ if (tileset != _tileset) {
+ tileset = _tileset;
+ maxzoom = tileset->zoomRange.max;
+ // TODO: this removes existing buckets, and will cause flickering.
+ // Should instead refresh tile data in place.
+ tilePyramid.tiles.clear();
+ tilePyramid.renderTiles.clear();
+ tilePyramid.cache.clear();
+ }
+ // Allow clearing the tile pyramid first, before the early return in case
+ // the new tileset is not yet available or has an error in loading
+ if (!_tileset) {
+ return;
+ }
+
+ tilePyramid.update(layers,
+ needsRendering,
+ needsRelayout,
+ parameters,
+ SourceType::RasterDEM,
+ impl().getTileSize(),
+ tileset->zoomRange,
+ tileset->bounds,
+ [&] (const OverscaledTileID& tileID) {
+ return std::make_unique<RasterDEMTile>(tileID, parameters, *tileset);
+ });
+}
+
+void RenderRasterDEMSource::onTileChanged(Tile& tile){
+ RasterDEMTile& demtile = static_cast<RasterDEMTile&>(tile);
+
+ std::map<DEMTileNeighbors, DEMTileNeighbors> opposites = {
+ { DEMTileNeighbors::Left, DEMTileNeighbors::Right },
+ { DEMTileNeighbors::Right, DEMTileNeighbors::Left },
+ { DEMTileNeighbors::TopLeft, DEMTileNeighbors::BottomRight },
+ { DEMTileNeighbors::TopCenter, DEMTileNeighbors::BottomCenter },
+ { DEMTileNeighbors::TopRight, DEMTileNeighbors::BottomLeft },
+ { DEMTileNeighbors::BottomRight, DEMTileNeighbors::TopLeft },
+ { DEMTileNeighbors::BottomCenter, DEMTileNeighbors:: TopCenter },
+ { DEMTileNeighbors::BottomLeft, DEMTileNeighbors::TopRight }
+ };
+
+ if (tile.isRenderable() && demtile.neighboringTiles != DEMTileNeighbors::Complete) {
+ const CanonicalTileID canonical = tile.id.canonical;
+ const uint32_t dim = std::pow(2, canonical.z);
+ const uint32_t px = (canonical.x - 1 + dim) % dim;
+ const int pxw = canonical.x == 0 ? tile.id.wrap - 1 : tile.id.wrap;
+ const uint32_t nx = (canonical.x + 1 + dim) % dim;
+ const int nxw = (canonical.x + 1 == dim) ? tile.id.wrap + 1 : tile.id.wrap;
+
+ auto getNeighbor = [&] (DEMTileNeighbors mask){
+ if (mask == DEMTileNeighbors::Left){
+ return OverscaledTileID(tile.id.overscaledZ, pxw, canonical.z, px, canonical.y);
+ } else if (mask == DEMTileNeighbors::Right){
+ return OverscaledTileID(tile.id.overscaledZ, nxw, canonical.z, nx, canonical.y);
+ } else if (mask == DEMTileNeighbors::TopLeft){
+ return OverscaledTileID(tile.id.overscaledZ, pxw, canonical.z, px, canonical.y - 1);
+ } else if (mask == DEMTileNeighbors::TopCenter){
+ return OverscaledTileID(tile.id.overscaledZ, tile.id.wrap, canonical.z, canonical.x, canonical.y - 1);
+ } else if (mask == DEMTileNeighbors::TopRight){
+ return OverscaledTileID(tile.id.overscaledZ, nxw, canonical.z, nx, canonical.y - 1);
+ } else if (mask == DEMTileNeighbors::BottomLeft){
+ return OverscaledTileID(tile.id.overscaledZ, pxw, canonical.z, px, canonical.y + 1);
+ } else if (mask == DEMTileNeighbors::BottomCenter){
+ return OverscaledTileID(tile.id.overscaledZ, tile.id.wrap, canonical.z, canonical.x, canonical.y + 1);
+ } else if (mask == DEMTileNeighbors::BottomRight){
+ return OverscaledTileID(tile.id.overscaledZ, nxw, canonical.z, nx, canonical.y + 1);
+ } else{
+ throw std::runtime_error("mask is not a valid tile neighbor");
+ }
+ };
+
+ for (uint8_t i = 0; i < 8; i++) {
+ DEMTileNeighbors mask = DEMTileNeighbors(std::pow(2,i));
+ // only backfill if this neighbor has not been previously backfilled
+ if ((demtile.neighboringTiles & mask) != mask) {
+ OverscaledTileID neighborid = getNeighbor(mask);
+ Tile* renderableNeighbor = tilePyramid.getTile(neighborid);
+ if (renderableNeighbor != nullptr && renderableNeighbor->isRenderable()) {
+ RasterDEMTile& borderTile = static_cast<RasterDEMTile&>(*renderableNeighbor);
+ demtile.backfillBorder(borderTile, mask);
+
+ // if the border tile has not been backfilled by a previous instance of the main
+ // tile, backfill its corresponding neighbor as well.
+ const DEMTileNeighbors& borderMask = opposites[mask];
+ if ((borderTile.neighboringTiles & borderMask) != borderMask){
+ borderTile.backfillBorder(demtile, borderMask);
+ }
+ }
+ }
+ }
+ }
+ RenderSource::onTileChanged(tile);
+}
+
+void RenderRasterDEMSource::startRender(PaintParameters& parameters) {
+ algorithm::updateTileMasks(tilePyramid.getRenderTiles());
+ tilePyramid.startRender(parameters);
+}
+
+void RenderRasterDEMSource::finishRender(PaintParameters& parameters) {
+ tilePyramid.finishRender(parameters);
+}
+
+std::vector<std::reference_wrapper<RenderTile>> RenderRasterDEMSource::getRenderTiles() {
+ return tilePyramid.getRenderTiles();
+}
+
+std::unordered_map<std::string, std::vector<Feature>>
+RenderRasterDEMSource::queryRenderedFeatures(const ScreenLineString&,
+ const TransformState&,
+ const std::vector<const RenderLayer*>&,
+ const RenderedQueryOptions&,
+ const CollisionIndex& ) const {
+ return std::unordered_map<std::string, std::vector<Feature>> {};
+}
+
+std::vector<Feature> RenderRasterDEMSource::querySourceFeatures(const SourceQueryOptions&) const {
+ return {};
+}
+
+void RenderRasterDEMSource::reduceMemoryUse() {
+ tilePyramid.reduceMemoryUse();
+}
+
+void RenderRasterDEMSource::dumpDebugLogs() const {
+ tilePyramid.dumpDebugLogs();
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/sources/render_raster_dem_source.hpp b/src/mbgl/renderer/sources/render_raster_dem_source.hpp
new file mode 100644
index 0000000000..741214a14d
--- /dev/null
+++ b/src/mbgl/renderer/sources/render_raster_dem_source.hpp
@@ -0,0 +1,59 @@
+#pragma once
+
+#include <mbgl/renderer/render_source.hpp>
+#include <mbgl/renderer/tile_pyramid.hpp>
+#include <mbgl/style/sources/raster_source_impl.hpp>
+
+namespace mbgl {
+
+class RenderRasterDEMSource : public RenderSource {
+public:
+ RenderRasterDEMSource(Immutable<style::RasterSource::Impl>);
+
+ bool isLoaded() const final;
+
+ void update(Immutable<style::Source::Impl>,
+ const std::vector<Immutable<style::Layer::Impl>>&,
+ bool needsRendering,
+ bool needsRelayout,
+ const TileParameters&) final;
+
+ void startRender(PaintParameters&) final;
+ void finishRender(PaintParameters&) final;
+
+ std::vector<std::reference_wrapper<RenderTile>> getRenderTiles() final;
+
+ std::unordered_map<std::string, std::vector<Feature>>
+ queryRenderedFeatures(const ScreenLineString& geometry,
+ const TransformState& transformState,
+ const std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) const final;
+
+ std::vector<Feature>
+ querySourceFeatures(const SourceQueryOptions&) const final;
+
+ void reduceMemoryUse() final;
+ void dumpDebugLogs() const final;
+
+ uint8_t getMaxZoom() const {
+ return maxzoom;
+ };
+
+private:
+ const style::RasterSource::Impl& impl() const;
+
+ TilePyramid tilePyramid;
+ optional<Tileset> tileset;
+ uint8_t maxzoom = 15;
+
+protected:
+ void onTileChanged(Tile&) final;
+};
+
+template <>
+inline bool RenderSource::is<RenderRasterDEMSource>() const {
+ return baseImpl->type == style::SourceType::RasterDEM;
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/sources/render_raster_source.cpp b/src/mbgl/renderer/sources/render_raster_source.cpp
index 46e3c2eff6..60b3fa9a3b 100644
--- a/src/mbgl/renderer/sources/render_raster_source.cpp
+++ b/src/mbgl/renderer/sources/render_raster_source.cpp
@@ -76,7 +76,8 @@ std::unordered_map<std::string, std::vector<Feature>>
RenderRasterSource::queryRenderedFeatures(const ScreenLineString&,
const TransformState&,
const std::vector<const RenderLayer*>&,
- const RenderedQueryOptions&) const {
+ const RenderedQueryOptions&,
+ const CollisionIndex& ) const {
return std::unordered_map<std::string, std::vector<Feature>> {};
}
@@ -84,8 +85,8 @@ std::vector<Feature> RenderRasterSource::querySourceFeatures(const SourceQueryOp
return {};
}
-void RenderRasterSource::onLowMemory() {
- tilePyramid.onLowMemory();
+void RenderRasterSource::reduceMemoryUse() {
+ tilePyramid.reduceMemoryUse();
}
void RenderRasterSource::dumpDebugLogs() const {
diff --git a/src/mbgl/renderer/sources/render_raster_source.hpp b/src/mbgl/renderer/sources/render_raster_source.hpp
index aebc49bf8a..78eda199ac 100644
--- a/src/mbgl/renderer/sources/render_raster_source.hpp
+++ b/src/mbgl/renderer/sources/render_raster_source.hpp
@@ -27,12 +27,13 @@ public:
queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
const std::vector<const RenderLayer*>& layers,
- const RenderedQueryOptions& options) const final;
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) const final;
std::vector<Feature>
querySourceFeatures(const SourceQueryOptions&) const final;
- void onLowMemory() final;
+ void reduceMemoryUse() final;
void dumpDebugLogs() const final;
private:
@@ -44,7 +45,7 @@ private:
template <>
inline bool RenderSource::is<RenderRasterSource>() const {
- return baseImpl->type == SourceType::Raster;
+ return baseImpl->type == style::SourceType::Raster;
}
} // namespace mbgl
diff --git a/src/mbgl/renderer/sources/render_vector_source.cpp b/src/mbgl/renderer/sources/render_vector_source.cpp
index 310dfe68b8..e87bea5dcd 100644
--- a/src/mbgl/renderer/sources/render_vector_source.cpp
+++ b/src/mbgl/renderer/sources/render_vector_source.cpp
@@ -79,16 +79,17 @@ std::unordered_map<std::string, std::vector<Feature>>
RenderVectorSource::queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
const std::vector<const RenderLayer*>& layers,
- const RenderedQueryOptions& options) const {
- return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options);
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) const {
+ return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, collisionIndex);
}
std::vector<Feature> RenderVectorSource::querySourceFeatures(const SourceQueryOptions& options) const {
return tilePyramid.querySourceFeatures(options);
}
-void RenderVectorSource::onLowMemory() {
- tilePyramid.onLowMemory();
+void RenderVectorSource::reduceMemoryUse() {
+ tilePyramid.reduceMemoryUse();
}
void RenderVectorSource::dumpDebugLogs() const {
diff --git a/src/mbgl/renderer/sources/render_vector_source.hpp b/src/mbgl/renderer/sources/render_vector_source.hpp
index f237b4b825..592160dc16 100644
--- a/src/mbgl/renderer/sources/render_vector_source.hpp
+++ b/src/mbgl/renderer/sources/render_vector_source.hpp
@@ -27,12 +27,13 @@ public:
queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
const std::vector<const RenderLayer*>& layers,
- const RenderedQueryOptions& options) const final;
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) const final;
std::vector<Feature>
querySourceFeatures(const SourceQueryOptions&) const final;
- void onLowMemory() final;
+ void reduceMemoryUse() final;
void dumpDebugLogs() const final;
private:
@@ -44,7 +45,7 @@ private:
template <>
inline bool RenderSource::is<RenderVectorSource>() const {
- return baseImpl->type == SourceType::Vector;
+ return baseImpl->type == style::SourceType::Vector;
}
} // namespace mbgl
diff --git a/src/mbgl/renderer/tile_pyramid.cpp b/src/mbgl/renderer/tile_pyramid.cpp
index c9e3b0630a..c4372e7112 100644
--- a/src/mbgl/renderer/tile_pyramid.cpp
+++ b/src/mbgl/renderer/tile_pyramid.cpp
@@ -5,7 +5,6 @@
#include <mbgl/renderer/tile_parameters.hpp>
#include <mbgl/renderer/query.hpp>
#include <mbgl/map/transform.hpp>
-#include <mbgl/text/placement_config.hpp>
#include <mbgl/math/clamp.hpp>
#include <mbgl/util/tile_cover.hpp>
#include <mbgl/util/tile_range.hpp>
@@ -57,6 +56,11 @@ std::vector<std::reference_wrapper<RenderTile>> TilePyramid::getRenderTiles() {
return { renderTiles.begin(), renderTiles.end() };
}
+Tile* TilePyramid::getTile(const OverscaledTileID& tileID){
+ auto it = tiles.find(tileID);
+ return it == tiles.end() ? cache.get(tileID) : it->second.get();
+}
+
void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layers,
const bool needsRendering,
const bool needsRelayout,
@@ -97,20 +101,21 @@ void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layer
if (overscaledZoom >= zoomRange.min) {
int32_t idealZoom = std::min<int32_t>(zoomRange.max, overscaledZoom);
+
// Make sure we're not reparsing overzoomed raster tiles.
if (type == SourceType::Raster) {
tileZoom = idealZoom;
+ }
- // FIXME: Prefetching is only enabled for raster
- // tiles until we fix #7026.
-
- // Request lower zoom level tiles (if configure to do so) in an attempt
+ // Only attempt prefetching in continuous mode.
+ if (parameters.mode == MapMode::Continuous) {
+ // Request lower zoom level tiles (if configured to do so) in an attempt
// to show something on the screen faster at the cost of a little of bandwidth.
if (parameters.prefetchZoomDelta) {
panZoom = std::max<int32_t>(tileZoom - parameters.prefetchZoomDelta, zoomRange.min);
}
- if (panZoom < tileZoom) {
+ if (panZoom < idealZoom) {
panTiles = util::tileCover(parameters.transformState, panZoom);
}
}
@@ -123,6 +128,7 @@ void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layer
// use because they're still loading. In addition to that, we also need to retain all tiles that
// we're actively using, e.g. as a replacement for tile that aren't loaded yet.
std::set<OverscaledTileID> retain;
+ std::set<UnwrappedTileID> rendered;
auto retainTileFn = [&](Tile& tile, TileNecessity necessity) -> void {
if (retain.emplace(tile.id).second) {
@@ -146,7 +152,7 @@ void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layer
if (tileRange && !tileRange->contains(tileID.canonical)) {
return nullptr;
}
- std::unique_ptr<Tile> tile = cache.get(tileID);
+ std::unique_ptr<Tile> tile = cache.pop(tileID);
if (!tile) {
tile = createTile(tileID);
if (tile) {
@@ -159,8 +165,17 @@ void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layer
}
return tiles.emplace(tileID, std::move(tile)).first->second.get();
};
+
+ std::map<UnwrappedTileID, Tile*> previouslyRenderedTiles;
+ for (auto& renderTile : renderTiles) {
+ previouslyRenderedTiles[renderTile.id] = &renderTile.tile;
+ }
+
auto renderTileFn = [&](const UnwrappedTileID& tileID, Tile& tile) {
renderTiles.emplace_back(tileID, tile);
+ rendered.emplace(tileID);
+ previouslyRenderedTiles.erase(tileID); // Still rendering this tile, no need for special fading logic.
+ tile.markRenderedIdeal();
};
renderTiles.clear();
@@ -172,6 +187,18 @@ void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layer
algorithm::updateRenderables(getTileFn, createTileFn, retainTileFn, renderTileFn,
idealTiles, zoomRange, tileZoom);
+
+ for (auto previouslyRenderedTile : previouslyRenderedTiles) {
+ Tile& tile = *previouslyRenderedTile.second;
+ tile.markRenderedPreviously();
+ if (tile.holdForFade()) {
+ // Since it was rendered in the last frame, we know we have it
+ // Don't mark the tile "Required" to avoid triggering a new network request
+ retainTileFn(tile, TileNecessity::Optional);
+ renderTiles.emplace_back(previouslyRenderedTile.first, tile);
+ rendered.emplace(previouslyRenderedTile.first);
+ }
+ }
if (type != SourceType::Annotations) {
size_t conservativeCacheSize =
@@ -204,20 +231,15 @@ void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layer
}
for (auto& pair : tiles) {
- const PlacementConfig config { parameters.transformState.getAngle(),
- parameters.transformState.getPitch(),
- parameters.transformState.getCameraToCenterDistance(),
- parameters.transformState.getCameraToTileDistance(pair.first.toUnwrapped()),
- parameters.debugOptions & MapDebugOptions::Collision };
-
- pair.second->setPlacementConfig(config);
+ pair.second->setShowCollisionBoxes(parameters.debugOptions & MapDebugOptions::Collision);
}
}
std::unordered_map<std::string, std::vector<Feature>> TilePyramid::queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
const std::vector<const RenderLayer*>& layers,
- const RenderedQueryOptions& options) const {
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) const {
std::unordered_map<std::string, std::vector<Feature>> result;
if (renderTiles.empty() || geometry.empty()) {
return result;
@@ -260,7 +282,8 @@ std::unordered_map<std::string, std::vector<Feature>> TilePyramid::queryRendered
tileSpaceQueryGeometry,
transformState,
layers,
- options);
+ options,
+ collisionIndex);
}
return result;
@@ -280,7 +303,7 @@ void TilePyramid::setCacheSize(size_t size) {
cache.setSize(size);
}
-void TilePyramid::onLowMemory() {
+void TilePyramid::reduceMemoryUse() {
cache.clear();
}
diff --git a/src/mbgl/renderer/tile_pyramid.hpp b/src/mbgl/renderer/tile_pyramid.hpp
index e34b050273..2638599c38 100644
--- a/src/mbgl/renderer/tile_pyramid.hpp
+++ b/src/mbgl/renderer/tile_pyramid.hpp
@@ -37,7 +37,7 @@ public:
bool needsRendering,
bool needsRelayout,
const TileParameters&,
- SourceType type,
+ style::SourceType type,
uint16_t tileSize,
Range<uint8_t> zoomRange,
optional<LatLngBounds> bounds,
@@ -47,17 +47,19 @@ public:
void finishRender(PaintParameters&);
std::vector<std::reference_wrapper<RenderTile>> getRenderTiles();
+ Tile* getTile(const OverscaledTileID&);
std::unordered_map<std::string, std::vector<Feature>>
queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
const std::vector<const RenderLayer*>&,
- const RenderedQueryOptions& options) const;
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) const;
std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const;
void setCacheSize(size_t);
- void onLowMemory();
+ void reduceMemoryUse();
void setObserver(TileObserver*);
void dumpDebugLogs() const;
diff --git a/src/mbgl/shaders/background.cpp b/src/mbgl/shaders/background.cpp
new file mode 100644
index 0000000000..3eafa47b49
--- /dev/null
+++ b/src/mbgl/shaders/background.cpp
@@ -0,0 +1,34 @@
+// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED.
+
+#include <mbgl/shaders/background.hpp>
+
+namespace mbgl {
+namespace shaders {
+
+const char* background::name = "background";
+const char* background::vertexSource = R"MBGL_SHADER(
+attribute vec2 a_pos;
+
+uniform mat4 u_matrix;
+
+void main() {
+ gl_Position = u_matrix * vec4(a_pos, 0, 1);
+}
+
+)MBGL_SHADER";
+const char* background::fragmentSource = R"MBGL_SHADER(
+uniform vec4 u_color;
+uniform float u_opacity;
+
+void main() {
+ gl_FragColor = u_color * u_opacity;
+
+#ifdef OVERDRAW_INSPECTOR
+ gl_FragColor = vec4(1.0);
+#endif
+}
+
+)MBGL_SHADER";
+
+} // namespace shaders
+} // namespace mbgl
diff --git a/src/mbgl/shaders/background.hpp b/src/mbgl/shaders/background.hpp
new file mode 100644
index 0000000000..2fa6f56e29
--- /dev/null
+++ b/src/mbgl/shaders/background.hpp
@@ -0,0 +1,16 @@
+// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED.
+
+#pragma once
+
+namespace mbgl {
+namespace shaders {
+
+class background {
+public:
+ static const char* name;
+ static const char* vertexSource;
+ static const char* fragmentSource;
+};
+
+} // namespace shaders
+} // namespace mbgl
diff --git a/src/mbgl/shaders/background_pattern.cpp b/src/mbgl/shaders/background_pattern.cpp
new file mode 100644
index 0000000000..6fd0a53d19
--- /dev/null
+++ b/src/mbgl/shaders/background_pattern.cpp
@@ -0,0 +1,65 @@
+// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED.
+
+#include <mbgl/shaders/background_pattern.hpp>
+
+namespace mbgl {
+namespace shaders {
+
+const char* background_pattern::name = "background_pattern";
+const char* background_pattern::vertexSource = R"MBGL_SHADER(
+uniform mat4 u_matrix;
+uniform vec2 u_pattern_size_a;
+uniform vec2 u_pattern_size_b;
+uniform vec2 u_pixel_coord_upper;
+uniform vec2 u_pixel_coord_lower;
+uniform float u_scale_a;
+uniform float u_scale_b;
+uniform float u_tile_units_to_pixels;
+
+attribute vec2 a_pos;
+
+varying vec2 v_pos_a;
+varying vec2 v_pos_b;
+
+void main() {
+ gl_Position = u_matrix * vec4(a_pos, 0, 1);
+
+ v_pos_a = get_pattern_pos(u_pixel_coord_upper, u_pixel_coord_lower, u_scale_a * u_pattern_size_a, u_tile_units_to_pixels, a_pos);
+ v_pos_b = get_pattern_pos(u_pixel_coord_upper, u_pixel_coord_lower, u_scale_b * u_pattern_size_b, u_tile_units_to_pixels, a_pos);
+}
+
+)MBGL_SHADER";
+const char* background_pattern::fragmentSource = R"MBGL_SHADER(
+uniform vec2 u_pattern_tl_a;
+uniform vec2 u_pattern_br_a;
+uniform vec2 u_pattern_tl_b;
+uniform vec2 u_pattern_br_b;
+uniform vec2 u_texsize;
+uniform float u_mix;
+uniform float u_opacity;
+
+uniform sampler2D u_image;
+
+varying vec2 v_pos_a;
+varying vec2 v_pos_b;
+
+void main() {
+ vec2 imagecoord = mod(v_pos_a, 1.0);
+ vec2 pos = mix(u_pattern_tl_a / u_texsize, u_pattern_br_a / u_texsize, imagecoord);
+ vec4 color1 = texture2D(u_image, pos);
+
+ vec2 imagecoord_b = mod(v_pos_b, 1.0);
+ vec2 pos2 = mix(u_pattern_tl_b / u_texsize, u_pattern_br_b / u_texsize, imagecoord_b);
+ vec4 color2 = texture2D(u_image, pos2);
+
+ gl_FragColor = mix(color1, color2, u_mix) * u_opacity;
+
+#ifdef OVERDRAW_INSPECTOR
+ gl_FragColor = vec4(1.0);
+#endif
+}
+
+)MBGL_SHADER";
+
+} // namespace shaders
+} // namespace mbgl
diff --git a/src/mbgl/shaders/background_pattern.hpp b/src/mbgl/shaders/background_pattern.hpp
new file mode 100644
index 0000000000..e970ffd670
--- /dev/null
+++ b/src/mbgl/shaders/background_pattern.hpp
@@ -0,0 +1,16 @@
+// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED.
+
+#pragma once
+
+namespace mbgl {
+namespace shaders {
+
+class background_pattern {
+public:
+ static const char* name;
+ static const char* vertexSource;
+ static const char* fragmentSource;
+};
+
+} // namespace shaders
+} // namespace mbgl
diff --git a/src/mbgl/shaders/clipping_mask.cpp b/src/mbgl/shaders/clipping_mask.cpp
new file mode 100644
index 0000000000..fb08d7cb00
--- /dev/null
+++ b/src/mbgl/shaders/clipping_mask.cpp
@@ -0,0 +1,27 @@
+// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED.
+
+#include <mbgl/shaders/clipping_mask.hpp>
+
+namespace mbgl {
+namespace shaders {
+
+const char* clipping_mask::name = "clipping_mask";
+const char* clipping_mask::vertexSource = R"MBGL_SHADER(
+attribute vec2 a_pos;
+
+uniform mat4 u_matrix;
+
+void main() {
+ gl_Position = u_matrix * vec4(a_pos, 0, 1);
+}
+
+)MBGL_SHADER";
+const char* clipping_mask::fragmentSource = R"MBGL_SHADER(
+void main() {
+ gl_FragColor = vec4(1.0);
+}
+
+)MBGL_SHADER";
+
+} // namespace shaders
+} // namespace mbgl
diff --git a/src/mbgl/shaders/clipping_mask.hpp b/src/mbgl/shaders/clipping_mask.hpp
new file mode 100644
index 0000000000..bd01ab62fa
--- /dev/null
+++ b/src/mbgl/shaders/clipping_mask.hpp
@@ -0,0 +1,16 @@
+// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED.
+
+#pragma once
+
+namespace mbgl {
+namespace shaders {
+
+class clipping_mask {
+public:
+ static const char* name;
+ static const char* vertexSource;
+ static const char* fragmentSource;
+};
+
+} // namespace shaders
+} // namespace mbgl
diff --git a/src/mbgl/shaders/collision_box.cpp b/src/mbgl/shaders/collision_box.cpp
index 07fa94e338..9d11640bf4 100644
--- a/src/mbgl/shaders/collision_box.cpp
+++ b/src/mbgl/shaders/collision_box.cpp
@@ -10,77 +10,50 @@ const char* collision_box::vertexSource = R"MBGL_SHADER(
attribute vec2 a_pos;
attribute vec2 a_anchor_pos;
attribute vec2 a_extrude;
-attribute vec2 a_data;
+attribute vec2 a_placed;
uniform mat4 u_matrix;
-uniform float u_scale;
-uniform float u_pitch;
-uniform float u_collision_y_stretch;
+uniform vec2 u_extrude_scale;
uniform float u_camera_to_center_distance;
-varying float v_max_zoom;
-varying float v_placement_zoom;
-varying float v_perspective_zoom_adjust;
-varying vec2 v_fade_tex;
+varying float v_placed;
+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 = 1.0 + 0.5 * ((camera_to_anchor_distance / u_camera_to_center_distance) - 1.0);
+ highp float collision_perspective_ratio = 0.5 + 0.5 * (u_camera_to_center_distance / camera_to_anchor_distance);
- highp float incidence_stretch = camera_to_anchor_distance / (u_camera_to_center_distance * cos(u_pitch));
- highp float collision_adjustment = max(1.0, incidence_stretch / u_collision_y_stretch);
+ 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;
- gl_Position = u_matrix * vec4(a_pos + a_extrude * collision_perspective_ratio * collision_adjustment / u_scale, 0.0, 1.0);
-
- v_max_zoom = a_data.x;
- v_placement_zoom = a_data.y;
-
- v_perspective_zoom_adjust = floor(log2(collision_perspective_ratio * collision_adjustment) * 10.0);
- v_fade_tex = vec2((v_placement_zoom + v_perspective_zoom_adjust) / 255.0, 0.0);
+ v_placed = a_placed.x;
+ v_notUsed = a_placed.y;
}
)MBGL_SHADER";
const char* collision_box::fragmentSource = R"MBGL_SHADER(
-uniform float u_zoom;
-// u_maxzoom is derived from the maximum scale considered by the CollisionTile
-// Labels with placement zoom greater than this value will not be placed,
-// regardless of perspective effects.
-uniform float u_maxzoom;
-uniform sampler2D u_fadetexture;
-
-// v_max_zoom is a collision-box-specific value that controls when line-following
-// collision boxes are used.
-varying float v_max_zoom;
-varying float v_placement_zoom;
-varying float v_perspective_zoom_adjust;
-varying vec2 v_fade_tex;
+
+varying float v_placed;
+varying float v_notUsed;
void main() {
float alpha = 0.5;
- // Green = no collisions, label is showing
- gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0) * alpha;
+ // Red = collision, hide label
+ gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0) * alpha;
- // Red = collision, label hidden
- if (texture2D(u_fadetexture, v_fade_tex).a < 1.0) {
- gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0) * alpha;
+ // Blue = no collision, label is showing
+ if (v_placed > 0.5) {
+ gl_FragColor = vec4(0.0, 0.0, 1.0, 0.5) * alpha;
}
- // Faded black = this collision box is not used at this zoom (for curved labels)
- if (u_zoom >= v_max_zoom + v_perspective_zoom_adjust) {
- gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0) * alpha * 0.25;
- }
-
- // Faded blue = the placement scale for this label is beyond the CollisionTile
- // max scale, so it's impossible for this label to show without collision detection
- // being run again (the label's glyphs haven't even been added to the symbol bucket)
- if (v_placement_zoom >= u_maxzoom) {
- gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0) * alpha * 0.2;
+ if (v_notUsed > 0.5) {
+ // This box not used, fade it out
+ gl_FragColor *= .1;
}
}
-
)MBGL_SHADER";
} // namespace shaders
diff --git a/src/mbgl/shaders/collision_circle.cpp b/src/mbgl/shaders/collision_circle.cpp
new file mode 100644
index 0000000000..82ebbf05a0
--- /dev/null
+++ b/src/mbgl/shaders/collision_circle.cpp
@@ -0,0 +1,87 @@
+// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED.
+
+#include <mbgl/shaders/collision_circle.hpp>
+
+namespace mbgl {
+namespace shaders {
+
+const char* collision_circle::name = "collision_circle";
+const char* collision_circle::vertexSource = R"MBGL_SHADER(
+attribute vec2 a_pos;
+attribute vec2 a_anchor_pos;
+attribute vec2 a_extrude;
+attribute vec2 a_placed;
+
+uniform mat4 u_matrix;
+uniform vec2 u_extrude_scale;
+uniform float u_camera_to_center_distance;
+
+varying float v_placed;
+varying float v_notUsed;
+varying float v_radius;
+
+varying vec2 v_extrude;
+varying vec2 v_extrude_scale;
+
+void main() {
+ vec4 projectedPoint = u_matrix * vec4(a_anchor_pos, 0, 1);
+ highp float camera_to_anchor_distance = projectedPoint.w;
+ highp float collision_perspective_ratio = clamp(
+ 0.5 + 0.5 * (u_camera_to_center_distance / camera_to_anchor_distance),
+ 0.0, // Prevents oversized near-field circles in pitched/overzoomed tiles
+ 4.0);
+
+ gl_Position = u_matrix * vec4(a_pos, 0.0, 1.0);
+
+ highp float padding_factor = 1.2; // Pad the vertices slightly to make room for anti-alias blur
+ gl_Position.xy += a_extrude * u_extrude_scale * padding_factor * gl_Position.w * collision_perspective_ratio;
+
+ v_placed = a_placed.x;
+ v_notUsed = a_placed.y;
+ v_radius = abs(a_extrude.y); // We don't pitch the circles, so both units of the extrusion vector are equal in magnitude to the radius
+
+ v_extrude = a_extrude * padding_factor;
+ v_extrude_scale = u_extrude_scale * u_camera_to_center_distance * collision_perspective_ratio;
+}
+
+)MBGL_SHADER";
+const char* collision_circle::fragmentSource = R"MBGL_SHADER(
+uniform float u_overscale_factor;
+
+varying float v_placed;
+varying float v_notUsed;
+varying float v_radius;
+varying vec2 v_extrude;
+varying vec2 v_extrude_scale;
+
+void main() {
+ float alpha = 0.5;
+
+ // Red = collision, hide label
+ vec4 color = vec4(1.0, 0.0, 0.0, 1.0) * alpha;
+
+ // Blue = no collision, label is showing
+ if (v_placed > 0.5) {
+ color = vec4(0.0, 0.0, 1.0, 0.5) * alpha;
+ }
+
+ if (v_notUsed > 0.5) {
+ // This box not used, fade it out
+ color *= .2;
+ }
+
+ float extrude_scale_length = length(v_extrude_scale);
+ float extrude_length = length(v_extrude) * extrude_scale_length;
+ float stroke_width = 15.0 * extrude_scale_length / u_overscale_factor;
+ float radius = v_radius * extrude_scale_length;
+
+ float distance_to_edge = abs(extrude_length - radius);
+ float opacity_t = smoothstep(-stroke_width, 0.0, -distance_to_edge);
+
+ gl_FragColor = opacity_t * color;
+}
+
+)MBGL_SHADER";
+
+} // namespace shaders
+} // namespace mbgl
diff --git a/src/mbgl/shaders/collision_circle.hpp b/src/mbgl/shaders/collision_circle.hpp
new file mode 100644
index 0000000000..12b1bcd445
--- /dev/null
+++ b/src/mbgl/shaders/collision_circle.hpp
@@ -0,0 +1,16 @@
+// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED.
+
+#pragma once
+
+namespace mbgl {
+namespace shaders {
+
+class collision_circle {
+public:
+ static const char* name;
+ static const char* vertexSource;
+ static const char* fragmentSource;
+};
+
+} // namespace shaders
+} // namespace mbgl
diff --git a/src/mbgl/shaders/debug.cpp b/src/mbgl/shaders/debug.cpp
index d18f3be5d1..9012cfa755 100644
--- a/src/mbgl/shaders/debug.cpp
+++ b/src/mbgl/shaders/debug.cpp
@@ -12,12 +12,7 @@ attribute vec2 a_pos;
uniform mat4 u_matrix;
void main() {
- // We are using Int16 for texture position coordinates to give us enough precision for
- // fractional coordinates. We use 8192 to scale the texture coordinates in the buffer
- // as an arbitrarily high number to preserve adequate precision when rendering.
- // This is also the same value as the EXTENT we are using for our tile buffer pos coordinates,
- // so math for modifying either is consistent.
- gl_Position = u_matrix * vec4(a_pos, step(8192.0, a_pos.x), 1);
+ gl_Position = u_matrix * vec4(a_pos, 0, 1);
}
)MBGL_SHADER";
diff --git a/src/mbgl/shaders/fill_extrusion.cpp b/src/mbgl/shaders/fill_extrusion.cpp
index 817f73391c..5bb2b9cd07 100644
--- a/src/mbgl/shaders/fill_extrusion.cpp
+++ b/src/mbgl/shaders/fill_extrusion.cpp
@@ -13,8 +13,7 @@ uniform lowp vec3 u_lightpos;
uniform lowp float u_lightintensity;
attribute vec2 a_pos;
-attribute vec3 a_normal;
-attribute float a_edgedistance;
+attribute vec4 a_normal_ed;
varying vec4 v_color;
@@ -70,11 +69,12 @@ void main() {
#endif
+ vec3 normal = a_normal_ed.xyz;
+
base = max(0.0, base);
height = max(0.0, height);
- float ed = a_edgedistance; // use each attrib in order to not trip a VAO assert
- float t = mod(a_normal.x, 2.0);
+ float t = mod(normal.x, 2.0);
gl_Position = u_matrix * vec4(a_pos, t > 0.0 ? height : base, 1);
@@ -88,7 +88,7 @@ void main() {
color += ambientlight;
// Calculate cos(theta), where theta is the angle between surface normal and diffuse light ray
- float directional = clamp(dot(a_normal / 16384.0, u_lightpos), 0.0, 1.0);
+ float directional = clamp(dot(normal / 16384.0, u_lightpos), 0.0, 1.0);
// Adjust directional so that
// the range of values for highlight/shading is narrower
@@ -97,7 +97,7 @@ void main() {
directional = mix((1.0 - u_lightintensity), max((1.0 - colorvalue + u_lightintensity), 1.0), directional);
// Add gradient along z axis of side surfaces
- if (a_normal.y != 0.0) {
+ if (normal.y != 0.0) {
directional *= clamp((t + base) * pow(height / 150.0, 0.5), mix(0.7, 0.98, 1.0 - u_lightintensity), 1.0);
}
diff --git a/src/mbgl/shaders/fill_extrusion_pattern.cpp b/src/mbgl/shaders/fill_extrusion_pattern.cpp
index d3e5eef1bf..466d0e04fe 100644
--- a/src/mbgl/shaders/fill_extrusion_pattern.cpp
+++ b/src/mbgl/shaders/fill_extrusion_pattern.cpp
@@ -22,8 +22,7 @@ uniform lowp vec3 u_lightpos;
uniform lowp float u_lightintensity;
attribute vec2 a_pos;
-attribute vec3 a_normal;
-attribute float a_edgedistance;
+attribute vec4 a_normal_ed;
varying vec2 v_pos_a;
varying vec2 v_pos_b;
@@ -65,26 +64,29 @@ void main() {
#endif
+ vec3 normal = a_normal_ed.xyz;
+ float edgedistance = a_normal_ed.w;
+
base = max(0.0, base);
height = max(0.0, height);
- float t = mod(a_normal.x, 2.0);
+ float t = mod(normal.x, 2.0);
float z = t > 0.0 ? height : base;
gl_Position = u_matrix * vec4(a_pos, z, 1);
- vec2 pos = a_normal.x == 1.0 && a_normal.y == 0.0 && a_normal.z == 16384.0
+ vec2 pos = normal.x == 1.0 && normal.y == 0.0 && normal.z == 16384.0
? a_pos // extrusion top
- : vec2(a_edgedistance, z * u_height_factor); // extrusion side
+ : vec2(edgedistance, z * u_height_factor); // extrusion side
v_pos_a = get_pattern_pos(u_pixel_coord_upper, u_pixel_coord_lower, u_scale_a * u_pattern_size_a, u_tile_units_to_pixels, pos);
v_pos_b = get_pattern_pos(u_pixel_coord_upper, u_pixel_coord_lower, u_scale_b * u_pattern_size_b, u_tile_units_to_pixels, pos);
v_lighting = vec4(0.0, 0.0, 0.0, 1.0);
- float directional = clamp(dot(a_normal / 16383.0, u_lightpos), 0.0, 1.0);
+ float directional = clamp(dot(normal / 16383.0, u_lightpos), 0.0, 1.0);
directional = mix((1.0 - u_lightintensity), max((0.5 + u_lightintensity), 1.0), directional);
- if (a_normal.y != 0.0) {
+ if (normal.y != 0.0) {
directional *= clamp((t + base) * pow(height / 150.0, 0.5), mix(0.7, 0.98, 1.0 - u_lightintensity), 1.0);
}
diff --git a/src/mbgl/shaders/heatmap.cpp b/src/mbgl/shaders/heatmap.cpp
new file mode 100644
index 0000000000..19927cfcc6
--- /dev/null
+++ b/src/mbgl/shaders/heatmap.cpp
@@ -0,0 +1,128 @@
+// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED.
+
+#include <mbgl/shaders/heatmap.hpp>
+
+namespace mbgl {
+namespace shaders {
+
+const char* heatmap::name = "heatmap";
+const char* heatmap::vertexSource = R"MBGL_SHADER(
+
+#ifndef HAS_UNIFORM_u_weight
+uniform lowp float a_weight_t;
+attribute highp vec2 a_weight;
+varying highp float weight;
+#else
+uniform highp float u_weight;
+#endif
+
+
+#ifndef HAS_UNIFORM_u_radius
+uniform lowp float a_radius_t;
+attribute mediump vec2 a_radius;
+#else
+uniform mediump float u_radius;
+#endif
+
+
+uniform mat4 u_matrix;
+uniform float u_extrude_scale;
+uniform float u_opacity;
+uniform float u_intensity;
+
+attribute vec2 a_pos;
+
+varying vec2 v_extrude;
+
+// Effective "0" in the kernel density texture to adjust the kernel size to;
+// this empirically chosen number minimizes artifacts on overlapping kernels
+// for typical heatmap cases (assuming clustered source)
+const highp float ZERO = 1.0 / 255.0 / 16.0;
+
+// Gaussian kernel coefficient: 1 / sqrt(2 * PI)
+#define GAUSS_COEF 0.3989422804014327
+
+void main(void) {
+
+#ifndef HAS_UNIFORM_u_weight
+ weight = unpack_mix_vec2(a_weight, a_weight_t);
+#else
+ highp float weight = u_weight;
+#endif
+
+
+#ifndef HAS_UNIFORM_u_radius
+ mediump float radius = unpack_mix_vec2(a_radius, a_radius_t);
+#else
+ mediump float radius = u_radius;
+#endif
+
+
+ // unencode the extrusion vector that we snuck into the a_pos vector
+ vec2 unscaled_extrude = vec2(mod(a_pos, 2.0) * 2.0 - 1.0);
+
+ // This 'extrude' comes in ranging from [-1, -1], to [1, 1]. We'll use
+ // it to produce the vertices of a square mesh framing the point feature
+ // we're adding to the kernel density texture. We'll also pass it as
+ // a varying, so that the fragment shader can determine the distance of
+ // each fragment from the point feature.
+ // Before we do so, we need to scale it up sufficiently so that the
+ // kernel falls effectively to zero at the edge of the mesh.
+ // That is, we want to know S such that
+ // weight * u_intensity * GAUSS_COEF * exp(-0.5 * 3.0^2 * S^2) == ZERO
+ // Which solves to:
+ // S = sqrt(-2.0 * log(ZERO / (weight * u_intensity * GAUSS_COEF))) / 3.0
+ float S = sqrt(-2.0 * log(ZERO / weight / u_intensity / GAUSS_COEF)) / 3.0;
+
+ // Pass the varying in units of radius
+ v_extrude = S * unscaled_extrude;
+
+ // Scale by radius and the zoom-based scale factor to produce actual
+ // mesh position
+ vec2 extrude = v_extrude * radius * u_extrude_scale;
+
+ // multiply a_pos by 0.5, since we had it * 2 in order to sneak
+ // in extrusion data
+ vec4 pos = vec4(floor(a_pos * 0.5) + extrude, 0, 1);
+
+ gl_Position = u_matrix * pos;
+}
+
+)MBGL_SHADER";
+const char* heatmap::fragmentSource = R"MBGL_SHADER(
+
+#ifndef HAS_UNIFORM_u_weight
+varying highp float weight;
+#else
+uniform highp float u_weight;
+#endif
+
+
+uniform highp float u_intensity;
+varying vec2 v_extrude;
+
+// Gaussian kernel coefficient: 1 / sqrt(2 * PI)
+#define GAUSS_COEF 0.3989422804014327
+
+void main() {
+
+#ifdef HAS_UNIFORM_u_weight
+ highp float weight = u_weight;
+#endif
+
+
+ // Kernel density estimation with a Gaussian kernel of size 5x5
+ float d = -0.5 * 3.0 * 3.0 * dot(v_extrude, v_extrude);
+ float val = weight * u_intensity * GAUSS_COEF * exp(d);
+
+ gl_FragColor = vec4(val, 1.0, 1.0, 1.0);
+
+#ifdef OVERDRAW_INSPECTOR
+ gl_FragColor = vec4(1.0);
+#endif
+}
+
+)MBGL_SHADER";
+
+} // namespace shaders
+} // namespace mbgl
diff --git a/src/mbgl/shaders/heatmap.hpp b/src/mbgl/shaders/heatmap.hpp
new file mode 100644
index 0000000000..a3c64db942
--- /dev/null
+++ b/src/mbgl/shaders/heatmap.hpp
@@ -0,0 +1,16 @@
+// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED.
+
+#pragma once
+
+namespace mbgl {
+namespace shaders {
+
+class heatmap {
+public:
+ static const char* name;
+ static const char* vertexSource;
+ static const char* fragmentSource;
+};
+
+} // namespace shaders
+} // namespace mbgl
diff --git a/src/mbgl/shaders/heatmap_texture.cpp b/src/mbgl/shaders/heatmap_texture.cpp
new file mode 100644
index 0000000000..c5d35c48ae
--- /dev/null
+++ b/src/mbgl/shaders/heatmap_texture.cpp
@@ -0,0 +1,42 @@
+// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED.
+
+#include <mbgl/shaders/heatmap_texture.hpp>
+
+namespace mbgl {
+namespace shaders {
+
+const char* heatmap_texture::name = "heatmap_texture";
+const char* heatmap_texture::vertexSource = R"MBGL_SHADER(
+uniform mat4 u_matrix;
+uniform vec2 u_world;
+attribute vec2 a_pos;
+varying vec2 v_pos;
+
+void main() {
+ gl_Position = u_matrix * vec4(a_pos * u_world, 0, 1);
+
+ v_pos.x = a_pos.x;
+ v_pos.y = 1.0 - a_pos.y;
+}
+
+)MBGL_SHADER";
+const char* heatmap_texture::fragmentSource = R"MBGL_SHADER(
+uniform sampler2D u_image;
+uniform sampler2D u_color_ramp;
+uniform float u_opacity;
+varying vec2 v_pos;
+
+void main() {
+ float t = texture2D(u_image, v_pos).r;
+ vec4 color = texture2D(u_color_ramp, vec2(t, 0.5));
+ gl_FragColor = color * u_opacity;
+
+#ifdef OVERDRAW_INSPECTOR
+ gl_FragColor = vec4(0.0);
+#endif
+}
+
+)MBGL_SHADER";
+
+} // namespace shaders
+} // namespace mbgl
diff --git a/src/mbgl/shaders/heatmap_texture.hpp b/src/mbgl/shaders/heatmap_texture.hpp
new file mode 100644
index 0000000000..c51dc6b178
--- /dev/null
+++ b/src/mbgl/shaders/heatmap_texture.hpp
@@ -0,0 +1,16 @@
+// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED.
+
+#pragma once
+
+namespace mbgl {
+namespace shaders {
+
+class heatmap_texture {
+public:
+ static const char* name;
+ static const char* vertexSource;
+ static const char* fragmentSource;
+};
+
+} // namespace shaders
+} // namespace mbgl
diff --git a/src/mbgl/shaders/hillshade.cpp b/src/mbgl/shaders/hillshade.cpp
new file mode 100644
index 0000000000..4083faa4b4
--- /dev/null
+++ b/src/mbgl/shaders/hillshade.cpp
@@ -0,0 +1,80 @@
+// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED.
+
+#include <mbgl/shaders/hillshade.hpp>
+
+namespace mbgl {
+namespace shaders {
+
+const char* hillshade::name = "hillshade";
+const char* hillshade::vertexSource = R"MBGL_SHADER(
+uniform mat4 u_matrix;
+
+attribute vec2 a_pos;
+attribute vec2 a_texture_pos;
+
+varying vec2 v_pos;
+
+void main() {
+ gl_Position = u_matrix * vec4(a_pos, 0, 1);
+ v_pos = a_texture_pos / 8192.0;
+}
+
+)MBGL_SHADER";
+const char* hillshade::fragmentSource = R"MBGL_SHADER(
+uniform sampler2D u_image;
+varying vec2 v_pos;
+
+uniform vec2 u_latrange;
+uniform vec2 u_light;
+uniform vec4 u_shadow;
+uniform vec4 u_highlight;
+uniform vec4 u_accent;
+
+#define PI 3.141592653589793
+
+void main() {
+ vec4 pixel = texture2D(u_image, v_pos);
+
+ vec2 deriv = ((pixel.rg * 2.0) - 1.0);
+
+ // We divide the slope by a scale factor based on the cosin of the pixel's approximate latitude
+ // to account for mercator projection distortion. see #4807 for details
+ float scaleFactor = cos(radians((u_latrange[0] - u_latrange[1]) * (1.0 - v_pos.y) + u_latrange[1]));
+ // We also multiply the slope by an arbitrary z-factor of 1.25
+ float slope = atan(1.25 * length(deriv) / scaleFactor);
+ float aspect = deriv.x != 0.0 ? atan(deriv.y, -deriv.x) : PI / 2.0 * (deriv.y > 0.0 ? 1.0 : -1.0);
+
+ float intensity = u_light.x;
+ // We add PI to make this property match the global light object, which adds PI/2 to the light's azimuthal
+ // position property to account for 0deg corresponding to north/the top of the viewport in the style spec
+ // and the original shader was written to accept (-illuminationDirection - 90) as the azimuthal.
+ float azimuth = u_light.y + PI;
+
+ // We scale the slope exponentially based on intensity, using a calculation similar to
+ // the exponential interpolation function in the style spec:
+ // https://github.com/mapbox/mapbox-gl-js/blob/master/src/style-spec/expression/definitions/interpolate.js#L217-L228
+ // so that higher intensity values create more opaque hillshading.
+ float base = 1.875 - intensity * 1.75;
+ float maxValue = 0.5 * PI;
+ float scaledSlope = intensity != 0.5 ? ((pow(base, slope) - 1.0) / (pow(base, maxValue) - 1.0)) * maxValue : slope;
+
+ // The accent color is calculated with the cosine of the slope while the shade color is calculated with the sine
+ // so that the accent color's rate of change eases in while the shade color's eases out.
+ float accent = cos(scaledSlope);
+ // We multiply both the accent and shade color by a clamped intensity value
+ // so that intensities >= 0.5 do not additionally affect the color values
+ // while intensity values < 0.5 make the overall color more transparent.
+ vec4 accent_color = (1.0 - accent) * u_accent * clamp(intensity * 2.0, 0.0, 1.0);
+ float shade = abs(mod((aspect + azimuth) / PI + 0.5, 2.0) - 1.0);
+ vec4 shade_color = mix(u_shadow, u_highlight, shade) * sin(scaledSlope) * clamp(intensity * 2.0, 0.0, 1.0);
+ gl_FragColor = accent_color * (1.0 - shade_color.a) + shade_color;
+
+#ifdef OVERDRAW_INSPECTOR
+ gl_FragColor = vec4(1.0);
+#endif
+}
+
+)MBGL_SHADER";
+
+} // namespace shaders
+} // namespace mbgl
diff --git a/src/mbgl/shaders/hillshade.hpp b/src/mbgl/shaders/hillshade.hpp
new file mode 100644
index 0000000000..a4a27cb595
--- /dev/null
+++ b/src/mbgl/shaders/hillshade.hpp
@@ -0,0 +1,16 @@
+// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED.
+
+#pragma once
+
+namespace mbgl {
+namespace shaders {
+
+class hillshade {
+public:
+ static const char* name;
+ static const char* vertexSource;
+ static const char* fragmentSource;
+};
+
+} // namespace shaders
+} // namespace mbgl
diff --git a/src/mbgl/shaders/hillshade_prepare.cpp b/src/mbgl/shaders/hillshade_prepare.cpp
new file mode 100644
index 0000000000..8d0571f6a4
--- /dev/null
+++ b/src/mbgl/shaders/hillshade_prepare.cpp
@@ -0,0 +1,100 @@
+// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED.
+
+#include <mbgl/shaders/hillshade_prepare.hpp>
+
+namespace mbgl {
+namespace shaders {
+
+const char* hillshade_prepare::name = "hillshade_prepare";
+const char* hillshade_prepare::vertexSource = R"MBGL_SHADER(
+uniform mat4 u_matrix;
+
+attribute vec2 a_pos;
+attribute vec2 a_texture_pos;
+
+varying vec2 v_pos;
+
+void main() {
+ gl_Position = u_matrix * vec4(a_pos, 0, 1);
+ v_pos = (a_texture_pos / 8192.0) / 2.0 + 0.25;
+}
+
+)MBGL_SHADER";
+const char* hillshade_prepare::fragmentSource = R"MBGL_SHADER(
+#ifdef GL_ES
+precision highp float;
+#endif
+
+uniform sampler2D u_image;
+varying vec2 v_pos;
+uniform vec2 u_dimension;
+uniform float u_zoom;
+uniform float u_maxzoom;
+
+float getElevation(vec2 coord, float bias) {
+ // Convert encoded elevation value to meters
+ vec4 data = texture2D(u_image, coord) * 255.0;
+ return (data.r + data.g * 256.0 + data.b * 256.0 * 256.0) / 4.0;
+}
+
+void main() {
+ vec2 epsilon = 1.0 / u_dimension;
+
+ // queried pixels:
+ // +-----------+
+ // | | | |
+ // | a | b | c |
+ // | | | |
+ // +-----------+
+ // | | | |
+ // | d | e | f |
+ // | | | |
+ // +-----------+
+ // | | | |
+ // | g | h | i |
+ // | | | |
+ // +-----------+
+
+ float a = getElevation(v_pos + vec2(-epsilon.x, -epsilon.y), 0.0);
+ float b = getElevation(v_pos + vec2(0, -epsilon.y), 0.0);
+ float c = getElevation(v_pos + vec2(epsilon.x, -epsilon.y), 0.0);
+ float d = getElevation(v_pos + vec2(-epsilon.x, 0), 0.0);
+ float e = getElevation(v_pos, 0.0);
+ float f = getElevation(v_pos + vec2(epsilon.x, 0), 0.0);
+ float g = getElevation(v_pos + vec2(-epsilon.x, epsilon.y), 0.0);
+ float h = getElevation(v_pos + vec2(0, epsilon.y), 0.0);
+ float i = getElevation(v_pos + vec2(epsilon.x, epsilon.y), 0.0);
+
+ // here we divide the x and y slopes by 8 * pixel size
+ // where pixel size (aka meters/pixel) is:
+ // circumference of the world / (pixels per tile * number of tiles)
+ // which is equivalent to: 8 * 40075016.6855785 / (512 * pow(2, u_zoom))
+ // which can be reduced to: pow(2, 19.25619978527 - u_zoom)
+ // we want to vertically exaggerate the hillshading though, because otherwise
+ // it is barely noticeable at low zooms. to do this, we multiply this by some
+ // scale factor pow(2, (u_zoom - u_maxzoom) * a) where a is an arbitrary value
+ // Here we use a=0.3 which works out to the expression below. see
+ // nickidlugash's awesome breakdown for more info
+ // https://github.com/mapbox/mapbox-gl-js/pull/5286#discussion_r148419556
+ float exaggeration = u_zoom < 2.0 ? 0.4 : u_zoom < 4.5 ? 0.35 : 0.3;
+
+ vec2 deriv = vec2(
+ (c + f + f + i) - (a + d + d + g),
+ (g + h + h + i) - (a + b + b + c)
+ ) / pow(2.0, (u_zoom - u_maxzoom) * exaggeration + 19.2562 - u_zoom);
+
+ gl_FragColor = clamp(vec4(
+ deriv.x / 2.0 + 0.5,
+ deriv.y / 2.0 + 0.5,
+ 1.0,
+ 1.0), 0.0, 1.0);
+
+#ifdef OVERDRAW_INSPECTOR
+ gl_FragColor = vec4(1.0);
+#endif
+}
+
+)MBGL_SHADER";
+
+} // namespace shaders
+} // namespace mbgl
diff --git a/src/mbgl/shaders/hillshade_prepare.hpp b/src/mbgl/shaders/hillshade_prepare.hpp
new file mode 100644
index 0000000000..c38b4a0d19
--- /dev/null
+++ b/src/mbgl/shaders/hillshade_prepare.hpp
@@ -0,0 +1,16 @@
+// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED.
+
+#pragma once
+
+namespace mbgl {
+namespace shaders {
+
+class hillshade_prepare {
+public:
+ static const char* name;
+ static const char* vertexSource;
+ static const char* fragmentSource;
+};
+
+} // namespace shaders
+} // namespace mbgl
diff --git a/src/mbgl/shaders/preludes.cpp b/src/mbgl/shaders/preludes.cpp
index feb185a684..6baa488a10 100644
--- a/src/mbgl/shaders/preludes.cpp
+++ b/src/mbgl/shaders/preludes.cpp
@@ -34,6 +34,10 @@ vec2 unpack_float(const float packedValue) {
return vec2(v0, packedIntValue - v0 * 256);
}
+vec2 unpack_opacity(const float packedOpacity) {
+ int intOpacity = int(packedOpacity) / 2;
+ return vec2(float(intOpacity) / 127.0, mod(packedOpacity, 2.0));
+}
// To minimize the number of attributes needed, we encode a 4-component
// color into a pair of floats (i.e. a vec2) as follows:
diff --git a/src/mbgl/shaders/symbol_icon.cpp b/src/mbgl/shaders/symbol_icon.cpp
index 1e96194738..f5c2bbe22d 100644
--- a/src/mbgl/shaders/symbol_icon.cpp
+++ b/src/mbgl/shaders/symbol_icon.cpp
@@ -12,6 +12,7 @@ const float PI = 3.141592653589793;
attribute vec4 a_pos_offset;
attribute vec4 a_data;
attribute vec3 a_projected_pos;
+attribute float a_fade_opacity;
uniform bool u_is_size_zoom_constant;
uniform bool u_is_size_feature_constant;
@@ -21,7 +22,7 @@ uniform highp float u_camera_to_center_distance;
uniform highp float u_pitch;
uniform bool u_rotate_symbol;
uniform highp float u_aspect_ratio;
-uniform highp float u_collision_y_stretch;
+uniform float u_fade_change;
#ifndef HAS_UNIFORM_u_opacity
@@ -43,7 +44,7 @@ uniform bool u_pitch_with_map;
uniform vec2 u_texsize;
varying vec2 v_tex;
-varying vec2 v_fade_tex;
+varying float v_fade_opacity;
void main() {
@@ -60,9 +61,7 @@ void main() {
vec2 a_tex = a_data.xy;
vec2 a_size = a_data.zw;
- highp vec2 angle_labelminzoom = unpack_float(a_projected_pos[2]);
- highp float segment_angle = -angle_labelminzoom[0] / 255.0 * 2.0 * PI;
- mediump float a_labelminzoom = angle_labelminzoom[1];
+ highp float segment_angle = -a_projected_pos[2];
float size;
if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {
@@ -106,19 +105,14 @@ void main() {
gl_Position = u_gl_coord_matrix * vec4(projected_pos.xy / projected_pos.w + rotation_matrix * (a_offset / 64.0 * fontScale), 0.0, 1.0);
v_tex = a_tex / u_texsize;
- // See comments in symbol_sdf.vertex
- highp float incidence_stretch = camera_to_anchor_distance / (u_camera_to_center_distance * cos(u_pitch));
- highp float collision_adjustment = max(1.0, incidence_stretch / u_collision_y_stretch);
-
- highp float collision_perspective_ratio = 1.0 + 0.5*((camera_to_anchor_distance / u_camera_to_center_distance) - 1.0);
- highp float perspective_zoom_adjust = floor(log2(collision_perspective_ratio * collision_adjustment) * 10.0);
- v_fade_tex = vec2((a_labelminzoom + perspective_zoom_adjust) / 255.0, 0.0);
+ vec2 fade_opacity = unpack_opacity(a_fade_opacity);
+ float fade_change = fade_opacity[1] > 0.5 ? u_fade_change : -u_fade_change;
+ v_fade_opacity = max(0.0, min(1.0, fade_opacity[0] + fade_change));
}
)MBGL_SHADER";
const char* symbol_icon::fragmentSource = R"MBGL_SHADER(
uniform sampler2D u_texture;
-uniform sampler2D u_fadetexture;
#ifndef HAS_UNIFORM_u_opacity
@@ -129,7 +123,7 @@ uniform lowp float u_opacity;
varying vec2 v_tex;
-varying vec2 v_fade_tex;
+varying float v_fade_opacity;
void main() {
@@ -138,7 +132,7 @@ void main() {
#endif
- lowp float alpha = texture2D(u_fadetexture, v_fade_tex).a * opacity;
+ lowp float alpha = opacity * v_fade_opacity;
gl_FragColor = texture2D(u_texture, v_tex) * alpha;
#ifdef OVERDRAW_INSPECTOR
diff --git a/src/mbgl/shaders/symbol_sdf.cpp b/src/mbgl/shaders/symbol_sdf.cpp
index a4427f31ab..441eaf7aac 100644
--- a/src/mbgl/shaders/symbol_sdf.cpp
+++ b/src/mbgl/shaders/symbol_sdf.cpp
@@ -12,6 +12,7 @@ const float PI = 3.141592653589793;
attribute vec4 a_pos_offset;
attribute vec4 a_data;
attribute vec3 a_projected_pos;
+attribute float a_fade_opacity;
// contents of a_size vary based on the type of property value
// used for {text,icon}-size.
@@ -81,12 +82,12 @@ uniform highp float u_pitch;
uniform bool u_rotate_symbol;
uniform highp float u_aspect_ratio;
uniform highp float u_camera_to_center_distance;
-uniform highp float u_collision_y_stretch;
+uniform float u_fade_change;
uniform vec2 u_texsize;
-varying vec4 v_data0;
-varying vec2 v_data1;
+varying vec2 v_data0;
+varying vec3 v_data1;
void main() {
@@ -131,9 +132,7 @@ void main() {
vec2 a_tex = a_data.xy;
vec2 a_size = a_data.zw;
- highp vec2 angle_labelminzoom = unpack_float(a_projected_pos[2]);
- highp float segment_angle = -angle_labelminzoom[0] / 255.0 * 2.0 * PI;
- mediump float a_labelminzoom = angle_labelminzoom[1];
+ highp float segment_angle = -a_projected_pos[2];
float size;
if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {
@@ -185,31 +184,12 @@ void main() {
float gamma_scale = gl_Position.w;
vec2 tex = a_tex / u_texsize;
- // incidence_stretch is the ratio of how much y space a label takes up on a tile while drawn perpendicular to the viewport vs
- // how much space it would take up if it were drawn flat on the tile
- // Using law of sines, camera_to_anchor/sin(ground_angle) = camera_to_center/sin(incidence_angle)
- // sin(incidence_angle) = 1/incidence_stretch
- // Incidence angle 90 -> head on, sin(incidence_angle) = 1, no incidence stretch
- // Incidence angle 1 -> very oblique, sin(incidence_angle) =~ 0, lots of incidence stretch
- // ground_angle = u_pitch + PI/2 -> sin(ground_angle) = cos(u_pitch)
- // This 2D calculation is only exactly correct when gl_Position.x is in the center of the viewport,
- // but it's a close enough approximation for our purposes
- highp float incidence_stretch = camera_to_anchor_distance / (u_camera_to_center_distance * cos(u_pitch));
- // incidence_stretch only applies to the y-axis, but without re-calculating the collision tile, we can't
- // adjust the size of only one axis. So, we do a crude approximation at placement time to get the aspect ratio
- // about right, and then do the rest of the adjustment here: there will be some extra padding on the x-axis,
- // but hopefully not too much.
- // Never make the adjustment less than 1.0: instead of allowing collisions on the x-axis, be conservative on
- // the y-axis.
- highp float collision_adjustment = max(1.0, incidence_stretch / u_collision_y_stretch);
-
- // Floor to 1/10th zoom to dodge precision issues that can cause partially hidden labels
- highp float collision_perspective_ratio = 1.0 + 0.5*((camera_to_anchor_distance / u_camera_to_center_distance) - 1.0);
- highp float perspective_zoom_adjust = floor(log2(collision_perspective_ratio * collision_adjustment) * 10.0);
- vec2 fade_tex = vec2((a_labelminzoom + perspective_zoom_adjust) / 255.0, 0.0);
-
- v_data0 = vec4(tex.x, tex.y, fade_tex.x, fade_tex.y);
- v_data1 = vec2(gamma_scale, size);
+ vec2 fade_opacity = unpack_opacity(a_fade_opacity);
+ float fade_change = fade_opacity[1] > 0.5 ? u_fade_change : -u_fade_change;
+ float interpolated_fade_opacity = max(0.0, min(1.0, fade_opacity[0] + fade_change));
+
+ v_data0 = vec2(tex.x, tex.y);
+ v_data1 = vec3(gamma_scale, size, interpolated_fade_opacity);
}
)MBGL_SHADER";
@@ -255,12 +235,11 @@ uniform lowp float u_halo_blur;
uniform sampler2D u_texture;
-uniform sampler2D u_fadetexture;
uniform highp float u_gamma_scale;
uniform bool u_is_text;
-varying vec4 v_data0;
-varying vec2 v_data1;
+varying vec2 v_data0;
+varying vec3 v_data1;
void main() {
@@ -290,9 +269,9 @@ void main() {
vec2 tex = v_data0.xy;
- vec2 fade_tex = v_data0.zw;
float gamma_scale = v_data1.x;
float size = v_data1.y;
+ float fade_opacity = v_data1[2];
float fontScale = u_is_text ? size / 24.0 : size;
@@ -306,11 +285,10 @@ void main() {
}
lowp float dist = texture2D(u_texture, tex).a;
- lowp float fade_alpha = texture2D(u_fadetexture, fade_tex).a;
highp float gamma_scaled = gamma * gamma_scale;
- highp float alpha = smoothstep(buff - gamma_scaled, buff + gamma_scaled, dist) * fade_alpha;
+ highp float alpha = smoothstep(buff - gamma_scaled, buff + gamma_scaled, dist);
- gl_FragColor = color * (alpha * opacity);
+ gl_FragColor = color * (alpha * opacity * fade_opacity);
#ifdef OVERDRAW_INSPECTOR
gl_FragColor = vec4(1.0);
diff --git a/src/mbgl/storage/asset_file_source.hpp b/src/mbgl/storage/asset_file_source.hpp
index 6ed7af8aaf..5d98b4e69e 100644
--- a/src/mbgl/storage/asset_file_source.hpp
+++ b/src/mbgl/storage/asset_file_source.hpp
@@ -15,6 +15,8 @@ public:
std::unique_ptr<AsyncRequest> request(const Resource&, Callback) override;
+ static bool acceptsURL(const std::string& url);
+
private:
class Impl;
diff --git a/src/mbgl/style/conversion/constant.cpp b/src/mbgl/style/conversion/constant.cpp
new file mode 100644
index 0000000000..e837c4e70b
--- /dev/null
+++ b/src/mbgl/style/conversion/constant.cpp
@@ -0,0 +1,94 @@
+#include <mbgl/style/conversion/constant.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+optional<bool> Converter<bool>::operator()(const Convertible& value, Error& error) const {
+ optional<bool> converted = toBool(value);
+ if (!converted) {
+ error = { "value must be a boolean" };
+ return {};
+ }
+ return *converted;
+}
+
+optional<float> Converter<float>::operator()(const Convertible& value, Error& error) const {
+ optional<float> converted = toNumber(value);
+ if (!converted) {
+ error = { "value must be a number" };
+ return {};
+ }
+ return *converted;
+}
+
+optional<std::string> Converter<std::string>::operator()(const Convertible& value, Error& error) const {
+ optional<std::string> converted = toString(value);
+ if (!converted) {
+ error = { "value must be a string" };
+ return {};
+ }
+ return *converted;
+}
+
+optional<Color> Converter<Color>::operator()(const Convertible& value, Error& error) const {
+ optional<std::string> string = toString(value);
+ if (!string) {
+ error = { "value must be a string" };
+ return {};
+ }
+
+ optional<Color> color = Color::parse(*string);
+ if (!color) {
+ error = { "value must be a valid color" };
+ return {};
+ }
+
+ return *color;
+}
+
+optional<std::vector<float>> Converter<std::vector<float>>::operator()(const Convertible& value, Error& error) const {
+ if (!isArray(value)) {
+ error = { "value must be an array" };
+ return {};
+ }
+
+ std::vector<float> result;
+ result.reserve(arrayLength(value));
+
+ for (std::size_t i = 0; i < arrayLength(value); ++i) {
+ optional<float> number = toNumber(arrayMember(value, i));
+ if (!number) {
+ error = { "value must be an array of numbers" };
+ return {};
+ }
+ result.push_back(*number);
+ }
+
+ return result;
+}
+
+optional<std::vector<std::string>> Converter<std::vector<std::string>>::operator()(const Convertible& value, Error& error) const {
+ if (!isArray(value)) {
+ error = { "value must be an array" };
+ return {};
+ }
+
+ std::vector<std::string> result;
+ result.reserve(arrayLength(value));
+
+ for (std::size_t i = 0; i < arrayLength(value); ++i) {
+ optional<std::string> string = toString(arrayMember(value, i));
+ if (!string) {
+ error = { "value must be an array of strings" };
+ return {};
+ }
+ result.push_back(*string);
+ }
+
+ return result;
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/conversion/coordinate.cpp b/src/mbgl/style/conversion/coordinate.cpp
new file mode 100644
index 0000000000..9b2be3381e
--- /dev/null
+++ b/src/mbgl/style/conversion/coordinate.cpp
@@ -0,0 +1,29 @@
+#include <mbgl/style/conversion/coordinate.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+optional<LatLng> Converter<LatLng>::operator() (const Convertible& value, Error& error) const {
+ if (!isArray(value) || arrayLength(value) < 2 ) {
+ error = { "coordinate array must contain numeric longitude and latitude values" };
+ return {};
+ }
+ //Style spec uses GeoJSON convention for specifying coordinates
+ optional<double> latitude = toDouble(arrayMember(value, 1));
+ optional<double> longitude = toDouble(arrayMember(value, 0));
+
+ if (!latitude || !longitude) {
+ error = { "coordinate array must contain numeric longitude and latitude values" };
+ return {};
+ }
+ if (*latitude < -90 || *latitude > 90 ){
+ error = { "coordinate latitude must be between -90 and 90" };
+ return {};
+ }
+ return LatLng(*latitude, *longitude);
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/conversion/filter.cpp b/src/mbgl/style/conversion/filter.cpp
new file mode 100644
index 0000000000..bb7bb6ea98
--- /dev/null
+++ b/src/mbgl/style/conversion/filter.cpp
@@ -0,0 +1,248 @@
+#include <mbgl/style/conversion/filter.hpp>
+#include <mbgl/util/geometry.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+static optional<Value> normalizeValue(const optional<Value>& value, Error& error) {
+ if (!value) {
+ error = { "filter expression value must be a boolean, number, or string" };
+ return {};
+ } else {
+ return *value;
+ }
+}
+
+static optional<FeatureType> toFeatureType(const Convertible& value, Error& error) {
+ optional<std::string> type = toString(value);
+ if (!type) {
+ error = { "value for $type filter must be a string" };
+ return {};
+ } else if (*type == "Point") {
+ return FeatureType::Point;
+ } else if (*type == "LineString") {
+ return FeatureType::LineString;
+ } else if (*type == "Polygon") {
+ return FeatureType::Polygon;
+ } else {
+ error = { "value for $type filter must be Point, LineString, or Polygon" };
+ return {};
+ }
+}
+
+static optional<FeatureIdentifier> toFeatureIdentifier(const Convertible& value, Error& error) {
+ optional<Value> identifier = toValue(value);
+ if (!identifier) {
+ error = { "filter expression value must be a boolean, number, or string" };
+ return {};
+ } else {
+ return (*identifier).match(
+ [] (uint64_t t) -> optional<FeatureIdentifier> { return { t }; },
+ [] ( int64_t t) -> optional<FeatureIdentifier> { return { t }; },
+ [] ( double t) -> optional<FeatureIdentifier> { return { t }; },
+ [] (const std::string& t) -> optional<FeatureIdentifier> { return { t }; },
+ [&] (const auto&) -> optional<FeatureIdentifier> {
+ error = { "filter expression value must be a boolean, number, or string" };
+ return {};
+ });
+ }
+}
+
+template <class FilterType, class IdentifierFilterType>
+optional<Filter> convertUnaryFilter(const Convertible& value, Error& error) {
+ if (arrayLength(value) < 2) {
+ error = { "filter expression must have 2 elements" };
+ return {};
+ }
+
+ optional<std::string> key = toString(arrayMember(value, 1));
+ if (!key) {
+ error = { "filter expression key must be a string" };
+ return {};
+ }
+
+ if (*key == "$id") {
+ return { IdentifierFilterType {} };
+ } else {
+ return { FilterType { *key } };
+ }
+}
+
+template <class FilterType, class TypeFilterType, class IdentifierFilterType>
+optional<Filter> convertEqualityFilter(const Convertible& value, Error& error) {
+ if (arrayLength(value) < 3) {
+ error = { "filter expression must have 3 elements" };
+ return {};
+ }
+
+ optional<std::string> key = toString(arrayMember(value, 1));
+ if (!key) {
+ error = { "filter expression key must be a string" };
+ return {};
+ }
+
+ if (*key == "$type") {
+ optional<FeatureType> filterValue = toFeatureType(arrayMember(value, 2), error);
+ if (!filterValue) {
+ return {};
+ }
+
+ return { TypeFilterType { *filterValue } };
+
+ } else if (*key == "$id") {
+ optional<FeatureIdentifier> filterValue = toFeatureIdentifier(arrayMember(value, 2), error);
+ if (!filterValue) {
+ return {};
+ }
+
+ return { IdentifierFilterType { *filterValue } };
+
+ } else {
+ optional<Value> filterValue = normalizeValue(toValue(arrayMember(value, 2)), error);
+ if (!filterValue) {
+ return {};
+ }
+
+ return { FilterType { *key, *filterValue } };
+ }
+}
+
+template <class FilterType>
+optional<Filter> convertBinaryFilter(const Convertible& value, Error& error) {
+ if (arrayLength(value) < 3) {
+ error = { "filter expression must have 3 elements" };
+ return {};
+ }
+
+ optional<std::string> key = toString(arrayMember(value, 1));
+ if (!key) {
+ error = { "filter expression key must be a string" };
+ return {};
+ }
+
+ optional<Value> filterValue = normalizeValue(toValue(arrayMember(value, 2)), error);
+ if (!filterValue) {
+ return {};
+ }
+
+ return { FilterType { *key, *filterValue } };
+}
+
+template <class FilterType, class TypeFilterType, class IdentifierFilterType>
+optional<Filter> convertSetFilter(const Convertible& value, Error& error) {
+ if (arrayLength(value) < 2) {
+ error = { "filter expression must at least 2 elements" };
+ return {};
+ }
+
+ optional<std::string> key = toString(arrayMember(value, 1));
+ if (!key) {
+ error = { "filter expression key must be a string" };
+ return {};
+ }
+
+ if (*key == "$type") {
+ std::vector<FeatureType> values;
+ for (std::size_t i = 2; i < arrayLength(value); ++i) {
+ optional<FeatureType> filterValue = toFeatureType(arrayMember(value, i), error);
+ if (!filterValue) {
+ return {};
+ }
+ values.push_back(*filterValue);
+ }
+
+ return { TypeFilterType { std::move(values) } };
+
+ } else if (*key == "$id") {
+ std::vector<FeatureIdentifier> values;
+ for (std::size_t i = 2; i < arrayLength(value); ++i) {
+ optional<FeatureIdentifier> filterValue = toFeatureIdentifier(arrayMember(value, i), error);
+ if (!filterValue) {
+ return {};
+ }
+ values.push_back(*filterValue);
+ }
+
+ return { IdentifierFilterType { std::move(values) } };
+
+ } else {
+ std::vector<Value> values;
+ for (std::size_t i = 2; i < arrayLength(value); ++i) {
+ optional<Value> filterValue = normalizeValue(toValue(arrayMember(value, i)), error);
+ if (!filterValue) {
+ return {};
+ }
+ values.push_back(*filterValue);
+ }
+
+ return { FilterType { *key, std::move(values) } };
+ }
+}
+
+template <class FilterType>
+optional<Filter> convertCompoundFilter(const Convertible& value, Error& error) {
+ std::vector<Filter> filters;
+ for (std::size_t i = 1; i < arrayLength(value); ++i) {
+ optional<Filter> element = convert<Filter>(arrayMember(value, i), error);
+ if (!element) {
+ return {};
+ }
+ filters.push_back(*element);
+ }
+
+ return { FilterType { std::move(filters) } };
+}
+
+optional<Filter> Converter<Filter>::operator()(const Convertible& value, Error& error) const {
+ if (!isArray(value)) {
+ error = { "filter expression must be an array" };
+ return {};
+ }
+
+ if (arrayLength(value) < 1) {
+ error = { "filter expression must have at least 1 element" };
+ return {};
+ }
+
+ optional<std::string> op = toString(arrayMember(value, 0));
+ if (!op) {
+ error = { "filter operator must be a string" };
+ return {};
+ }
+
+ if (*op == "==") {
+ return convertEqualityFilter<EqualsFilter, TypeEqualsFilter, IdentifierEqualsFilter>(value, error);
+ } else if (*op == "!=") {
+ return convertEqualityFilter<NotEqualsFilter, TypeNotEqualsFilter, IdentifierNotEqualsFilter>(value, error);
+ } else if (*op == ">") {
+ return convertBinaryFilter<GreaterThanFilter>(value, error);
+ } else if (*op == ">=") {
+ return convertBinaryFilter<GreaterThanEqualsFilter>(value, error);
+ } else if (*op == "<") {
+ return convertBinaryFilter<LessThanFilter>(value, error);
+ } else if (*op == "<=") {
+ return convertBinaryFilter<LessThanEqualsFilter>(value, error);
+ } else if (*op == "in") {
+ return convertSetFilter<InFilter, TypeInFilter, IdentifierInFilter>(value, error);
+ } else if (*op == "!in") {
+ return convertSetFilter<NotInFilter, TypeNotInFilter, IdentifierNotInFilter>(value, error);
+ } else if (*op == "all") {
+ return convertCompoundFilter<AllFilter>(value, error);
+ } else if (*op == "any") {
+ return convertCompoundFilter<AnyFilter>(value, error);
+ } else if (*op == "none") {
+ return convertCompoundFilter<NoneFilter>(value, error);
+ } else if (*op == "has") {
+ return convertUnaryFilter<HasFilter, HasIdentifierFilter>(value, error);
+ } else if (*op == "!has") {
+ return convertUnaryFilter<NotHasFilter, NotHasIdentifierFilter>(value, error);
+ }
+
+ error = { R"(filter operator must be one of "==", "!=", ">", ">=", "<", "<=", "in", "!in", "all", "any", "none", "has", or "!has")" };
+ return {};
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/conversion/geojson.cpp b/src/mbgl/style/conversion/geojson.cpp
index 8103e9014a..e39a1a80eb 100644
--- a/src/mbgl/style/conversion/geojson.cpp
+++ b/src/mbgl/style/conversion/geojson.cpp
@@ -1,26 +1,16 @@
#include <mbgl/style/conversion/geojson.hpp>
#include <mbgl/style/conversion/json.hpp>
-#include <mbgl/util/rapidjson.hpp>
-
-#include <mapbox/geojson.hpp>
-#include <mapbox/geojson/rapidjson.hpp>
namespace mbgl {
namespace style {
namespace conversion {
-optional<GeoJSON> Converter<GeoJSON>::operator()(const std::string& value, Error& error) const {
- return convertJSON<GeoJSON>(value, error);
+optional<GeoJSON> Converter<GeoJSON>::operator()(const Convertible& value, Error& error) const {
+ return toGeoJSON(value, error);
}
-template <>
-optional<GeoJSON> Converter<GeoJSON>::operator()(const JSValue& value, Error& error) const {
- try {
- return mapbox::geojson::convert(value);
- } catch (const std::exception& ex) {
- error = { ex.what() };
- return {};
- }
+optional<GeoJSON> parseGeoJSON(const std::string& value, Error& error) {
+ return convertJSON<GeoJSON>(value, error);
}
} // namespace conversion
diff --git a/src/mbgl/style/conversion/geojson_options.cpp b/src/mbgl/style/conversion/geojson_options.cpp
new file mode 100644
index 0000000000..a2c5ed8816
--- /dev/null
+++ b/src/mbgl/style/conversion/geojson_options.cpp
@@ -0,0 +1,85 @@
+#include <mbgl/style/conversion/geojson_options.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+optional<GeoJSONOptions> Converter<GeoJSONOptions>::operator()(const Convertible& value, Error& error) const {
+ GeoJSONOptions options;
+
+ const auto minzoomValue = objectMember(value, "minzoom");
+ if (minzoomValue) {
+ if (toNumber(*minzoomValue)) {
+ options.minzoom = static_cast<uint8_t>(*toNumber(*minzoomValue));
+ } else {
+ error = { "GeoJSON source minzoom value must be a number" };
+ return {};
+ }
+ }
+
+ const auto maxzoomValue = objectMember(value, "maxzoom");
+ if (maxzoomValue) {
+ if (toNumber(*maxzoomValue)) {
+ options.maxzoom = static_cast<uint8_t>(*toNumber(*maxzoomValue));
+ } else {
+ error = { "GeoJSON source maxzoom value must be a number" };
+ return {};
+ }
+ }
+
+ const auto bufferValue = objectMember(value, "buffer");
+ if (bufferValue) {
+ if (toNumber(*bufferValue)) {
+ options.buffer = static_cast<uint16_t>(*toNumber(*bufferValue));
+ } else {
+ error = { "GeoJSON source buffer value must be a number" };
+ return {};
+ }
+ }
+
+ const auto toleranceValue = objectMember(value, "tolerance");
+ if (toleranceValue) {
+ if (toNumber(*toleranceValue)) {
+ options.tolerance = static_cast<double>(*toNumber(*toleranceValue));
+ } else {
+ error = { "GeoJSON source tolerance value must be a number" };
+ return {};
+ }
+ }
+
+ const auto clusterValue = objectMember(value, "cluster");
+ if (clusterValue) {
+ if (toBool(*clusterValue)) {
+ options.cluster = *toBool(*clusterValue);
+ } else {
+ error = { "GeoJSON source cluster value must be a boolean" };
+ return {};
+ }
+ }
+
+ const auto clusterMaxZoomValue = objectMember(value, "clusterMaxZoom");
+ if (clusterMaxZoomValue) {
+ if (toNumber(*clusterMaxZoomValue)) {
+ options.clusterMaxZoom = static_cast<uint8_t>(*toNumber(*clusterMaxZoomValue));
+ } else {
+ error = { "GeoJSON source clusterMaxZoom value must be a number" };
+ return {};
+ }
+ }
+
+ const auto clusterRadiusValue = objectMember(value, "clusterRadius");
+ if (clusterRadiusValue) {
+ if (toNumber(*clusterRadiusValue)) {
+ options.clusterRadius = static_cast<double>(*toNumber(*clusterRadiusValue));
+ } else {
+ error = { "GeoJSON source clusterRadius value must be a number" };
+ return {};
+ }
+ }
+
+ return { options };
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/conversion/get_json_type.cpp b/src/mbgl/style/conversion/get_json_type.cpp
new file mode 100644
index 0000000000..cd3b4608b1
--- /dev/null
+++ b/src/mbgl/style/conversion/get_json_type.cpp
@@ -0,0 +1,34 @@
+#include <mbgl/style/conversion/get_json_type.hpp>
+#include <mbgl/util/feature.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+std::string getJSONType(const Convertible& value) {
+ if (isUndefined(value)) {
+ return "null";
+ }
+ if (isArray(value)) {
+ return "array";
+ }
+ if (isObject(value)) {
+ return "object";
+ }
+ optional<mbgl::Value> v = toValue(value);
+
+ // Since we've already checked the non-atomic types above, value must then
+ // be a string, number, or boolean -- thus, assume that the toValue()
+ // conversion succeeds.
+ assert(v);
+
+ return v->match(
+ [&] (const std::string&) { return "string"; },
+ [&] (bool) { return "boolean"; },
+ [&] (auto) { return "number"; }
+ );
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/conversion/json.hpp b/src/mbgl/style/conversion/json.hpp
index 0817ac09df..7dd2378f6b 100644
--- a/src/mbgl/style/conversion/json.hpp
+++ b/src/mbgl/style/conversion/json.hpp
@@ -20,7 +20,7 @@ optional<T> convertJSON(const std::string& json, Error& error, Args&&...args) {
return {};
}
- return convert<T, JSValue>(document, error, std::forward<Args>(args)...);
+ return convert<T>(document, error, std::forward<Args>(args)...);
}
} // namespace conversion
diff --git a/src/mbgl/style/conversion/layer.cpp b/src/mbgl/style/conversion/layer.cpp
new file mode 100644
index 0000000000..19472bc8d6
--- /dev/null
+++ b/src/mbgl/style/conversion/layer.cpp
@@ -0,0 +1,232 @@
+#include <mbgl/style/conversion/layer.hpp>
+#include <mbgl/style/conversion/constant.hpp>
+#include <mbgl/style/conversion/filter.hpp>
+#include <mbgl/style/conversion/make_property_setters.hpp>
+#include <mbgl/style/layers/background_layer.hpp>
+#include <mbgl/style/layers/circle_layer.hpp>
+#include <mbgl/style/layers/fill_layer.hpp>
+#include <mbgl/style/layers/fill_extrusion_layer.hpp>
+#include <mbgl/style/layers/heatmap_layer.hpp>
+#include <mbgl/style/layers/hillshade_layer.hpp>
+#include <mbgl/style/layers/line_layer.hpp>
+#include <mbgl/style/layers/raster_layer.hpp>
+#include <mbgl/style/layers/symbol_layer.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+optional<Error> setLayoutProperty(Layer& layer, const std::string& name, const Convertible& value) {
+ static const auto setters = makeLayoutPropertySetters();
+ auto it = setters.find(name);
+ if (it == setters.end()) {
+ return Error { "property not found" };
+ }
+ return it->second(layer, value);
+}
+
+optional<Error> setPaintProperty(Layer& layer, const std::string& name, const Convertible& value) {
+ static const auto setters = makePaintPropertySetters();
+ auto it = setters.find(name);
+ if (it == setters.end()) {
+ return Error { "property not found" };
+ }
+ return it->second(layer, value);
+}
+
+optional<Error> setPaintProperties(Layer& layer, const Convertible& value) {
+ auto paintValue = objectMember(value, "paint");
+ if (!paintValue) {
+ return {};
+ }
+ if (!isObject(*paintValue)) {
+ return { { "paint must be an object" } };
+ }
+ return eachMember(*paintValue, [&] (const std::string& k, const Convertible& v) {
+ return setPaintProperty(layer, k, v);
+ });
+}
+
+template <class LayerType>
+optional<std::unique_ptr<Layer>> convertVectorLayer(const std::string& id, const Convertible& value, Error& error) {
+ auto sourceValue = objectMember(value, "source");
+ if (!sourceValue) {
+ error = { "layer must have a source" };
+ return {};
+ }
+
+ optional<std::string> source = toString(*sourceValue);
+ if (!source) {
+ error = { "layer source must be a string" };
+ return {};
+ }
+
+ std::unique_ptr<LayerType> layer = std::make_unique<LayerType>(id, *source);
+
+ auto sourceLayerValue = objectMember(value, "source-layer");
+ if (sourceLayerValue) {
+ optional<std::string> sourceLayer = toString(*sourceLayerValue);
+ if (!sourceLayer) {
+ error = { "layer source-layer must be a string" };
+ return {};
+ }
+ layer->setSourceLayer(*sourceLayer);
+ }
+
+ auto filterValue = objectMember(value, "filter");
+ if (filterValue) {
+ optional<Filter> filter = convert<Filter>(*filterValue, error);
+ if (!filter) {
+ return {};
+ }
+ layer->setFilter(*filter);
+ }
+
+ return { std::move(layer) };
+}
+
+static optional<std::unique_ptr<Layer>> convertRasterLayer(const std::string& id, const Convertible& value, Error& error) {
+ auto sourceValue = objectMember(value, "source");
+ if (!sourceValue) {
+ error = { "layer must have a source" };
+ return {};
+ }
+
+ optional<std::string> source = toString(*sourceValue);
+ if (!source) {
+ error = { "layer source must be a string" };
+ return {};
+ }
+
+ return { std::make_unique<RasterLayer>(id, *source) };
+}
+
+static optional<std::unique_ptr<Layer>> convertHillshadeLayer(const std::string& id, const Convertible& value, Error& error) {
+ auto sourceValue = objectMember(value, "source");
+ if (!sourceValue) {
+ error = { "layer must have a source" };
+ return {};
+ }
+
+ optional<std::string> source = toString(*sourceValue);
+ if (!source) {
+ error = { "layer source must be a string" };
+ return {};
+ }
+
+ return { std::make_unique<HillshadeLayer>(id, *source) };
+}
+
+
+static optional<std::unique_ptr<Layer>> convertBackgroundLayer(const std::string& id, const Convertible&, Error&) {
+ return { std::make_unique<BackgroundLayer>(id) };
+}
+
+optional<std::unique_ptr<Layer>> Converter<std::unique_ptr<Layer>>::operator()(const Convertible& value, Error& error) const {
+ if (!isObject(value)) {
+ error = { "layer must be an object" };
+ return {};
+ }
+
+ auto idValue = objectMember(value, "id");
+ if (!idValue) {
+ error = { "layer must have an id" };
+ return {};
+ }
+
+ optional<std::string> id = toString(*idValue);
+ if (!id) {
+ error = { "layer id must be a string" };
+ return {};
+ }
+
+ auto typeValue = objectMember(value, "type");
+ if (!typeValue) {
+ error = { "layer must have a type" };
+ return {};
+ }
+
+ optional<std::string> type = toString(*typeValue);
+ if (!type) {
+ error = { "layer type must be a string" };
+ return {};
+ }
+
+ optional<std::unique_ptr<Layer>> converted;
+
+ if (*type == "fill") {
+ converted = convertVectorLayer<FillLayer>(*id, value, error);
+ } else if (*type == "fill-extrusion") {
+ converted = convertVectorLayer<FillExtrusionLayer>(*id, value, error);
+ } else if (*type == "line") {
+ converted = convertVectorLayer<LineLayer>(*id, value, error);
+ } else if (*type == "circle") {
+ converted = convertVectorLayer<CircleLayer>(*id, value, error);
+ } else if (*type == "symbol") {
+ converted = convertVectorLayer<SymbolLayer>(*id, value, error);
+ } else if (*type == "raster") {
+ converted = convertRasterLayer(*id, value, error);
+ } else if (*type == "heatmap") {
+ converted = convertVectorLayer<HeatmapLayer>(*id, value, error);
+ } else if (*type == "hillshade") {
+ converted = convertHillshadeLayer(*id, value, error);
+ } else if (*type == "background") {
+ converted = convertBackgroundLayer(*id, value, error);
+ } else {
+ error = { "invalid layer type" };
+ return {};
+ }
+
+ if (!converted) {
+ return converted;
+ }
+
+ std::unique_ptr<Layer> layer = std::move(*converted);
+
+ auto minzoomValue = objectMember(value, "minzoom");
+ if (minzoomValue) {
+ optional<float> minzoom = toNumber(*minzoomValue);
+ if (!minzoom) {
+ error = { "minzoom must be numeric" };
+ return {};
+ }
+ layer->setMinZoom(*minzoom);
+ }
+
+ auto maxzoomValue = objectMember(value, "maxzoom");
+ if (maxzoomValue) {
+ optional<float> maxzoom = toNumber(*maxzoomValue);
+ if (!maxzoom) {
+ error = { "maxzoom must be numeric" };
+ return {};
+ }
+ layer->setMaxZoom(*maxzoom);
+ }
+
+ auto layoutValue = objectMember(value, "layout");
+ if (layoutValue) {
+ if (!isObject(*layoutValue)) {
+ error = { "layout must be an object" };
+ return {};
+ }
+ optional<Error> error_ = eachMember(*layoutValue, [&] (const std::string& k, const Convertible& v) {
+ return setLayoutProperty(*layer, k, v);
+ });
+ if (error_) {
+ error = *error_;
+ return {};
+ }
+ }
+
+ optional<Error> error_ = setPaintProperties(*layer, value);
+ if (error_) {
+ error = *error_;
+ return {};
+ }
+
+ return std::move(layer);
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/conversion/light.cpp b/src/mbgl/style/conversion/light.cpp
new file mode 100644
index 0000000000..f521f74386
--- /dev/null
+++ b/src/mbgl/style/conversion/light.cpp
@@ -0,0 +1,115 @@
+#include <mbgl/style/conversion/light.hpp>
+#include <mbgl/style/conversion/position.hpp>
+#include <mbgl/style/conversion/property_value.hpp>
+#include <mbgl/style/conversion/transition_options.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+optional<Light> Converter<Light>::operator()(const Convertible& value, Error& error) const {
+ if (!isObject(value)) {
+ error = { "light must be an object" };
+ return {};
+ }
+
+ Light light;
+
+ const auto anchor = objectMember(value, "anchor");
+ if (anchor) {
+ optional<PropertyValue<LightAnchorType>> convertedAnchor =
+ convert<PropertyValue<LightAnchorType>>(*anchor, error);
+
+ if (convertedAnchor) {
+ light.setAnchor(*convertedAnchor);
+ } else {
+ return {};
+ }
+ }
+
+ const auto anchorTransition = objectMember(value, "anchor-transition");
+ if (anchorTransition) {
+ optional<TransitionOptions> transition =
+ convert<TransitionOptions>(*anchorTransition, error);
+ if (transition) {
+ light.setAnchorTransition(*transition);
+ } else {
+ return {};
+ }
+ }
+
+ const auto color = objectMember(value, "color");
+ if (color) {
+ optional<PropertyValue<Color>> convertedColor =
+ convert<PropertyValue<Color>>(*color, error);
+
+ if (convertedColor) {
+ light.setColor(*convertedColor);
+ } else {
+ return {};
+ }
+ }
+
+ const auto colorTransition = objectMember(value, "color-transition");
+ if (colorTransition) {
+ optional<TransitionOptions> transition =
+ convert<TransitionOptions>(*colorTransition, error);
+ if (transition) {
+ light.setColorTransition(*transition);
+ } else {
+ return {};
+ }
+ }
+
+ const auto position = objectMember(value, "position");
+ if (position) {
+ optional<PropertyValue<Position>> convertedPosition =
+ convert<PropertyValue<Position>>(*position, error);
+
+ if (convertedPosition) {
+ light.setPosition(*convertedPosition);
+ } else {
+ return {};
+ }
+ }
+
+ const auto positionTransition = objectMember(value, "position-transition");
+ if (positionTransition) {
+ optional<TransitionOptions> transition =
+ convert<TransitionOptions>(*positionTransition, error);
+ if (transition) {
+ light.setPositionTransition(*transition);
+ } else {
+ return {};
+ }
+ }
+
+ const auto intensity = objectMember(value, "intensity");
+ if (intensity) {
+ optional<PropertyValue<float>> convertedIntensity =
+ convert<PropertyValue<float>>(*intensity, error);
+
+ if (convertedIntensity) {
+ light.setIntensity(*convertedIntensity);
+ } else {
+ return {};
+ }
+ }
+
+ const auto intensityTransition = objectMember(value, "intensity-transition");
+ if (intensityTransition) {
+ optional<TransitionOptions> transition =
+ convert<TransitionOptions>(*intensityTransition, error);
+ if (transition) {
+ light.setIntensityTransition(*transition);
+ } else {
+ return {};
+ }
+ }
+
+ return { std::move(light) };
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/conversion/make_property_setters.hpp b/src/mbgl/style/conversion/make_property_setters.hpp
new file mode 100644
index 0000000000..25c8fdb1ca
--- /dev/null
+++ b/src/mbgl/style/conversion/make_property_setters.hpp
@@ -0,0 +1,237 @@
+#pragma once
+
+// This file is generated. Edit make_property_setters.hpp.ejs, then run `make style-code`.
+
+#include <mbgl/style/conversion/property_setter.hpp>
+
+#include <mbgl/style/layers/fill_layer.hpp>
+#include <mbgl/style/layers/line_layer.hpp>
+#include <mbgl/style/layers/symbol_layer.hpp>
+#include <mbgl/style/layers/circle_layer.hpp>
+#include <mbgl/style/layers/heatmap_layer.hpp>
+#include <mbgl/style/layers/fill_extrusion_layer.hpp>
+#include <mbgl/style/layers/raster_layer.hpp>
+#include <mbgl/style/layers/hillshade_layer.hpp>
+#include <mbgl/style/layers/background_layer.hpp>
+
+#include <unordered_map>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+inline auto makeLayoutPropertySetters() {
+ std::unordered_map<std::string, PropertySetter> result;
+
+ result["visibility"] = &setVisibility;
+
+
+ result["line-cap"] = &setProperty<LineLayer, PropertyValue<LineCapType>, &LineLayer::setLineCap>;
+ result["line-join"] = &setProperty<LineLayer, DataDrivenPropertyValue<LineJoinType>, &LineLayer::setLineJoin>;
+ result["line-miter-limit"] = &setProperty<LineLayer, PropertyValue<float>, &LineLayer::setLineMiterLimit>;
+ result["line-round-limit"] = &setProperty<LineLayer, PropertyValue<float>, &LineLayer::setLineRoundLimit>;
+
+ result["symbol-placement"] = &setProperty<SymbolLayer, PropertyValue<SymbolPlacementType>, &SymbolLayer::setSymbolPlacement>;
+ result["symbol-spacing"] = &setProperty<SymbolLayer, PropertyValue<float>, &SymbolLayer::setSymbolSpacing>;
+ result["symbol-avoid-edges"] = &setProperty<SymbolLayer, PropertyValue<bool>, &SymbolLayer::setSymbolAvoidEdges>;
+ result["icon-allow-overlap"] = &setProperty<SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconAllowOverlap>;
+ result["icon-ignore-placement"] = &setProperty<SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconIgnorePlacement>;
+ result["icon-optional"] = &setProperty<SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconOptional>;
+ result["icon-rotation-alignment"] = &setProperty<SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setIconRotationAlignment>;
+ result["icon-size"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconSize>;
+ result["icon-text-fit"] = &setProperty<SymbolLayer, PropertyValue<IconTextFitType>, &SymbolLayer::setIconTextFit>;
+ result["icon-text-fit-padding"] = &setProperty<SymbolLayer, PropertyValue<std::array<float, 4>>, &SymbolLayer::setIconTextFitPadding>;
+ result["icon-image"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<std::string>, &SymbolLayer::setIconImage>;
+ result["icon-rotate"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconRotate>;
+ result["icon-padding"] = &setProperty<SymbolLayer, PropertyValue<float>, &SymbolLayer::setIconPadding>;
+ result["icon-keep-upright"] = &setProperty<SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconKeepUpright>;
+ result["icon-offset"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<std::array<float, 2>>, &SymbolLayer::setIconOffset>;
+ result["icon-anchor"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<SymbolAnchorType>, &SymbolLayer::setIconAnchor>;
+ result["icon-pitch-alignment"] = &setProperty<SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setIconPitchAlignment>;
+ result["text-pitch-alignment"] = &setProperty<SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setTextPitchAlignment>;
+ result["text-rotation-alignment"] = &setProperty<SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setTextRotationAlignment>;
+ result["text-field"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<std::string>, &SymbolLayer::setTextField>;
+ result["text-font"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<std::vector<std::string>>, &SymbolLayer::setTextFont>;
+ result["text-size"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextSize>;
+ result["text-max-width"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextMaxWidth>;
+ result["text-line-height"] = &setProperty<SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextLineHeight>;
+ result["text-letter-spacing"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextLetterSpacing>;
+ result["text-justify"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<TextJustifyType>, &SymbolLayer::setTextJustify>;
+ result["text-anchor"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<SymbolAnchorType>, &SymbolLayer::setTextAnchor>;
+ result["text-max-angle"] = &setProperty<SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextMaxAngle>;
+ result["text-rotate"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextRotate>;
+ result["text-padding"] = &setProperty<SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextPadding>;
+ result["text-keep-upright"] = &setProperty<SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextKeepUpright>;
+ result["text-transform"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<TextTransformType>, &SymbolLayer::setTextTransform>;
+ result["text-offset"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<std::array<float, 2>>, &SymbolLayer::setTextOffset>;
+ result["text-allow-overlap"] = &setProperty<SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextAllowOverlap>;
+ result["text-ignore-placement"] = &setProperty<SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextIgnorePlacement>;
+ result["text-optional"] = &setProperty<SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextOptional>;
+
+
+
+
+
+
+
+ return result;
+}
+
+inline auto makePaintPropertySetters() {
+ std::unordered_map<std::string, PropertySetter> result;
+
+ result["fill-antialias"] = &setProperty<FillLayer, PropertyValue<bool>, &FillLayer::setFillAntialias>;
+ result["fill-antialias-transition"] = &setTransition<FillLayer, &FillLayer::setFillAntialiasTransition>;
+ result["fill-opacity"] = &setProperty<FillLayer, DataDrivenPropertyValue<float>, &FillLayer::setFillOpacity>;
+ result["fill-opacity-transition"] = &setTransition<FillLayer, &FillLayer::setFillOpacityTransition>;
+ result["fill-color"] = &setProperty<FillLayer, DataDrivenPropertyValue<Color>, &FillLayer::setFillColor>;
+ result["fill-color-transition"] = &setTransition<FillLayer, &FillLayer::setFillColorTransition>;
+ result["fill-outline-color"] = &setProperty<FillLayer, DataDrivenPropertyValue<Color>, &FillLayer::setFillOutlineColor>;
+ result["fill-outline-color-transition"] = &setTransition<FillLayer, &FillLayer::setFillOutlineColorTransition>;
+ result["fill-translate"] = &setProperty<FillLayer, PropertyValue<std::array<float, 2>>, &FillLayer::setFillTranslate>;
+ result["fill-translate-transition"] = &setTransition<FillLayer, &FillLayer::setFillTranslateTransition>;
+ result["fill-translate-anchor"] = &setProperty<FillLayer, PropertyValue<TranslateAnchorType>, &FillLayer::setFillTranslateAnchor>;
+ result["fill-translate-anchor-transition"] = &setTransition<FillLayer, &FillLayer::setFillTranslateAnchorTransition>;
+ result["fill-pattern"] = &setProperty<FillLayer, PropertyValue<std::string>, &FillLayer::setFillPattern>;
+ result["fill-pattern-transition"] = &setTransition<FillLayer, &FillLayer::setFillPatternTransition>;
+
+ result["line-opacity"] = &setProperty<LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineOpacity>;
+ result["line-opacity-transition"] = &setTransition<LineLayer, &LineLayer::setLineOpacityTransition>;
+ result["line-color"] = &setProperty<LineLayer, DataDrivenPropertyValue<Color>, &LineLayer::setLineColor>;
+ result["line-color-transition"] = &setTransition<LineLayer, &LineLayer::setLineColorTransition>;
+ result["line-translate"] = &setProperty<LineLayer, PropertyValue<std::array<float, 2>>, &LineLayer::setLineTranslate>;
+ result["line-translate-transition"] = &setTransition<LineLayer, &LineLayer::setLineTranslateTransition>;
+ result["line-translate-anchor"] = &setProperty<LineLayer, PropertyValue<TranslateAnchorType>, &LineLayer::setLineTranslateAnchor>;
+ result["line-translate-anchor-transition"] = &setTransition<LineLayer, &LineLayer::setLineTranslateAnchorTransition>;
+ result["line-width"] = &setProperty<LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineWidth>;
+ result["line-width-transition"] = &setTransition<LineLayer, &LineLayer::setLineWidthTransition>;
+ result["line-gap-width"] = &setProperty<LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineGapWidth>;
+ result["line-gap-width-transition"] = &setTransition<LineLayer, &LineLayer::setLineGapWidthTransition>;
+ result["line-offset"] = &setProperty<LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineOffset>;
+ result["line-offset-transition"] = &setTransition<LineLayer, &LineLayer::setLineOffsetTransition>;
+ result["line-blur"] = &setProperty<LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineBlur>;
+ result["line-blur-transition"] = &setTransition<LineLayer, &LineLayer::setLineBlurTransition>;
+ result["line-dasharray"] = &setProperty<LineLayer, PropertyValue<std::vector<float>>, &LineLayer::setLineDasharray>;
+ result["line-dasharray-transition"] = &setTransition<LineLayer, &LineLayer::setLineDasharrayTransition>;
+ result["line-pattern"] = &setProperty<LineLayer, PropertyValue<std::string>, &LineLayer::setLinePattern>;
+ result["line-pattern-transition"] = &setTransition<LineLayer, &LineLayer::setLinePatternTransition>;
+
+ result["icon-opacity"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconOpacity>;
+ result["icon-opacity-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setIconOpacityTransition>;
+ result["icon-color"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<Color>, &SymbolLayer::setIconColor>;
+ result["icon-color-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setIconColorTransition>;
+ result["icon-halo-color"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<Color>, &SymbolLayer::setIconHaloColor>;
+ result["icon-halo-color-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setIconHaloColorTransition>;
+ result["icon-halo-width"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconHaloWidth>;
+ result["icon-halo-width-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setIconHaloWidthTransition>;
+ result["icon-halo-blur"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconHaloBlur>;
+ result["icon-halo-blur-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setIconHaloBlurTransition>;
+ result["icon-translate"] = &setProperty<SymbolLayer, PropertyValue<std::array<float, 2>>, &SymbolLayer::setIconTranslate>;
+ result["icon-translate-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setIconTranslateTransition>;
+ result["icon-translate-anchor"] = &setProperty<SymbolLayer, PropertyValue<TranslateAnchorType>, &SymbolLayer::setIconTranslateAnchor>;
+ result["icon-translate-anchor-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setIconTranslateAnchorTransition>;
+ result["text-opacity"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextOpacity>;
+ result["text-opacity-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setTextOpacityTransition>;
+ result["text-color"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<Color>, &SymbolLayer::setTextColor>;
+ result["text-color-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setTextColorTransition>;
+ result["text-halo-color"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<Color>, &SymbolLayer::setTextHaloColor>;
+ result["text-halo-color-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setTextHaloColorTransition>;
+ result["text-halo-width"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextHaloWidth>;
+ result["text-halo-width-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setTextHaloWidthTransition>;
+ result["text-halo-blur"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextHaloBlur>;
+ result["text-halo-blur-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setTextHaloBlurTransition>;
+ result["text-translate"] = &setProperty<SymbolLayer, PropertyValue<std::array<float, 2>>, &SymbolLayer::setTextTranslate>;
+ result["text-translate-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setTextTranslateTransition>;
+ result["text-translate-anchor"] = &setProperty<SymbolLayer, PropertyValue<TranslateAnchorType>, &SymbolLayer::setTextTranslateAnchor>;
+ result["text-translate-anchor-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setTextTranslateAnchorTransition>;
+
+ result["circle-radius"] = &setProperty<CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleRadius>;
+ result["circle-radius-transition"] = &setTransition<CircleLayer, &CircleLayer::setCircleRadiusTransition>;
+ result["circle-color"] = &setProperty<CircleLayer, DataDrivenPropertyValue<Color>, &CircleLayer::setCircleColor>;
+ result["circle-color-transition"] = &setTransition<CircleLayer, &CircleLayer::setCircleColorTransition>;
+ result["circle-blur"] = &setProperty<CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleBlur>;
+ result["circle-blur-transition"] = &setTransition<CircleLayer, &CircleLayer::setCircleBlurTransition>;
+ result["circle-opacity"] = &setProperty<CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleOpacity>;
+ result["circle-opacity-transition"] = &setTransition<CircleLayer, &CircleLayer::setCircleOpacityTransition>;
+ result["circle-translate"] = &setProperty<CircleLayer, PropertyValue<std::array<float, 2>>, &CircleLayer::setCircleTranslate>;
+ result["circle-translate-transition"] = &setTransition<CircleLayer, &CircleLayer::setCircleTranslateTransition>;
+ result["circle-translate-anchor"] = &setProperty<CircleLayer, PropertyValue<TranslateAnchorType>, &CircleLayer::setCircleTranslateAnchor>;
+ result["circle-translate-anchor-transition"] = &setTransition<CircleLayer, &CircleLayer::setCircleTranslateAnchorTransition>;
+ result["circle-pitch-scale"] = &setProperty<CircleLayer, PropertyValue<CirclePitchScaleType>, &CircleLayer::setCirclePitchScale>;
+ result["circle-pitch-scale-transition"] = &setTransition<CircleLayer, &CircleLayer::setCirclePitchScaleTransition>;
+ result["circle-pitch-alignment"] = &setProperty<CircleLayer, PropertyValue<AlignmentType>, &CircleLayer::setCirclePitchAlignment>;
+ result["circle-pitch-alignment-transition"] = &setTransition<CircleLayer, &CircleLayer::setCirclePitchAlignmentTransition>;
+ result["circle-stroke-width"] = &setProperty<CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleStrokeWidth>;
+ result["circle-stroke-width-transition"] = &setTransition<CircleLayer, &CircleLayer::setCircleStrokeWidthTransition>;
+ result["circle-stroke-color"] = &setProperty<CircleLayer, DataDrivenPropertyValue<Color>, &CircleLayer::setCircleStrokeColor>;
+ result["circle-stroke-color-transition"] = &setTransition<CircleLayer, &CircleLayer::setCircleStrokeColorTransition>;
+ result["circle-stroke-opacity"] = &setProperty<CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleStrokeOpacity>;
+ result["circle-stroke-opacity-transition"] = &setTransition<CircleLayer, &CircleLayer::setCircleStrokeOpacityTransition>;
+
+ result["heatmap-radius"] = &setProperty<HeatmapLayer, DataDrivenPropertyValue<float>, &HeatmapLayer::setHeatmapRadius>;
+ result["heatmap-radius-transition"] = &setTransition<HeatmapLayer, &HeatmapLayer::setHeatmapRadiusTransition>;
+ result["heatmap-weight"] = &setProperty<HeatmapLayer, DataDrivenPropertyValue<float>, &HeatmapLayer::setHeatmapWeight>;
+ result["heatmap-weight-transition"] = &setTransition<HeatmapLayer, &HeatmapLayer::setHeatmapWeightTransition>;
+ result["heatmap-intensity"] = &setProperty<HeatmapLayer, PropertyValue<float>, &HeatmapLayer::setHeatmapIntensity>;
+ result["heatmap-intensity-transition"] = &setTransition<HeatmapLayer, &HeatmapLayer::setHeatmapIntensityTransition>;
+ result["heatmap-color"] = &setProperty<HeatmapLayer, HeatmapColorPropertyValue, &HeatmapLayer::setHeatmapColor>;
+ result["heatmap-color-transition"] = &setTransition<HeatmapLayer, &HeatmapLayer::setHeatmapColorTransition>;
+ result["heatmap-opacity"] = &setProperty<HeatmapLayer, PropertyValue<float>, &HeatmapLayer::setHeatmapOpacity>;
+ result["heatmap-opacity-transition"] = &setTransition<HeatmapLayer, &HeatmapLayer::setHeatmapOpacityTransition>;
+
+ result["fill-extrusion-opacity"] = &setProperty<FillExtrusionLayer, PropertyValue<float>, &FillExtrusionLayer::setFillExtrusionOpacity>;
+ result["fill-extrusion-opacity-transition"] = &setTransition<FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionOpacityTransition>;
+ result["fill-extrusion-color"] = &setProperty<FillExtrusionLayer, DataDrivenPropertyValue<Color>, &FillExtrusionLayer::setFillExtrusionColor>;
+ result["fill-extrusion-color-transition"] = &setTransition<FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionColorTransition>;
+ result["fill-extrusion-translate"] = &setProperty<FillExtrusionLayer, PropertyValue<std::array<float, 2>>, &FillExtrusionLayer::setFillExtrusionTranslate>;
+ result["fill-extrusion-translate-transition"] = &setTransition<FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionTranslateTransition>;
+ result["fill-extrusion-translate-anchor"] = &setProperty<FillExtrusionLayer, PropertyValue<TranslateAnchorType>, &FillExtrusionLayer::setFillExtrusionTranslateAnchor>;
+ result["fill-extrusion-translate-anchor-transition"] = &setTransition<FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionTranslateAnchorTransition>;
+ result["fill-extrusion-pattern"] = &setProperty<FillExtrusionLayer, PropertyValue<std::string>, &FillExtrusionLayer::setFillExtrusionPattern>;
+ result["fill-extrusion-pattern-transition"] = &setTransition<FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionPatternTransition>;
+ result["fill-extrusion-height"] = &setProperty<FillExtrusionLayer, DataDrivenPropertyValue<float>, &FillExtrusionLayer::setFillExtrusionHeight>;
+ result["fill-extrusion-height-transition"] = &setTransition<FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionHeightTransition>;
+ result["fill-extrusion-base"] = &setProperty<FillExtrusionLayer, DataDrivenPropertyValue<float>, &FillExtrusionLayer::setFillExtrusionBase>;
+ result["fill-extrusion-base-transition"] = &setTransition<FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionBaseTransition>;
+
+ result["raster-opacity"] = &setProperty<RasterLayer, PropertyValue<float>, &RasterLayer::setRasterOpacity>;
+ result["raster-opacity-transition"] = &setTransition<RasterLayer, &RasterLayer::setRasterOpacityTransition>;
+ result["raster-hue-rotate"] = &setProperty<RasterLayer, PropertyValue<float>, &RasterLayer::setRasterHueRotate>;
+ result["raster-hue-rotate-transition"] = &setTransition<RasterLayer, &RasterLayer::setRasterHueRotateTransition>;
+ result["raster-brightness-min"] = &setProperty<RasterLayer, PropertyValue<float>, &RasterLayer::setRasterBrightnessMin>;
+ result["raster-brightness-min-transition"] = &setTransition<RasterLayer, &RasterLayer::setRasterBrightnessMinTransition>;
+ result["raster-brightness-max"] = &setProperty<RasterLayer, PropertyValue<float>, &RasterLayer::setRasterBrightnessMax>;
+ result["raster-brightness-max-transition"] = &setTransition<RasterLayer, &RasterLayer::setRasterBrightnessMaxTransition>;
+ result["raster-saturation"] = &setProperty<RasterLayer, PropertyValue<float>, &RasterLayer::setRasterSaturation>;
+ result["raster-saturation-transition"] = &setTransition<RasterLayer, &RasterLayer::setRasterSaturationTransition>;
+ result["raster-contrast"] = &setProperty<RasterLayer, PropertyValue<float>, &RasterLayer::setRasterContrast>;
+ result["raster-contrast-transition"] = &setTransition<RasterLayer, &RasterLayer::setRasterContrastTransition>;
+ result["raster-fade-duration"] = &setProperty<RasterLayer, PropertyValue<float>, &RasterLayer::setRasterFadeDuration>;
+ result["raster-fade-duration-transition"] = &setTransition<RasterLayer, &RasterLayer::setRasterFadeDurationTransition>;
+
+ result["hillshade-illumination-direction"] = &setProperty<HillshadeLayer, PropertyValue<float>, &HillshadeLayer::setHillshadeIlluminationDirection>;
+ result["hillshade-illumination-direction-transition"] = &setTransition<HillshadeLayer, &HillshadeLayer::setHillshadeIlluminationDirectionTransition>;
+ result["hillshade-illumination-anchor"] = &setProperty<HillshadeLayer, PropertyValue<HillshadeIlluminationAnchorType>, &HillshadeLayer::setHillshadeIlluminationAnchor>;
+ result["hillshade-illumination-anchor-transition"] = &setTransition<HillshadeLayer, &HillshadeLayer::setHillshadeIlluminationAnchorTransition>;
+ result["hillshade-exaggeration"] = &setProperty<HillshadeLayer, PropertyValue<float>, &HillshadeLayer::setHillshadeExaggeration>;
+ result["hillshade-exaggeration-transition"] = &setTransition<HillshadeLayer, &HillshadeLayer::setHillshadeExaggerationTransition>;
+ result["hillshade-shadow-color"] = &setProperty<HillshadeLayer, PropertyValue<Color>, &HillshadeLayer::setHillshadeShadowColor>;
+ result["hillshade-shadow-color-transition"] = &setTransition<HillshadeLayer, &HillshadeLayer::setHillshadeShadowColorTransition>;
+ result["hillshade-highlight-color"] = &setProperty<HillshadeLayer, PropertyValue<Color>, &HillshadeLayer::setHillshadeHighlightColor>;
+ result["hillshade-highlight-color-transition"] = &setTransition<HillshadeLayer, &HillshadeLayer::setHillshadeHighlightColorTransition>;
+ result["hillshade-accent-color"] = &setProperty<HillshadeLayer, PropertyValue<Color>, &HillshadeLayer::setHillshadeAccentColor>;
+ result["hillshade-accent-color-transition"] = &setTransition<HillshadeLayer, &HillshadeLayer::setHillshadeAccentColorTransition>;
+
+ result["background-color"] = &setProperty<BackgroundLayer, PropertyValue<Color>, &BackgroundLayer::setBackgroundColor>;
+ result["background-color-transition"] = &setTransition<BackgroundLayer, &BackgroundLayer::setBackgroundColorTransition>;
+ result["background-pattern"] = &setProperty<BackgroundLayer, PropertyValue<std::string>, &BackgroundLayer::setBackgroundPattern>;
+ result["background-pattern-transition"] = &setTransition<BackgroundLayer, &BackgroundLayer::setBackgroundPatternTransition>;
+ result["background-opacity"] = &setProperty<BackgroundLayer, PropertyValue<float>, &BackgroundLayer::setBackgroundOpacity>;
+ result["background-opacity-transition"] = &setTransition<BackgroundLayer, &BackgroundLayer::setBackgroundOpacityTransition>;
+
+ return result;
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/conversion/make_property_setters.hpp.ejs b/src/mbgl/style/conversion/make_property_setters.hpp.ejs
new file mode 100644
index 0000000000..2975cb19f2
--- /dev/null
+++ b/src/mbgl/style/conversion/make_property_setters.hpp.ejs
@@ -0,0 +1,46 @@
+#pragma once
+
+// This file is generated. Edit make_property_setters.hpp.ejs, then run `make style-code`.
+
+#include <mbgl/style/conversion/property_setter.hpp>
+
+<% for (const layer of locals.layers) { -%>
+#include <mbgl/style/layers/<%- layer.type.replace('-', '_') %>_layer.hpp>
+<% } -%>
+
+#include <unordered_map>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+inline auto makeLayoutPropertySetters() {
+ std::unordered_map<std::string, PropertySetter> result;
+
+ result["visibility"] = &setVisibility;
+
+<% for (const layer of locals.layers) { -%>
+<% for (const property of layer.layoutProperties) { -%>
+ result["<%- property.name %>"] = &setProperty<<%- camelize(layer.type) %>Layer, <%- propertyValueType(property) %>, &<%- camelize(layer.type) %>Layer::set<%- camelize(property.name) %>>;
+<% } -%>
+
+<% } -%>
+ return result;
+}
+
+inline auto makePaintPropertySetters() {
+ std::unordered_map<std::string, PropertySetter> result;
+
+<% for (const layer of locals.layers) { -%>
+<% for (const property of layer.paintProperties) { -%>
+ result["<%- property.name %>"] = &setProperty<<%- camelize(layer.type) %>Layer, <%- propertyValueType(property) %>, &<%- camelize(layer.type) %>Layer::set<%- camelize(property.name) %>>;
+ result["<%- property.name %>-transition"] = &setTransition<<%- camelize(layer.type) %>Layer, &<%- camelize(layer.type) %>Layer::set<%- camelize(property.name) %>Transition>;
+<% } -%>
+
+<% } -%>
+ return result;
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/conversion/position.cpp b/src/mbgl/style/conversion/position.cpp
new file mode 100644
index 0000000000..702d250dbf
--- /dev/null
+++ b/src/mbgl/style/conversion/position.cpp
@@ -0,0 +1,22 @@
+#include <mbgl/style/conversion/position.hpp>
+#include <mbgl/style/conversion/constant.hpp>
+
+#include <array>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+optional<Position> Converter<Position>::operator()(const Convertible& value, Error& error) const {
+ optional<std::array<float, 3>> spherical = convert<std::array<float, 3>>(value, error);
+
+ if (!spherical) {
+ return {};
+ }
+
+ return Position(*spherical);
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/conversion/property_setter.hpp b/src/mbgl/style/conversion/property_setter.hpp
index 759c4512cc..e3716a18dc 100644
--- a/include/mbgl/style/conversion/property_setter.hpp
+++ b/src/mbgl/style/conversion/property_setter.hpp
@@ -5,6 +5,7 @@
#include <mbgl/style/conversion/constant.hpp>
#include <mbgl/style/conversion/property_value.hpp>
#include <mbgl/style/conversion/data_driven_property_value.hpp>
+#include <mbgl/style/conversion/heatmap_color_property_value.hpp>
#include <mbgl/style/conversion/transition_options.hpp>
#include <string>
@@ -13,11 +14,10 @@ namespace mbgl {
namespace style {
namespace conversion {
-template <class V>
-using PropertySetter = optional<Error> (*) (Layer&, const V&);
+using PropertySetter = optional<Error> (*) (Layer&, const Convertible&);
-template <class V, class L, class PropertyValue, void (L::*setter)(PropertyValue)>
-optional<Error> setProperty(Layer& layer, const V& value) {
+template <class L, class PropertyValue, void (L::*setter)(PropertyValue)>
+optional<Error> setProperty(Layer& layer, const Convertible& value) {
auto* typedLayer = layer.as<L>();
if (!typedLayer) {
return Error { "layer doesn't support this property" };
@@ -33,8 +33,8 @@ optional<Error> setProperty(Layer& layer, const V& value) {
return {};
}
-template <class V, class L, void (L::*setter)(const TransitionOptions&)>
-optional<Error> setTransition(Layer& layer, const V& value) {
+template <class L, void (L::*setter)(const TransitionOptions&)>
+optional<Error> setTransition(Layer& layer, const Convertible& value) {
auto* typedLayer = layer.as<L>();
if (!typedLayer) {
return Error { "layer doesn't support this property" };
@@ -50,8 +50,7 @@ optional<Error> setTransition(Layer& layer, const V& value) {
return {};
}
-template <class V>
-optional<Error> setVisibility(Layer& layer, const V& value) {
+inline optional<Error> setVisibility(Layer& layer, const Convertible& value) {
if (isUndefined(value)) {
layer.setVisibility(VisibilityType::Visible);
return {};
diff --git a/src/mbgl/style/conversion/source.cpp b/src/mbgl/style/conversion/source.cpp
new file mode 100644
index 0000000000..670f50c041
--- /dev/null
+++ b/src/mbgl/style/conversion/source.cpp
@@ -0,0 +1,200 @@
+#include <mbgl/style/conversion/source.hpp>
+#include <mbgl/style/conversion/coordinate.hpp>
+#include <mbgl/style/conversion/geojson.hpp>
+#include <mbgl/style/conversion/geojson_options.hpp>
+#include <mbgl/style/conversion/tileset.hpp>
+#include <mbgl/style/sources/geojson_source.hpp>
+#include <mbgl/style/sources/raster_source.hpp>
+#include <mbgl/style/sources/raster_dem_source.hpp>
+#include <mbgl/style/sources/vector_source.hpp>
+#include <mbgl/style/sources/image_source.hpp>
+#include <mbgl/util/geo.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+// A tile source can either specify a URL to TileJSON, or inline TileJSON.
+static optional<variant<std::string, Tileset>> convertURLOrTileset(const Convertible& value, Error& error) {
+ auto urlVal = objectMember(value, "url");
+ if (!urlVal) {
+ optional<Tileset> tileset = convert<Tileset>(value, error);
+ if (!tileset) {
+ return {};
+ }
+ return { *tileset };
+ }
+
+ optional<std::string> url = toString(*urlVal);
+ if (!url) {
+ error = { "source url must be a string" };
+ return {};
+ }
+
+ return { *url };
+}
+
+static optional<std::unique_ptr<Source>> convertRasterSource(const std::string& id,
+ const Convertible& value,
+ Error& error) {
+ optional<variant<std::string, Tileset>> urlOrTileset = convertURLOrTileset(value, error);
+ if (!urlOrTileset) {
+ return {};
+ }
+
+ uint16_t tileSize = util::tileSize;
+ auto tileSizeValue = objectMember(value, "tileSize");
+ if (tileSizeValue) {
+ optional<float> size = toNumber(*tileSizeValue);
+ if (!size || *size < 0 || *size > std::numeric_limits<uint16_t>::max()) {
+ error = { "invalid tileSize" };
+ return {};
+ }
+ tileSize = *size;
+ }
+
+ return { std::make_unique<RasterSource>(id, std::move(*urlOrTileset), tileSize) };
+}
+
+static optional<std::unique_ptr<Source>> convertRasterDEMSource(const std::string& id,
+ const Convertible& value,
+ Error& error) {
+ optional<variant<std::string, Tileset>> urlOrTileset = convertURLOrTileset(value, error);
+ if (!urlOrTileset) {
+ return {};
+ }
+
+ uint16_t tileSize = util::tileSize;
+ auto tileSizeValue = objectMember(value, "tileSize");
+ if (tileSizeValue) {
+ optional<float> size = toNumber(*tileSizeValue);
+ if (!size || *size < 0 || *size > std::numeric_limits<uint16_t>::max()) {
+ error = { "invalid tileSize" };
+ return {};
+ }
+ tileSize = *size;
+ }
+
+ return { std::make_unique<RasterDEMSource>(id, std::move(*urlOrTileset), tileSize) };
+}
+
+static optional<std::unique_ptr<Source>> convertVectorSource(const std::string& id,
+ const Convertible& value,
+ Error& error) {
+ optional<variant<std::string, Tileset>> urlOrTileset = convertURLOrTileset(value, error);
+ if (!urlOrTileset) {
+ return {};
+ }
+
+ return { std::make_unique<VectorSource>(id, std::move(*urlOrTileset)) };
+}
+
+static optional<std::unique_ptr<Source>> convertGeoJSONSource(const std::string& id,
+ const Convertible& value,
+ Error& error) {
+ auto dataValue = objectMember(value, "data");
+ if (!dataValue) {
+ error = { "GeoJSON source must have a data value" };
+ return {};
+ }
+
+ optional<GeoJSONOptions> options = convert<GeoJSONOptions>(value, error);
+ if (!options) {
+ return {};
+ }
+
+ auto result = std::make_unique<GeoJSONSource>(id, *options);
+
+ if (isObject(*dataValue)) {
+ optional<GeoJSON> geoJSON = convert<GeoJSON>(*dataValue, error);
+ if (!geoJSON) {
+ return {};
+ }
+ result->setGeoJSON(std::move(*geoJSON));
+ } else if (toString(*dataValue)) {
+ result->setURL(*toString(*dataValue));
+ } else {
+ error = { "GeoJSON data must be a URL or an object" };
+ return {};
+ }
+
+ return { std::move(result) };
+}
+
+static optional<std::unique_ptr<Source>> convertImageSource(const std::string& id,
+ const Convertible& value,
+ Error& error) {
+ auto urlValue = objectMember(value, "url");
+ if (!urlValue) {
+ error = { "Image source must have a url value" };
+ return {};
+ }
+
+ auto urlString = toString(*urlValue);
+ if (!urlString) {
+ error = { "Image url must be a URL string" };
+ return {};
+ }
+
+ auto coordinatesValue = objectMember(value, "coordinates");
+ if (!coordinatesValue) {
+ error = { "Image source must have a coordinates values" };
+ return {};
+ }
+
+ if (!isArray(*coordinatesValue) || arrayLength(*coordinatesValue) != 4) {
+ error = { "Image coordinates must be an array of four longitude latitude pairs" };
+ return {};
+ }
+
+ std::array<LatLng, 4> coordinates;
+ for (std::size_t i=0; i < 4; i++) {
+ auto latLng = conversion::convert<LatLng>(arrayMember(*coordinatesValue,i), error);
+ if (!latLng) {
+ return {};
+ }
+ coordinates[i] = *latLng;
+ }
+ auto result = std::make_unique<ImageSource>(id, coordinates);
+ result->setURL(*urlString);
+
+ return { std::move(result) };
+}
+
+optional<std::unique_ptr<Source>> Converter<std::unique_ptr<Source>>::operator()(const Convertible& value, Error& error, const std::string& id) const {
+ if (!isObject(value)) {
+ error = { "source must be an object" };
+ return {};
+ }
+
+ auto typeValue = objectMember(value, "type");
+ if (!typeValue) {
+ error = { "source must have a type" };
+ return {};
+ }
+
+ optional<std::string> type = toString(*typeValue);
+ if (!type) {
+ error = { "source type must be a string" };
+ return {};
+ }
+ const std::string tname = *type;
+ if (*type == "raster") {
+ return convertRasterSource(id, value, error);
+ } else if (*type == "raster-dem") {
+ return convertRasterDEMSource(id, value, error);
+ } else if (*type == "vector") {
+ return convertVectorSource(id, value, error);
+ } else if (*type == "geojson") {
+ return convertGeoJSONSource(id, value, error);
+ } else if (*type == "image") {
+ return convertImageSource(id, value, error);
+ } else {
+ error = { "invalid source type" };
+ return {};
+ }
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/conversion/tileset.cpp b/src/mbgl/style/conversion/tileset.cpp
index 6e559c0cac..6d89cef944 100644
--- a/src/mbgl/style/conversion/tileset.cpp
+++ b/src/mbgl/style/conversion/tileset.cpp
@@ -6,7 +6,7 @@ namespace style {
namespace conversion {
bool validateLatitude(const double lat) {
- return lat < 90 && lat > -90;
+ return lat <= 90 && lat >= -90;
}
optional<Tileset> Converter<Tileset>::operator()(const Convertible& value, Error& error) const {
@@ -40,6 +40,16 @@ optional<Tileset> Converter<Tileset>::operator()(const Convertible& value, Error
}
}
+ auto encodingValue = objectMember(value, "encoding");
+ if (encodingValue) {
+ optional<std::string> encoding = toString(*encodingValue);
+ if (encoding && *encoding == "terrarium") {
+ result.encoding = Tileset::DEMEncoding::Terrarium;
+ } else if (encoding && *encoding != "mapbox") {
+ error = { "invalid raster-dem encoding type - valid types are 'mapbox' and 'terrarium' " };
+ }
+ }
+
auto minzoomValue = objectMember(value, "minzoom");
if (minzoomValue) {
optional<float> minzoom = toNumber(*minzoomValue);
diff --git a/src/mbgl/style/conversion/transition_options.cpp b/src/mbgl/style/conversion/transition_options.cpp
new file mode 100644
index 0000000000..8a60c5bfd8
--- /dev/null
+++ b/src/mbgl/style/conversion/transition_options.cpp
@@ -0,0 +1,40 @@
+#include <mbgl/style/conversion/transition_options.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+optional<TransitionOptions> Converter<TransitionOptions>::operator()(const Convertible& value, Error& error) const {
+ if (!isObject(value)) {
+ error = { "transition must be an object" };
+ return {};
+ }
+
+ TransitionOptions result;
+
+ auto duration = objectMember(value, "duration");
+ if (duration) {
+ auto number = toNumber(*duration);
+ if (!number) {
+ error = { "duration must be a number" };
+ return {};
+ }
+ result.duration = { std::chrono::milliseconds(int64_t(*number)) };
+ }
+
+ auto delay = objectMember(value, "delay");
+ if (delay) {
+ auto number = toNumber(*delay);
+ if (!number) {
+ error = { "delay must be a number" };
+ return {};
+ }
+ result.delay = { std::chrono::milliseconds(int64_t(*number)) };
+ }
+
+ return result;
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/custom_tile_loader.cpp b/src/mbgl/style/custom_tile_loader.cpp
new file mode 100644
index 0000000000..1c587302b8
--- /dev/null
+++ b/src/mbgl/style/custom_tile_loader.cpp
@@ -0,0 +1,116 @@
+#include <mbgl/style/custom_tile_loader.hpp>
+#include <mbgl/tile/custom_geometry_tile.hpp>
+#include <mbgl/util/tile_range.hpp>
+
+namespace mbgl {
+namespace style {
+
+CustomTileLoader::CustomTileLoader(const TileFunction& fetchTileFn, const TileFunction& cancelTileFn) {
+ fetchTileFunction = fetchTileFn;
+ cancelTileFunction = cancelTileFn;
+}
+
+void CustomTileLoader::fetchTile(const OverscaledTileID& tileID, ActorRef<CustomGeometryTile> tileRef) {
+ auto cachedTileData = dataCache.find(tileID.canonical);
+ if (cachedTileData != dataCache.end()) {
+ tileRef.invoke(&CustomGeometryTile::setTileData, *(cachedTileData->second));
+ }
+ auto tileCallbacks = tileCallbackMap.find(tileID.canonical);
+ if (tileCallbacks == tileCallbackMap.end()) {
+ auto tuple = std::make_tuple(tileID.overscaledZ, tileID.wrap, tileRef);
+ tileCallbackMap.insert({ tileID.canonical, std::vector<OverscaledIDFunctionTuple>(1, tuple) });
+ } else {
+ for (auto iter = tileCallbacks->second.begin(); iter != tileCallbacks->second.end(); iter++) {
+ if (std::get<0>(*iter) == tileID.overscaledZ && std::get<1>(*iter) == tileID.wrap ) {
+ std::get<2>(*iter) = tileRef;
+ return;
+ }
+ }
+ tileCallbacks->second.emplace_back(std::make_tuple(tileID.overscaledZ, tileID.wrap, tileRef));
+ }
+ if (cachedTileData == dataCache.end()) {
+ invokeTileFetch(tileID.canonical);
+ }
+}
+
+void CustomTileLoader::cancelTile(const OverscaledTileID& tileID) {
+ if (tileCallbackMap.find(tileID.canonical) != tileCallbackMap.end()) {
+ invokeTileCancel(tileID.canonical);
+ }
+}
+
+void CustomTileLoader::removeTile(const OverscaledTileID& tileID) {
+ auto tileCallbacks = tileCallbackMap.find(tileID.canonical);
+ if (tileCallbacks == tileCallbackMap.end()) return;
+ for (auto iter = tileCallbacks->second.begin(); iter != tileCallbacks->second.end(); iter++) {
+ if (std::get<0>(*iter) == tileID.overscaledZ && std::get<1>(*iter) == tileID.wrap ) {
+ tileCallbacks->second.erase(iter);
+ invokeTileCancel(tileID.canonical);
+ break;
+ }
+ }
+ if (tileCallbacks->second.size() == 0) {
+ tileCallbackMap.erase(tileCallbacks);
+ dataCache.erase(tileID.canonical);
+ }
+}
+
+void CustomTileLoader::setTileData(const CanonicalTileID& tileID, const GeoJSON& data) {
+
+ auto iter = tileCallbackMap.find(tileID);
+ if (iter == tileCallbackMap.end()) return;
+ auto dataPtr = std::make_unique<mapbox::geojson::geojson>(std::move(data));
+ for (auto tuple : iter->second) {
+ auto actor = std::get<2>(tuple);
+ actor.invoke(&CustomGeometryTile::setTileData, *dataPtr);
+ }
+ dataCache[tileID] = std::move(dataPtr);
+}
+
+void CustomTileLoader::invalidateTile(const CanonicalTileID& tileID) {
+ auto tileCallbacks = tileCallbackMap.find(tileID);
+ if (tileCallbacks == tileCallbackMap.end()) { return; }
+ for (auto iter = tileCallbacks->second.begin(); iter != tileCallbacks->second.end(); iter++) {
+ auto actor = std::get<2>(*iter);
+ actor.invoke(&CustomGeometryTile::invalidateTileData);
+ invokeTileCancel(tileID);
+ }
+ tileCallbackMap.erase(tileCallbacks);
+ dataCache.erase(tileID);
+}
+
+void CustomTileLoader::invalidateRegion(const LatLngBounds& bounds, Range<uint8_t> ) {
+ std::map<uint8_t, util::TileRange> tileRanges;
+
+ for (auto idtuple= tileCallbackMap.begin(); idtuple != tileCallbackMap.end(); idtuple++) {
+ auto zoom = idtuple->first.z;
+ auto tileRange = tileRanges.find(zoom);
+ if(tileRange == tileRanges.end()) {
+ tileRange = tileRanges.emplace(std::make_pair(zoom, util::TileRange::fromLatLngBounds(bounds, zoom))).first;
+ }
+ if (tileRange->second.contains(idtuple->first)) {
+ for (auto iter = idtuple->second.begin(); iter != idtuple->second.end(); iter++) {
+ auto actor = std::get<2>(*iter);
+ actor.invoke(&CustomGeometryTile::invalidateTileData);
+ invokeTileCancel(idtuple->first);
+ dataCache.erase(idtuple->first);
+ }
+ idtuple->second.clear();
+ }
+ }
+}
+
+void CustomTileLoader::invokeTileFetch(const CanonicalTileID& tileID) {
+ if (fetchTileFunction != nullptr) {
+ fetchTileFunction(tileID);
+ }
+}
+
+void CustomTileLoader::invokeTileCancel(const CanonicalTileID& tileID) {
+ if (cancelTileFunction != nullptr) {
+ cancelTileFunction(tileID);
+ }
+}
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/custom_tile_loader.hpp b/src/mbgl/style/custom_tile_loader.hpp
new file mode 100644
index 0000000000..335d8c6143
--- /dev/null
+++ b/src/mbgl/style/custom_tile_loader.hpp
@@ -0,0 +1,45 @@
+#pragma once
+
+#include <mbgl/style/sources/custom_geometry_source.hpp>
+#include <mbgl/tile/tile_id.hpp>
+#include <mbgl/util/geojson.hpp>
+#include <mbgl/actor/actor_ref.hpp>
+
+#include <map>
+
+namespace mbgl {
+
+class CustomGeometryTile;
+
+namespace style {
+
+class CustomTileLoader : private util::noncopyable {
+public:
+
+ using OverscaledIDFunctionTuple = std::tuple<uint8_t, int16_t, ActorRef<CustomGeometryTile>>;
+
+ CustomTileLoader(const TileFunction& fetchTileFn, const TileFunction& cancelTileFn);
+
+ void fetchTile(const OverscaledTileID& tileID, ActorRef<CustomGeometryTile> tileRef);
+ void cancelTile(const OverscaledTileID& tileID);
+
+ void removeTile(const OverscaledTileID& tileID);
+ void setTileData(const CanonicalTileID& tileID, const GeoJSON& data);
+
+ void invalidateTile(const CanonicalTileID&);
+ void invalidateRegion(const LatLngBounds&, Range<uint8_t>);
+
+private:
+ void invokeTileFetch(const CanonicalTileID& tileID);
+ void invokeTileCancel(const CanonicalTileID& tileID);
+
+ TileFunction fetchTileFunction;
+ TileFunction cancelTileFunction;
+ std::unordered_map<CanonicalTileID, std::vector<OverscaledIDFunctionTuple>> tileCallbackMap;
+ // Keep around a cache of tile data to serve back for wrapped and over-zooomed tiles
+ std::map<CanonicalTileID, std::unique_ptr<GeoJSON>> dataCache;
+
+};
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/array_assertion.cpp b/src/mbgl/style/expression/array_assertion.cpp
new file mode 100644
index 0000000000..4049301b0b
--- /dev/null
+++ b/src/mbgl/style/expression/array_assertion.cpp
@@ -0,0 +1,105 @@
+#include <mbgl/style/expression/array_assertion.hpp>
+#include <mbgl/style/expression/check_subtype.hpp>
+#include <mbgl/util/string.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+EvaluationResult ArrayAssertion::evaluate(const EvaluationContext& params) const {
+ auto result = input->evaluate(params);
+ if (!result) {
+ return result.error();
+ }
+ type::Type expected = getType();
+ type::Type actual = typeOf(*result);
+ if (checkSubtype(expected, actual)) {
+ return EvaluationError {
+ "Expected value to be of type " + toString(expected) +
+ ", but found " + toString(actual) + " instead."
+ };
+ }
+ return *result;
+}
+
+void ArrayAssertion::eachChild(const std::function<void(const Expression&)>& visit) const {
+ visit(*input);
+}
+
+using namespace mbgl::style::conversion;
+ParseResult ArrayAssertion::parse(const Convertible& value, ParsingContext& ctx) {
+
+ static std::unordered_map<std::string, type::Type> itemTypes {
+ {"string", type::String},
+ {"number", type::Number},
+ {"boolean", type::Boolean}
+ };
+
+ auto length = arrayLength(value);
+ if (length < 2 || length > 4) {
+ ctx.error("Expected 1, 2, or 3 arguments, but found " + util::toString(length - 1) + " instead.");
+ return ParseResult();
+ }
+
+ optional<type::Type> itemType;
+ optional<std::size_t> N;
+ if (length > 2) {
+ optional<std::string> itemTypeName = toString(arrayMember(value, 1));
+ auto it = itemTypeName ? itemTypes.find(*itemTypeName) : itemTypes.end();
+ if (it == itemTypes.end()) {
+ ctx.error(
+ R"(The item type argument of "array" must be one of string, number, boolean)",
+ 1
+ );
+ return ParseResult();
+ }
+ itemType = it->second;
+ } else {
+ itemType = {type::Value};
+ }
+
+ if (length > 3) {
+ auto n = toNumber(arrayMember(value, 2));
+ if (!n || *n != std::floor(*n)) {
+ ctx.error(
+ R"(The length argument to "array" must be a positive integer literal.)",
+ 2
+ );
+ return ParseResult();
+ }
+ N = optional<std::size_t>(*n);
+ }
+
+ auto input = ctx.parse(arrayMember(value, length - 1), length - 1, {type::Value});
+ if (!input) {
+ return input;
+ }
+
+ return ParseResult(std::make_unique<ArrayAssertion>(
+ type::Array(*itemType, N),
+ std::move(*input)
+ ));
+}
+
+mbgl::Value ArrayAssertion::serialize() const {
+ std::vector<mbgl::Value> serialized;
+ serialized.emplace_back(getOperator());
+
+
+ const auto array = getType().get<type::Array>();
+ if (array.itemType.is<type::StringType>()
+ || array.itemType.is<type::NumberType>()
+ || array.itemType.is<type::BooleanType>()) {
+ serialized.emplace_back(type::toString(array.itemType));
+ if (array.N) {
+ serialized.emplace_back(uint64_t(*array.N));
+ }
+ }
+
+ serialized.emplace_back(input->serialize());
+ return serialized;
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/assertion.cpp b/src/mbgl/style/expression/assertion.cpp
new file mode 100644
index 0000000000..d6f3f1b584
--- /dev/null
+++ b/src/mbgl/style/expression/assertion.cpp
@@ -0,0 +1,87 @@
+#include <mbgl/style/expression/assertion.hpp>
+#include <mbgl/style/expression/check_subtype.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+using namespace mbgl::style::conversion;
+ParseResult Assertion::parse(const Convertible& value, ParsingContext& ctx) {
+ static std::unordered_map<std::string, type::Type> types {
+ {"string", type::String},
+ {"number", type::Number},
+ {"boolean", type::Boolean},
+ {"object", type::Object}
+ };
+
+ std::size_t length = arrayLength(value);
+
+ if (length < 2) {
+ ctx.error("Expected at least one argument.");
+ return ParseResult();
+ }
+
+ auto it = types.find(*toString(arrayMember(value, 0)));
+ assert(it != types.end());
+
+ std::vector<std::unique_ptr<Expression>> parsed;
+ parsed.reserve(length - 1);
+ for (std::size_t i = 1; i < length; i++) {
+ ParseResult input = ctx.parse(arrayMember(value, i), i, {type::Value});
+ if (!input) return ParseResult();
+ parsed.push_back(std::move(*input));
+ }
+
+ return ParseResult(std::make_unique<Assertion>(it->second, std::move(parsed)));
+}
+
+std::string Assertion::getOperator() const {
+ return type::toString(getType());
+}
+
+EvaluationResult Assertion::evaluate(const EvaluationContext& params) const {
+ for (std::size_t i = 0; i < inputs.size(); i++) {
+ EvaluationResult value = inputs[i]->evaluate(params);
+ if (!value) return value;
+ if (!type::checkSubtype(getType(), typeOf(*value))) {
+ return value;
+ } else if (i == inputs.size() - 1) {
+ return EvaluationError {
+ "Expected value to be of type " + toString(getType()) +
+ ", but found " + toString(typeOf(*value)) + " instead."
+ };
+ }
+ }
+
+ assert(false);
+ return EvaluationError { "Unreachable" };
+};
+
+void Assertion::eachChild(const std::function<void(const Expression&)>& visit) const {
+ for(const std::unique_ptr<Expression>& input : inputs) {
+ visit(*input);
+ }
+};
+
+bool Assertion::operator==(const Expression& e) const {
+ if (auto rhs = dynamic_cast<const Assertion*>(&e)) {
+ return getType() == rhs->getType() && Expression::childrenEqual(inputs, rhs->inputs);
+ }
+ return false;
+}
+
+std::vector<optional<Value>> Assertion::possibleOutputs() const {
+ std::vector<optional<Value>> result;
+ for (const auto& input : inputs) {
+ for (auto& output : input->possibleOutputs()) {
+ result.push_back(std::move(output));
+ }
+ }
+ return result;
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
+
+
diff --git a/src/mbgl/style/expression/at.cpp b/src/mbgl/style/expression/at.cpp
new file mode 100644
index 0000000000..e447d33bc7
--- /dev/null
+++ b/src/mbgl/style/expression/at.cpp
@@ -0,0 +1,64 @@
+#include <mbgl/style/expression/at.hpp>
+#include <mbgl/util/string.hpp>
+
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+EvaluationResult At::evaluate(const EvaluationContext& params) const {
+ const EvaluationResult evaluatedIndex = index->evaluate(params);
+ const EvaluationResult evaluatedInput = input->evaluate(params);
+ if (!evaluatedIndex) {
+ return evaluatedIndex.error();
+ }
+ if (!evaluatedInput) {
+ return evaluatedInput.error();
+ }
+
+ const auto i = evaluatedIndex->get<double>();
+ const auto inputArray = evaluatedInput->get<std::vector<Value>>();
+
+ if (i < 0 || i >= inputArray.size()) {
+ return EvaluationError {
+ "Array index out of bounds: " + stringify(i) +
+ " > " + util::toString(inputArray.size()) + "."
+ };
+ }
+ if (i != std::floor(i)) {
+ return EvaluationError {
+ "Array index must be an integer, but found " + stringify(i) + " instead."
+ };
+ }
+ return inputArray[static_cast<std::size_t>(i)];
+}
+
+void At::eachChild(const std::function<void(const Expression&)>& visit) const {
+ visit(*index);
+ visit(*input);
+}
+
+using namespace mbgl::style::conversion;
+ParseResult At::parse(const Convertible& value, ParsingContext& ctx) {
+ assert(isArray(value));
+
+ std::size_t length = arrayLength(value);
+ if (length != 3) {
+ ctx.error("Expected 2 arguments, but found " + util::toString(length - 1) + " instead.");
+ return ParseResult();
+ }
+
+ ParseResult index = ctx.parse(arrayMember(value, 1), 1, {type::Number});
+
+ type::Type inputType = type::Array(ctx.getExpected() ? *ctx.getExpected() : type::Value);
+ ParseResult input = ctx.parse(arrayMember(value, 2), 2, {inputType});
+
+ if (!index || !input) return ParseResult();
+
+ return ParseResult(std::make_unique<At>(std::move(*index), std::move(*input)));
+
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/boolean_operator.cpp b/src/mbgl/style/expression/boolean_operator.cpp
new file mode 100644
index 0000000000..8d277450ba
--- /dev/null
+++ b/src/mbgl/style/expression/boolean_operator.cpp
@@ -0,0 +1,95 @@
+#include <mbgl/style/expression/boolean_operator.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+EvaluationResult Any::evaluate(const EvaluationContext& params) const {
+ for (auto it = inputs.begin(); it != inputs.end(); it++) {
+ const EvaluationResult result = (*it)->evaluate(params);
+ if (!result) return result;
+ if (result->get<bool>()) return EvaluationResult(true);
+ }
+ return EvaluationResult(false);
+}
+
+void Any::eachChild(const std::function<void(const Expression&)>& visit) const {
+ for (const std::unique_ptr<Expression>& input : inputs) {
+ visit(*input);
+ }
+}
+
+bool Any::operator==(const Expression& e) const {
+ if (auto rhs = dynamic_cast<const Any*>(&e)) {
+ return Expression::childrenEqual(inputs, rhs->inputs);
+ }
+ return false;
+}
+
+std::vector<optional<Value>> Any::possibleOutputs() const {
+ return {{ true }, { false }};
+}
+
+
+EvaluationResult All::evaluate(const EvaluationContext& params) const {
+ for (auto it = inputs.begin(); it != inputs.end(); it++) {
+ const EvaluationResult result = (*it)->evaluate(params);
+ if (!result) return result;
+ if (!result->get<bool>()) return EvaluationResult(false);
+ }
+ return EvaluationResult(true);
+}
+
+void All::eachChild(const std::function<void(const Expression&)>& visit) const {
+ for (const std::unique_ptr<Expression>& input : inputs) {
+ visit(*input);
+ }
+}
+
+bool All::operator==(const Expression& e) const {
+ if (auto rhs = dynamic_cast<const All*>(&e)) {
+ return Expression::childrenEqual(inputs, rhs->inputs);
+ }
+ return false;
+}
+
+std::vector<optional<Value>> All::possibleOutputs() const {
+ return {{ true }, { false }};
+}
+
+using namespace mbgl::style::conversion;
+
+template <class T>
+ParseResult parseBooleanOp(const Convertible& value, ParsingContext& ctx) {
+
+ assert(isArray(value));
+ auto length = arrayLength(value);
+
+ std::vector<std::unique_ptr<Expression>> parsedInputs;
+
+ parsedInputs.reserve(length - 1);
+ for (std::size_t i = 1; i < length; i++) {
+ auto parsed = ctx.parse(arrayMember(value, i), i, {type::Boolean});
+ if (!parsed) {
+ return parsed;
+ }
+
+ parsedInputs.push_back(std::move(*parsed));
+ }
+
+ return ParseResult(std::make_unique<T>(std::move(parsedInputs)));
+}
+
+ParseResult Any::parse(const Convertible& value, ParsingContext& ctx) {
+ return parseBooleanOp<Any>(value, ctx);
+}
+
+ParseResult All::parse(const Convertible& value, ParsingContext& ctx) {
+ return parseBooleanOp<All>(value, ctx);
+}
+
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
+
diff --git a/src/mbgl/style/expression/case.cpp b/src/mbgl/style/expression/case.cpp
new file mode 100644
index 0000000000..295e694189
--- /dev/null
+++ b/src/mbgl/style/expression/case.cpp
@@ -0,0 +1,104 @@
+#include <mbgl/style/expression/case.hpp>
+#include <mbgl/util/string.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+EvaluationResult Case::evaluate(const EvaluationContext& params) const {
+ for (const auto& branch : branches) {
+ const EvaluationResult evaluatedTest = branch.first->evaluate(params);
+ if (!evaluatedTest) {
+ return evaluatedTest.error();
+ }
+ if (evaluatedTest->get<bool>()) {
+ return branch.second->evaluate(params);
+ }
+ }
+
+ return otherwise->evaluate(params);
+}
+
+void Case::eachChild(const std::function<void(const Expression&)>& visit) const {
+ for (const Branch& branch : branches) {
+ visit(*branch.first);
+ visit(*branch.second);
+ }
+ visit(*otherwise);
+}
+
+bool Case::operator==(const Expression& e) const {
+ if (auto rhs = dynamic_cast<const Case*>(&e)) {
+ return *otherwise == *(rhs->otherwise) && Expression::childrenEqual(branches, rhs->branches);
+ }
+ return false;
+}
+
+std::vector<optional<Value>> Case::possibleOutputs() const {
+ std::vector<optional<Value>> result;
+ for (const auto& branch : branches) {
+ for (auto& output : branch.second->possibleOutputs()) {
+ result.push_back(std::move(output));
+ }
+ }
+ for (auto& output : otherwise->possibleOutputs()) {
+ result.push_back(std::move(output));
+ }
+ return result;
+}
+
+using namespace mbgl::style::conversion;
+ParseResult Case::parse(const Convertible& value, ParsingContext& ctx) {
+ assert(isArray(value));
+ auto length = arrayLength(value);
+ if (length < 4) {
+ ctx.error("Expected at least 3 arguments, but found only " + util::toString(length - 1) + ".");
+ return ParseResult();
+ }
+
+ // Expect even-length array: ["case", 2 * (n pairs)..., otherwise]
+ if (length % 2 != 0) {
+ ctx.error("Expected an odd number of arguments");
+ return ParseResult();
+ }
+
+ optional<type::Type> outputType;
+ if (ctx.getExpected() && *ctx.getExpected() != type::Value) {
+ outputType = ctx.getExpected();
+ }
+
+ std::vector<Case::Branch> branches;
+ branches.reserve((length - 2) / 2);
+ for (size_t i = 1; i + 1 < length; i += 2) {
+ auto test = ctx.parse(arrayMember(value, i), i, {type::Boolean});
+ if (!test) {
+ return test;
+ }
+
+ auto output = ctx.parse(arrayMember(value, i + 1), i + 1, outputType);
+ if (!output) {
+ return output;
+ }
+
+ if (!outputType) {
+ outputType = (*output)->getType();
+ }
+
+ branches.push_back(std::make_pair(std::move(*test), std::move(*output)));
+ }
+
+ assert(outputType);
+
+ auto otherwise = ctx.parse(arrayMember(value, length - 1), length - 1, outputType);
+ if (!otherwise) {
+ return otherwise;
+ }
+
+ return ParseResult(std::make_unique<Case>(*outputType,
+ std::move(branches),
+ std::move(*otherwise)));
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/check_subtype.cpp b/src/mbgl/style/expression/check_subtype.cpp
new file mode 100644
index 0000000000..04a1643f0c
--- /dev/null
+++ b/src/mbgl/style/expression/check_subtype.cpp
@@ -0,0 +1,60 @@
+#include <string>
+#include <mbgl/style/expression/check_subtype.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+namespace type {
+
+std::string errorMessage(const Type& expected, const Type& t) {
+ return {"Expected " + toString(expected) + " but found " + toString(t) + " instead."};
+}
+
+optional<std::string> checkSubtype(const Type& expected, const Type& t) {
+ if (t.is<ErrorType>()) return {};
+
+ optional<std::string> result = expected.match(
+ [&] (const Array& expectedArray) -> optional<std::string> {
+ if (!t.is<Array>()) { return {errorMessage(expected, t)}; }
+ const auto& actualArray = t.get<Array>();
+ const auto err = checkSubtype(expectedArray.itemType, actualArray.itemType);
+ if (err) return { errorMessage(expected, t) };
+ if (expectedArray.N && expectedArray.N != actualArray.N) return { errorMessage(expected, t) };
+ return {};
+ },
+ [&] (const ValueType&) -> optional<std::string> {
+ if (t.is<ValueType>()) return {};
+
+ const Type members[] = {
+ Null,
+ Boolean,
+ Number,
+ String,
+ Object,
+ Color,
+ Array(Value)
+ };
+
+ for (const auto& member : members) {
+ const auto err = checkSubtype(member, t);
+ if (!err) {
+ return {};
+ }
+ }
+ return { errorMessage(expected, t) };
+ },
+ [&] (const auto&) -> optional<std::string> {
+ if (expected != t) {
+ return { errorMessage(expected, t) };
+ }
+ return {};
+ }
+ );
+
+ return result;
+}
+
+} // namespace type
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/coalesce.cpp b/src/mbgl/style/expression/coalesce.cpp
new file mode 100644
index 0000000000..872a9abbef
--- /dev/null
+++ b/src/mbgl/style/expression/coalesce.cpp
@@ -0,0 +1,84 @@
+#include <mbgl/style/expression/coalesce.hpp>
+#include <mbgl/style/expression/check_subtype.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+EvaluationResult Coalesce::evaluate(const EvaluationContext& params) const {
+ EvaluationResult result = Null;
+ for (const auto& arg : args) {
+ result = arg->evaluate(params);
+ if (!result || *result != Null) break;
+ }
+ return result;
+}
+
+void Coalesce::eachChild(const std::function<void(const Expression&)>& visit) const {
+ for (const std::unique_ptr<Expression>& arg : args) {
+ visit(*arg);
+ }
+}
+
+bool Coalesce::operator==(const Expression& e) const {
+ if (auto rhs = dynamic_cast<const Coalesce*>(&e)) {
+ return Expression::childrenEqual(args, rhs->args);
+ }
+ return false;
+}
+
+std::vector<optional<Value>> Coalesce::possibleOutputs() const {
+ std::vector<optional<Value>> result;
+ for (const auto& arg : args) {
+ for (auto& output : arg->possibleOutputs()) {
+ result.push_back(std::move(output));
+ }
+ }
+ return result;
+}
+
+using namespace mbgl::style::conversion;
+ParseResult Coalesce::parse(const Convertible& value, ParsingContext& ctx) {
+ assert(isArray(value));
+ auto length = arrayLength(value);
+ if (length < 2) {
+ ctx.error("Expected at least one argument.");
+ return ParseResult();
+ }
+
+ optional<type::Type> outputType;
+ optional<type::Type> expectedType = ctx.getExpected();
+ if (expectedType && *expectedType != type::Value) {
+ outputType = expectedType;
+ }
+
+ Coalesce::Args args;
+ args.reserve(length - 1);
+ for (std::size_t i = 1; i < length; i++) {
+ auto parsed = ctx.parse(arrayMember(value, i), i, outputType, ParsingContext::omitTypeAnnotations);
+ if (!parsed) {
+ return parsed;
+ }
+ if (!outputType) {
+ outputType = (*parsed)->getType();
+ }
+ args.push_back(std::move(*parsed));
+ }
+ assert(outputType);
+
+ // Above, we parse arguments without inferred type annotation so that
+ // they don't produce a runtime error for `null` input, which would
+ // preempt the desired null-coalescing behavior.
+ // Thus, if any of our arguments would have needed an annotation, we
+ // need to wrap the enclosing coalesce expression with it instead.
+ bool needsAnnotation = expectedType &&
+ std::any_of(args.begin(), args.end(), [&] (const auto& arg) {
+ return type::checkSubtype(*expectedType, arg->getType());
+ });
+
+ return ParseResult(std::make_unique<Coalesce>(needsAnnotation ? type::Value : *outputType, std::move(args)));
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/coercion.cpp b/src/mbgl/style/expression/coercion.cpp
new file mode 100644
index 0000000000..d9cd3ffdc9
--- /dev/null
+++ b/src/mbgl/style/expression/coercion.cpp
@@ -0,0 +1,161 @@
+#include <mbgl/style/expression/coercion.hpp>
+#include <mbgl/style/expression/check_subtype.hpp>
+#include <mbgl/style/expression/util.hpp>
+#include <mbgl/util/string.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+EvaluationResult toNumber(const Value& v) {
+ optional<double> result = v.match(
+ [](const double f) -> optional<double> { return f; },
+ [](const std::string& s) -> optional<double> {
+ try {
+ return util::stof(s);
+ } catch(std::exception) {
+ return optional<double>();
+ }
+ },
+ [](const auto&) { return optional<double>(); }
+ );
+ if (!result) {
+ return EvaluationError {
+ "Could not convert " + stringify(v) + " to number."
+ };
+ }
+ return *result;
+}
+
+EvaluationResult toColor(const Value& colorValue) {
+ return colorValue.match(
+ [&](const std::string& colorString) -> EvaluationResult {
+ const optional<Color> result = Color::parse(colorString);
+ if (result) {
+ return *result;
+ } else {
+ return EvaluationError{
+ "Could not parse color from value '" + colorString + "'"
+ };
+ }
+ },
+ [&](const std::vector<Value>& components) -> EvaluationResult {
+ std::size_t len = components.size();
+ bool isNumeric = std::all_of(components.begin(), components.end(), [](const Value& item) {
+ return item.template is<double>();
+ });
+ if ((len == 3 || len == 4) && isNumeric) {
+ Result<Color> c = {rgba(
+ components[0].template get<double>(),
+ components[1].template get<double>(),
+ components[2].template get<double>(),
+ len == 4 ? components[3].template get<double>() : 1.0
+ )};
+ if (!c) return c.error();
+ return *c;
+ } else {
+ return EvaluationError{
+ "Invalid rbga value " + stringify(colorValue) + ": expected an array containing either three or four numeric values."
+ };
+ }
+ },
+ [&](const auto&) -> EvaluationResult {
+ return EvaluationError{
+ "Could not parse color from value '" + stringify(colorValue) + "'"
+ };
+ }
+ );
+}
+
+Coercion::Coercion(type::Type type_, std::vector<std::unique_ptr<Expression>> inputs_) :
+ Expression(std::move(type_)),
+ inputs(std::move(inputs_))
+{
+ type::Type t = getType();
+ if (t.is<type::NumberType>()) {
+ coerceSingleValue = toNumber;
+ } else if (t.is<type::ColorType>()) {
+ coerceSingleValue = toColor;
+ } else {
+ assert(false);
+ }
+}
+
+std::string Coercion::getOperator() const {
+ return getType().match(
+ [](const type::NumberType&) { return "to-number"; },
+ [](const type::ColorType&) { return "to-color"; },
+ [](const auto&) { assert(false); return ""; });
+}
+
+using namespace mbgl::style::conversion;
+ParseResult Coercion::parse(const Convertible& value, ParsingContext& ctx) {
+ static std::unordered_map<std::string, type::Type> types {
+ {"to-number", type::Number},
+ {"to-color", type::Color}
+ };
+
+ std::size_t length = arrayLength(value);
+
+ if (length < 2) {
+ ctx.error("Expected at least one argument.");
+ return ParseResult();
+ }
+
+ auto it = types.find(*toString(arrayMember(value, 0)));
+ assert(it != types.end());
+
+ std::vector<std::unique_ptr<Expression>> parsed;
+ parsed.reserve(length - 1);
+ for (std::size_t i = 1; i < length; i++) {
+ ParseResult input = ctx.parse(arrayMember(value, i), i, {type::Value});
+ if (!input) return ParseResult();
+ parsed.push_back(std::move(*input));
+ }
+
+ return ParseResult(std::make_unique<Coercion>(it->second, std::move(parsed)));
+}
+
+EvaluationResult Coercion::evaluate(const EvaluationContext& params) const {
+ for (std::size_t i = 0; i < inputs.size(); i++) {
+ EvaluationResult value = inputs[i]->evaluate(params);
+ if (!value) return value;
+ EvaluationResult coerced = coerceSingleValue(*value);
+ if (coerced || i == inputs.size() - 1) {
+ return coerced;
+ }
+ }
+
+ assert(false);
+ return EvaluationError { "Unreachable" };
+};
+
+void Coercion::eachChild(const std::function<void(const Expression&)>& visit) const {
+ for(const std::unique_ptr<Expression>& input : inputs) {
+ visit(*input);
+ }
+};
+
+bool Coercion::operator==(const Expression& e) const {
+ if (auto rhs = dynamic_cast<const Coercion*>(&e)) {
+ return getType() == rhs->getType() && Expression::childrenEqual(inputs, rhs->inputs);
+ }
+ return false;
+}
+
+std::vector<optional<Value>> Coercion::possibleOutputs() const {
+ std::vector<optional<Value>> result;
+ for (const auto& input : inputs) {
+ for (auto& output : input->possibleOutputs()) {
+ result.push_back(std::move(output));
+ }
+ }
+ return result;
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
+
+
+
diff --git a/src/mbgl/style/expression/compound_expression.cpp b/src/mbgl/style/expression/compound_expression.cpp
new file mode 100644
index 0000000000..86d968c521
--- /dev/null
+++ b/src/mbgl/style/expression/compound_expression.cpp
@@ -0,0 +1,555 @@
+#include <mbgl/style/expression/compound_expression.hpp>
+#include <mbgl/style/expression/check_subtype.hpp>
+#include <mbgl/style/expression/util.hpp>
+#include <mbgl/tile/geometry_tile_data.hpp>
+#include <mbgl/math/log2.hpp>
+#include <mbgl/util/ignore.hpp>
+#include <mbgl/util/string.hpp>
+#include <mbgl/util/platform.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+namespace detail {
+
+/*
+ The Signature<Fn> structs are wrappers around an "evaluate()" function whose
+ purpose is to extract the necessary Type data from the evaluate function's
+ type. There are three key (partial) specializations:
+
+ Signature<R (Params...)>:
+ Wraps a simple evaluate function (const T0&, const T1&, ...) -> Result<U>
+
+ Signature<R (const Varargs<T>&)>:
+ Wraps an evaluate function that takes an arbitrary number of arguments (via
+ a Varargs<T>, which is just an alias for std::vector).
+
+ Signature<R (const EvaluationContext&, Params...)>:
+ Wraps an evaluate function that needs to access the expression evaluation
+ parameters in addition to its subexpressions, i.e.,
+ (const EvaluationParams& const T0&, const T1&, ...) -> Result<U>. Needed
+ for expressions like ["zoom"], ["get", key], etc.
+
+ In each of the above evaluate signatures, T0, T1, etc. are the types of
+ the successfully evaluated subexpressions.
+*/
+template <class, class Enable = void>
+struct Signature;
+
+// Simple evaluate function (const T0&, const T1&, ...) -> Result<U>
+template <class R, class... Params>
+struct Signature<R (Params...)> : SignatureBase {
+ using Args = std::array<std::unique_ptr<Expression>, sizeof...(Params)>;
+
+ Signature(R (*evaluate_)(Params...), std::string name_) :
+ SignatureBase(
+ valueTypeToExpressionType<std::decay_t<typename R::Value>>(),
+ std::vector<type::Type> {valueTypeToExpressionType<std::decay_t<Params>>()...},
+ std::move(name_)
+ ),
+ evaluate(evaluate_) {}
+
+ EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const {
+ return applyImpl(evaluationParameters, args, std::index_sequence_for<Params...>{});
+ }
+
+ std::unique_ptr<Expression> makeExpression(std::vector<std::unique_ptr<Expression>> args) const override {
+ typename Signature::Args argsArray;
+ std::copy_n(std::make_move_iterator(args.begin()), sizeof...(Params), argsArray.begin());
+ return std::make_unique<CompoundExpression<Signature>>(name, *this, std::move(argsArray));
+ }
+
+ R (*evaluate)(Params...);
+private:
+ template <std::size_t ...I>
+ EvaluationResult applyImpl(const EvaluationContext& evaluationParameters, const Args& args, std::index_sequence<I...>) const {
+ const std::array<EvaluationResult, sizeof...(I)> evaluated = {{std::get<I>(args)->evaluate(evaluationParameters)...}};
+ for (const auto& arg : evaluated) {
+ if(!arg) return arg.error();
+ }
+ const R value = evaluate(*fromExpressionValue<std::decay_t<Params>>(*(evaluated[I]))...);
+ if (!value) return value.error();
+ return *value;
+ }
+};
+
+// Varargs evaluate function (const Varargs<T>&) -> Result<U>
+template <class R, typename T>
+struct Signature<R (const Varargs<T>&)> : SignatureBase {
+ using Args = std::vector<std::unique_ptr<Expression>>;
+
+ Signature(R (*evaluate_)(const Varargs<T>&), std::string name_) :
+ SignatureBase(
+ valueTypeToExpressionType<std::decay_t<typename R::Value>>(),
+ VarargsType { valueTypeToExpressionType<T>() },
+ std::move(name_)
+ ),
+ evaluate(evaluate_)
+ {}
+
+ std::unique_ptr<Expression> makeExpression(std::vector<std::unique_ptr<Expression>> args) const override {
+ return std::make_unique<CompoundExpression<Signature>>(name, *this, std::move(args));
+ };
+
+ EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const {
+ Varargs<T> evaluated;
+ evaluated.reserve(args.size());
+ for (const auto& arg : args) {
+ const EvaluationResult evaluatedArg = arg->evaluate(evaluationParameters);
+ if(!evaluatedArg) return evaluatedArg.error();
+ evaluated.push_back(*fromExpressionValue<std::decay_t<T>>(*evaluatedArg));
+ }
+ const R value = evaluate(evaluated);
+ if (!value) return value.error();
+ return *value;
+ }
+
+ R (*evaluate)(const Varargs<T>&);
+};
+
+// Evaluate function needing parameter access,
+// (const EvaluationParams&, const T0&, const T1&, ...) -> Result<U>
+template <class R, class... Params>
+struct Signature<R (const EvaluationContext&, Params...)> : SignatureBase {
+ using Args = std::array<std::unique_ptr<Expression>, sizeof...(Params)>;
+
+ Signature(R (*evaluate_)(const EvaluationContext&, Params...), std::string name_) :
+ SignatureBase(
+ valueTypeToExpressionType<std::decay_t<typename R::Value>>(),
+ std::vector<type::Type> {valueTypeToExpressionType<std::decay_t<Params>>()...},
+ std::move(name_)
+ ),
+ evaluate(evaluate_)
+ {}
+
+ std::unique_ptr<Expression> makeExpression(std::vector<std::unique_ptr<Expression>> args) const override {
+ typename Signature::Args argsArray;
+ std::copy_n(std::make_move_iterator(args.begin()), sizeof...(Params), argsArray.begin());
+ return std::make_unique<CompoundExpression<Signature>>(name, *this, std::move(argsArray));
+ }
+
+ EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const {
+ return applyImpl(evaluationParameters, args, std::index_sequence_for<Params...>{});
+ }
+
+private:
+ template <std::size_t ...I>
+ EvaluationResult applyImpl(const EvaluationContext& evaluationParameters, const Args& args, std::index_sequence<I...>) const {
+ const std::array<EvaluationResult, sizeof...(I)> evaluated = {{std::get<I>(args)->evaluate(evaluationParameters)...}};
+ for (const auto& arg : evaluated) {
+ if(!arg) return arg.error();
+ }
+ // TODO: assert correct runtime type of each arg value
+ const R value = evaluate(evaluationParameters, *fromExpressionValue<std::decay_t<Params>>(*(evaluated[I]))...);
+ if (!value) return value.error();
+ return *value;
+ }
+
+ R (*evaluate)(const EvaluationContext&, Params...);
+};
+
+// Machinery to pull out function types from class methods, lambdas, etc.
+template <class R, class... Params>
+struct Signature<R (*)(Params...)>
+ : Signature<R (Params...)>
+{ using Signature<R (Params...)>::Signature; };
+
+template <class T, class R, class... Params>
+struct Signature<R (T::*)(Params...) const>
+ : Signature<R (Params...)>
+{ using Signature<R (Params...)>::Signature; };
+
+template <class T, class R, class... Params>
+struct Signature<R (T::*)(Params...)>
+ : Signature<R (Params...)>
+{ using Signature<R (Params...)>::Signature; };
+
+template <class Lambda>
+struct Signature<Lambda, std::enable_if_t<std::is_class<Lambda>::value>>
+ : Signature<decltype(&Lambda::operator())>
+{ using Signature<decltype(&Lambda::operator())>::Signature; };
+
+} // namespace detail
+
+using Definition = CompoundExpressionRegistry::Definition;
+
+template <typename Fn>
+static std::unique_ptr<detail::SignatureBase> makeSignature(Fn evaluateFunction, std::string name) {
+ return std::make_unique<detail::Signature<Fn>>(evaluateFunction, std::move(name));
+}
+
+std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initializeDefinitions() {
+ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> definitions;
+ auto define = [&](std::string name, auto fn) {
+ definitions[name].push_back(makeSignature(fn, name));
+ };
+
+ define("e", []() -> Result<double> { return 2.718281828459045; });
+ define("pi", []() -> Result<double> { return 3.141592653589793; });
+ define("ln2", []() -> Result<double> { return 0.6931471805599453; });
+
+ define("typeof", [](const Value& v) -> Result<std::string> { return toString(typeOf(v)); });
+
+ define("to-string", [](const Value& value) -> Result<std::string> {
+ return value.match(
+ [](const Color& c) -> Result<std::string> { return c.stringify(); }, // avoid quoting
+ [](const std::string& s) -> Result<std::string> { return s; }, // avoid quoting
+ [](const auto& v) -> Result<std::string> { return stringify(v); }
+ );
+ });
+
+ define("to-boolean", [](const Value& v) -> Result<bool> {
+ return v.match(
+ [&] (double f) { return (bool)f; },
+ [&] (const std::string& s) { return s.length() > 0; },
+ [&] (bool b) { return b; },
+ [&] (const NullValue&) { return false; },
+ [&] (const auto&) { return true; }
+ );
+ });
+ define("to-rgba", [](const Color& color) -> Result<std::array<double, 4>> {
+ return std::array<double, 4> {{
+ 255 * color.r / color.a,
+ 255 * color.g / color.a,
+ 255 * color.b / color.a,
+ color.a
+ }};
+ });
+
+ define("rgba", rgba);
+ define("rgb", [](double r, double g, double b) { return rgba(r, g, b, 1.0f); });
+
+ define("zoom", [](const EvaluationContext& params) -> Result<double> {
+ if (!params.zoom) {
+ return EvaluationError {
+ "The 'zoom' expression is unavailable in the current evaluation context."
+ };
+ }
+ return *(params.zoom);
+ });
+
+ define("heatmap-density", [](const EvaluationContext& params) -> Result<double> {
+ if (!params.heatmapDensity) {
+ return EvaluationError {
+ "The 'heatmap-density' expression is unavailable in the current evaluation context."
+ };
+ }
+ return *(params.heatmapDensity);
+ });
+
+ define("has", [](const EvaluationContext& params, const std::string& key) -> Result<bool> {
+ if (!params.feature) {
+ return EvaluationError {
+ "Feature data is unavailable in the current evaluation context."
+ };
+ }
+
+ return params.feature->getValue(key) ? true : false;
+ });
+ define("has", [](const std::string& key, const std::unordered_map<std::string, Value>& object) -> Result<bool> {
+ return object.find(key) != object.end();
+ });
+
+ define("get", [](const EvaluationContext& params, const std::string& key) -> Result<Value> {
+ if (!params.feature) {
+ return EvaluationError {
+ "Feature data is unavailable in the current evaluation context."
+ };
+ }
+
+ auto propertyValue = params.feature->getValue(key);
+ if (!propertyValue) {
+ return Null;
+ }
+ return Value(toExpressionValue(*propertyValue));
+ });
+ define("get", [](const std::string& key, const std::unordered_map<std::string, Value>& object) -> Result<Value> {
+ if (object.find(key) == object.end()) {
+ return Null;
+ }
+ return object.at(key);
+ });
+
+ define("length", [](const std::vector<Value>& arr) -> Result<double> {
+ return arr.size();
+ });
+ define("length", [] (const std::string s) -> Result<double> {
+ return s.size();
+ });
+
+ define("properties", [](const EvaluationContext& params) -> Result<std::unordered_map<std::string, Value>> {
+ if (!params.feature) {
+ return EvaluationError {
+ "Feature data is unavailable in the current evaluation context."
+ };
+ }
+ std::unordered_map<std::string, Value> result;
+ const PropertyMap properties = params.feature->getProperties();
+ for (const auto& entry : properties) {
+ result[entry.first] = toExpressionValue(entry.second);
+ }
+ return result;
+ });
+
+ define("geometry-type", [](const EvaluationContext& params) -> Result<std::string> {
+ if (!params.feature) {
+ return EvaluationError {
+ "Feature data is unavailable in the current evaluation context."
+ };
+ }
+
+ auto type = params.feature->getType();
+ if (type == FeatureType::Point) {
+ return "Point";
+ } else if (type == FeatureType::LineString) {
+ return "LineString";
+ } else if (type == FeatureType::Polygon) {
+ return "Polygon";
+ } else {
+ return "Unknown";
+ }
+ });
+
+ define("id", [](const EvaluationContext& params) -> Result<Value> {
+ if (!params.feature) {
+ return EvaluationError {
+ "Feature data is unavailable in the current evaluation context."
+ };
+ }
+
+ auto id = params.feature->getID();
+ if (!id) {
+ return Null;
+ }
+ return id->match(
+ [](const auto& idValue) {
+ return toExpressionValue(mbgl::Value(idValue));
+ }
+ );
+ });
+
+ define("+", [](const Varargs<double>& args) -> Result<double> {
+ double sum = 0.0f;
+ for (auto arg : args) {
+ sum += arg;
+ }
+ return sum;
+ });
+ define("-", [](double a, double b) -> Result<double> { return a - b; });
+ define("-", [](double a) -> Result<double> { return -a; });
+ define("*", [](const Varargs<double>& args) -> Result<double> {
+ double prod = 1.0f;
+ for (auto arg : args) {
+ prod *= arg;
+ }
+ return prod;
+ });
+ define("/", [](double a, double b) -> Result<double> { return a / b; });
+ define("%", [](double a, double b) -> Result<double> { return fmod(a, b); });
+ define("^", [](double a, double b) -> Result<double> { return pow(a, b); });
+ define("sqrt", [](double x) -> Result<double> { return sqrt(x); });
+ define("log10", [](double x) -> Result<double> { return log10(x); });
+ define("ln", [](double x) -> Result<double> { return log(x); });
+ define("log2", [](double x) -> Result<double> { return util::log2(x); });
+ define("sin", [](double x) -> Result<double> { return sin(x); });
+ define("cos", [](double x) -> Result<double> { return cos(x); });
+ define("tan", [](double x) -> Result<double> { return tan(x); });
+ define("asin", [](double x) -> Result<double> { return asin(x); });
+ define("acos", [](double x) -> Result<double> { return acos(x); });
+ define("atan", [](double x) -> Result<double> { return atan(x); });
+
+ define("min", [](const Varargs<double>& args) -> Result<double> {
+ double result = std::numeric_limits<double>::infinity();
+ for (double arg : args) {
+ result = fmin(arg, result);
+ }
+ return result;
+ });
+ define("max", [](const Varargs<double>& args) -> Result<double> {
+ double result = -std::numeric_limits<double>::infinity();
+ for (double arg : args) {
+ result = fmax(arg, result);
+ }
+ return result;
+ });
+
+ define(">", [](double lhs, double rhs) -> Result<bool> { return lhs > rhs; });
+ define(">", [](const std::string& lhs, const std::string& rhs) -> Result<bool> { return lhs > rhs; });
+ define(">=", [](double lhs, double rhs) -> Result<bool> { return lhs >= rhs; });
+ define(">=",[](const std::string& lhs, const std::string& rhs) -> Result<bool> { return lhs >= rhs; });
+ define("<", [](double lhs, double rhs) -> Result<bool> { return lhs < rhs; });
+ define("<", [](const std::string& lhs, const std::string& rhs) -> Result<bool> { return lhs < rhs; });
+ define("<=", [](double lhs, double rhs) -> Result<bool> { return lhs <= rhs; });
+ define("<=", [](const std::string& lhs, const std::string& rhs) -> Result<bool> { return lhs <= rhs; });
+
+ define("!", [](bool e) -> Result<bool> { return !e; });
+
+ define("upcase", [](const std::string& input) -> Result<std::string> {
+ return platform::uppercase(input);
+ });
+ define("downcase", [](const std::string& input) -> Result<std::string> {
+ return platform::lowercase(input);
+ });
+ define("concat", [](const Varargs<std::string>& args) -> Result<std::string> {
+ std::string s;
+ for (const std::string& arg : args) {
+ s += arg;
+ }
+ return s;
+ });
+ define("error", [](const std::string& input) -> Result<type::ErrorType> {
+ return EvaluationError { input };
+ });
+
+ return definitions;
+}
+
+std::unordered_map<std::string, Definition> CompoundExpressionRegistry::definitions = initializeDefinitions();
+
+using namespace mbgl::style::conversion;
+ParseResult parseCompoundExpression(const std::string name, const Convertible& value, ParsingContext& ctx) {
+ assert(isArray(value) && arrayLength(value) > 0);
+
+ auto it = CompoundExpressionRegistry::definitions.find(name);
+ if (it == CompoundExpressionRegistry::definitions.end()) {
+ ctx.error(
+ R"(Unknown expression ")" + name + R"(". If you wanted a literal array, use ["literal", [...]].)",
+ 0
+ );
+ return ParseResult();
+ }
+ const CompoundExpressionRegistry::Definition& definition = it->second;
+
+ auto length = arrayLength(value);
+
+ // Check if we have a single signature with the correct number of
+ // parameters. If so, then use that signature's parameter types for parsing
+ // (and inferring the types of) the arguments.
+ optional<std::size_t> singleMatchingSignature;
+ for (std::size_t j = 0; j < definition.size(); j++) {
+ const std::unique_ptr<detail::SignatureBase>& signature = definition[j];
+ if (
+ signature->params.is<VarargsType>() ||
+ signature->params.get<std::vector<type::Type>>().size() == length - 1
+ ) {
+ if (singleMatchingSignature) {
+ singleMatchingSignature = {};
+ } else {
+ singleMatchingSignature = j;
+ }
+ }
+ }
+
+ // parse subexpressions first
+ std::vector<std::unique_ptr<Expression>> args;
+ args.reserve(length - 1);
+ for (std::size_t i = 1; i < length; i++) {
+ optional<type::Type> expected;
+
+ if (singleMatchingSignature) {
+ expected = definition[*singleMatchingSignature]->params.match(
+ [](const VarargsType& varargs) { return varargs.type; },
+ [&](const std::vector<type::Type>& params_) { return params_[i - 1]; }
+ );
+ }
+
+ auto parsed = ctx.parse(arrayMember(value, i), i, expected);
+ if (!parsed) {
+ return parsed;
+ }
+ args.push_back(std::move(*parsed));
+ }
+ return createCompoundExpression(definition, std::move(args), ctx);
+}
+
+
+ParseResult createCompoundExpression(const std::string& name,
+ std::vector<std::unique_ptr<Expression>> args,
+ ParsingContext& ctx)
+{
+ return createCompoundExpression(CompoundExpressionRegistry::definitions.at(name), std::move(args), ctx);
+}
+
+
+ParseResult createCompoundExpression(const Definition& definition,
+ std::vector<std::unique_ptr<Expression>> args,
+ ParsingContext& ctx)
+{
+ ParsingContext signatureContext(ctx.getKey());
+
+ for (const std::unique_ptr<detail::SignatureBase>& signature : definition) {
+ signatureContext.clearErrors();
+
+ if (signature->params.is<std::vector<type::Type>>()) {
+ const std::vector<type::Type>& params = signature->params.get<std::vector<type::Type>>();
+ if (params.size() != args.size()) {
+ signatureContext.error(
+ "Expected " + util::toString(params.size()) +
+ " arguments, but found " + util::toString(args.size()) + " instead."
+ );
+ continue;
+ }
+
+ for (std::size_t i = 0; i < args.size(); i++) {
+ const std::unique_ptr<Expression>& arg = args[i];
+ optional<std::string> err = type::checkSubtype(params.at(i), arg->getType());
+ if (err) {
+ signatureContext.error(*err, i + 1);
+ }
+ }
+ } else if (signature->params.is<VarargsType>()) {
+ const type::Type& paramType = signature->params.get<VarargsType>().type;
+ for (std::size_t i = 0; i < args.size(); i++) {
+ const std::unique_ptr<Expression>& arg = args[i];
+ optional<std::string> err = type::checkSubtype(paramType, arg->getType());
+ if (err) {
+ signatureContext.error(*err, i + 1);
+ }
+ }
+ }
+
+ if (signatureContext.getErrors().size() == 0) {
+ return ParseResult(signature->makeExpression(std::move(args)));
+ }
+ }
+
+ if (definition.size() == 1) {
+ ctx.appendErrors(std::move(signatureContext));
+ } else {
+ std::string signatures;
+ for (const auto& signature : definition) {
+ signatures += (signatures.size() > 0 ? " | " : "");
+ signature->params.match(
+ [&](const VarargsType& varargs) {
+ signatures += "(" + toString(varargs.type) + ")";
+ },
+ [&](const std::vector<type::Type>& params) {
+ signatures += "(";
+ bool first = true;
+ for (const type::Type& param : params) {
+ if (!first) signatures += ", ";
+ signatures += toString(param);
+ first = false;
+ }
+ signatures += ")";
+ }
+ );
+
+ }
+ std::string actualTypes;
+ for (const auto& arg : args) {
+ if (actualTypes.size() > 0) {
+ actualTypes += ", ";
+ }
+ actualTypes += toString(arg->getType());
+ }
+ ctx.error("Expected arguments of type " + signatures + ", but found (" + actualTypes + ") instead.");
+ }
+
+ return ParseResult();
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/equals.cpp b/src/mbgl/style/expression/equals.cpp
new file mode 100644
index 0000000000..6d963cc1d8
--- /dev/null
+++ b/src/mbgl/style/expression/equals.cpp
@@ -0,0 +1,87 @@
+#include <mbgl/style/expression/equals.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+Equals::Equals(std::unique_ptr<Expression> lhs_, std::unique_ptr<Expression> rhs_, bool negate_)
+ : Expression(type::Boolean),
+ lhs(std::move(lhs_)),
+ rhs(std::move(rhs_)),
+ negate(negate_) {
+}
+
+EvaluationResult Equals::evaluate(const EvaluationContext& params) const {
+ EvaluationResult lhsResult = lhs->evaluate(params);
+ if (!lhsResult) return lhsResult;
+
+ EvaluationResult rhsResult = rhs->evaluate(params);
+ if (!rhsResult) return lhsResult;
+
+ bool result = *lhsResult == *rhsResult;
+ if (negate) {
+ result = !result;
+ }
+ return result;
+}
+
+void Equals::eachChild(const std::function<void(const Expression&)>& visit) const {
+ visit(*lhs);
+ visit(*rhs);
+}
+
+bool Equals::operator==(const Expression& e) const {
+ if (auto eq = dynamic_cast<const Equals*>(&e)) {
+ return eq->negate == negate && *eq->lhs == *lhs && *eq->rhs == *rhs;
+ }
+ return false;
+}
+
+std::vector<optional<Value>> Equals::possibleOutputs() const {
+ return {{ true }, { false }};
+}
+
+static bool isComparableType(const type::Type& type) {
+ return type == type::String ||
+ type == type::Number ||
+ type == type::Boolean ||
+ type == type::Null;
+}
+
+using namespace mbgl::style::conversion;
+ParseResult Equals::parse(const Convertible& value, ParsingContext& ctx) {
+ std::size_t length = arrayLength(value);
+
+ if (length != 3) {
+ ctx.error("Expected two arguments.");
+ return ParseResult();
+ }
+
+ bool negate = toString(arrayMember(value, 0)) == std::string("!=");
+
+ ParseResult lhs = ctx.parse(arrayMember(value, 1), 1, {type::Value});
+ if (!lhs) return ParseResult();
+
+ ParseResult rhs = ctx.parse(arrayMember(value, 2), 2, {type::Value});
+ if (!rhs) return ParseResult();
+
+ type::Type lhsType = (*lhs)->getType();
+ type::Type rhsType = (*rhs)->getType();
+
+ if (!isComparableType(lhsType) && !isComparableType(rhsType)) {
+ ctx.error("Expected at least one argument to be a string, number, boolean, or null, but found (" +
+ toString(lhsType) + ", " + toString(rhsType) + ") instead.");
+ return ParseResult();
+ }
+
+ if (lhsType != rhsType && lhsType != type::Value && rhsType != type::Value) {
+ ctx.error("Cannot compare " + toString(lhsType) + " and " + toString(rhsType) + ".");
+ return ParseResult();
+ }
+
+ return ParseResult(std::make_unique<Equals>(std::move(*lhs), std::move(*rhs), negate));
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/find_zoom_curve.cpp b/src/mbgl/style/expression/find_zoom_curve.cpp
new file mode 100644
index 0000000000..5d39e0791e
--- /dev/null
+++ b/src/mbgl/style/expression/find_zoom_curve.cpp
@@ -0,0 +1,76 @@
+#include <mbgl/style/expression/find_zoom_curve.hpp>
+#include <mbgl/style/expression/compound_expression.hpp>
+#include <mbgl/style/expression/let.hpp>
+#include <mbgl/style/expression/coalesce.hpp>
+
+#include <mbgl/util/variant.hpp>
+#include <mbgl/util/optional.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+optional<variant<const InterpolateBase*, const Step*, ParsingError>> findZoomCurve(const expression::Expression* e) {
+ optional<variant<const InterpolateBase*, const Step*, ParsingError>> result;
+
+ if (auto let = dynamic_cast<const Let*>(e)) {
+ result = findZoomCurve(let->getResult());
+ } else if (auto coalesce = dynamic_cast<const Coalesce*>(e)) {
+ std::size_t length = coalesce->getLength();
+ for (std::size_t i = 0; i < length; i++) {
+ result = findZoomCurve(coalesce->getChild(i));
+ if (result) {
+ break;
+ }
+ }
+ } else if (auto curve = dynamic_cast<const InterpolateBase*>(e)) {
+ auto z = dynamic_cast<CompoundExpressionBase*>(curve->getInput().get());
+ if (z && z->getName() == "zoom") {
+ result = {curve};
+ }
+ } else if (auto step = dynamic_cast<const Step*>(e)) {
+ auto z = dynamic_cast<CompoundExpressionBase*>(step->getInput().get());
+ if (z && z->getName() == "zoom") {
+ result = {step};
+ }
+ }
+
+ if (result && result->is<ParsingError>()) {
+ return result;
+ }
+
+ e->eachChild([&](const Expression& child) {
+ optional<variant<const InterpolateBase*, const Step*, ParsingError>> childResult(findZoomCurve(&child));
+ if (childResult) {
+ if (childResult->is<ParsingError>()) {
+ result = childResult;
+ } else if (!result && childResult) {
+ result = {ParsingError {
+ R"("zoom" expression may only be used as input to a top-level "step" or "interpolate" expression.)", ""
+ }};
+ } else if (result && childResult && result != childResult) {
+ result = {ParsingError {
+ R"(Only one zoom-based "step" or "interpolate" subexpression may be used in an expression.)", ""
+ }};
+ }
+ }
+ });
+
+ return result;
+}
+
+variant<const InterpolateBase*, const Step*> findZoomCurveChecked(const expression::Expression* e) {
+ return findZoomCurve(e)->match(
+ [](const ParsingError&) -> variant<const InterpolateBase*, const Step*> {
+ assert(false);
+ return {};
+ },
+ [](auto zoomCurve) -> variant<const InterpolateBase*, const Step*> {
+ return {std::move(zoomCurve)};
+ }
+ );
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/get_covering_stops.cpp b/src/mbgl/style/expression/get_covering_stops.cpp
new file mode 100644
index 0000000000..c9f87d93ac
--- /dev/null
+++ b/src/mbgl/style/expression/get_covering_stops.cpp
@@ -0,0 +1,26 @@
+#include <mbgl/style/expression/get_covering_stops.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+Range<float> getCoveringStops(const std::map<double, std::unique_ptr<Expression>>& stops,
+ const double lower, const double upper) {
+ assert(!stops.empty());
+ auto minIt = stops.lower_bound(lower);
+ auto maxIt = stops.lower_bound(upper);
+
+ // lower_bound yields first element >= lowerZoom, but we want the *last*
+ // element <= lowerZoom, so if we found a stop > lowerZoom, back up by one.
+ if (minIt != stops.begin() && minIt != stops.end() && minIt->first > lower) {
+ minIt--;
+ }
+ return Range<float> {
+ static_cast<float>(minIt == stops.end() ? stops.rbegin()->first : minIt->first),
+ static_cast<float>(maxIt == stops.end() ? stops.rbegin()->first : maxIt->first)
+ };
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/interpolate.cpp b/src/mbgl/style/expression/interpolate.cpp
new file mode 100644
index 0000000000..30b2cba81b
--- /dev/null
+++ b/src/mbgl/style/expression/interpolate.cpp
@@ -0,0 +1,245 @@
+#include <mbgl/style/expression/interpolate.hpp>
+#include <mbgl/util/string.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+using Interpolator = variant<ExponentialInterpolator,
+ CubicBezierInterpolator>;
+
+using namespace mbgl::style::conversion;
+
+ParseResult parseInterpolate(const Convertible& value, ParsingContext& ctx) {
+ assert(isArray(value));
+
+ auto length = arrayLength(value);
+
+ if (length < 2) {
+ ctx.error("Expected an interpolation type expression.");
+ return ParseResult();
+ }
+
+ const Convertible& interp = arrayMember(value, 1);
+ if (!isArray(interp) || arrayLength(interp) == 0) {
+ ctx.error("Expected an interpolation type expression.");
+ return ParseResult();
+ }
+
+ optional<Interpolator> interpolator;
+
+ const optional<std::string> interpName = toString(arrayMember(interp, 0));
+ if (interpName && *interpName == "linear") {
+ interpolator = {ExponentialInterpolator(1.0)};
+ } else if (interpName && *interpName == "exponential") {
+ optional<double> base;
+ if (arrayLength(interp) == 2) {
+ base = toDouble(arrayMember(interp, 1));
+ }
+ if (!base) {
+ ctx.error("Exponential interpolation requires a numeric base.", 1, 1);
+ return ParseResult();
+ }
+ interpolator = {ExponentialInterpolator(*base)};
+ } else if (interpName && *interpName == "cubic-bezier") {
+ optional<double> x1;
+ optional<double> y1;
+ optional<double> x2;
+ optional<double> y2;
+ if (arrayLength(interp) == 5) {
+ x1 = toDouble(arrayMember(interp, 1));
+ y1 = toDouble(arrayMember(interp, 2));
+ x2 = toDouble(arrayMember(interp, 3));
+ y2 = toDouble(arrayMember(interp, 4));
+ }
+ if (
+ !x1 || !y1 || !x2 || !y2 ||
+ *x1 < 0 || *x1 > 1 ||
+ *y1 < 0 || *y1 > 1 ||
+ *x2 < 0 || *x2 > 1 ||
+ *y2 < 0 || *y2 > 1
+ ) {
+ ctx.error("Cubic bezier interpolation requires four numeric arguments with values between 0 and 1.", 1);
+ return ParseResult();
+
+ }
+ interpolator = {CubicBezierInterpolator(*x1, *y1, *x2, *y2)};
+ }
+
+ if (!interpolator) {
+ ctx.error("Unknown interpolation type " + (interpName ? *interpName : ""), 1, 0);
+ return ParseResult();
+ }
+
+ std::size_t minArgs = 4;
+ if (length - 1 < minArgs) {
+ ctx.error("Expected at least 4 arguments, but found only " + util::toString(length - 1) + ".");
+ return ParseResult();
+ }
+
+ // [interpolation, interp_type, input, 2 * (n pairs)...]
+ if ((length - 1) % 2 != 0) {
+ ctx.error("Expected an even number of arguments.");
+ return ParseResult();
+ }
+
+ ParseResult input = ctx.parse(arrayMember(value, 2), 2, {type::Number});
+ if (!input) {
+ return input;
+ }
+
+ std::map<double, std::unique_ptr<Expression>> stops;
+ optional<type::Type> outputType;
+ if (ctx.getExpected() && *ctx.getExpected() != type::Value) {
+ outputType = ctx.getExpected();
+ }
+
+ double previous = - std::numeric_limits<double>::infinity();
+
+ for (std::size_t i = 3; i + 1 < length; i += 2) {
+ const optional<mbgl::Value> labelValue = toValue(arrayMember(value, i));
+ optional<double> label;
+ optional<std::string> labelError;
+ if (labelValue) {
+ labelValue->match(
+ [&](uint64_t n) {
+ if (n > std::numeric_limits<double>::max()) {
+ label = {std::numeric_limits<double>::infinity()};
+ } else {
+ label = {static_cast<double>(n)};
+ }
+ },
+ [&](int64_t n) {
+ if (n > std::numeric_limits<double>::max()) {
+ label = {std::numeric_limits<double>::infinity()};
+ } else {
+ label = {static_cast<double>(n)};
+ }
+ },
+ [&](double n) {
+ if (n > std::numeric_limits<double>::max()) {
+ label = {std::numeric_limits<double>::infinity()};
+ } else {
+ label = {static_cast<double>(n)};
+ }
+ },
+ [&](const auto&) {}
+ );
+ }
+ if (!label) {
+ ctx.error(labelError ? *labelError :
+ R"(Input/output pairs for "interpolate" expressions must be defined using literal numeric values (not computed expressions) for the input values.)",
+ i);
+ return ParseResult();
+ }
+
+ if (*label <= previous) {
+ ctx.error(
+ R"(Input/output pairs for "interpolate" expressions must be arranged with input values in strictly ascending order.)",
+ i
+ );
+ return ParseResult();
+ }
+ previous = *label;
+
+ auto output = ctx.parse(arrayMember(value, i + 1), i + 1, outputType);
+ if (!output) {
+ return ParseResult();
+ }
+ if (!outputType) {
+ outputType = (*output)->getType();
+ }
+
+ stops.emplace(*label, std::move(*output));
+ }
+
+ assert(outputType);
+
+ if (
+ *outputType != type::Number &&
+ *outputType != type::Color &&
+ !(
+ outputType->is<type::Array>() &&
+ outputType->get<type::Array>().itemType == type::Number &&
+ outputType->get<type::Array>().N
+ )
+ )
+ {
+ ctx.error("Type " + toString(*outputType) + " is not interpolatable.");
+ return ParseResult();
+ }
+
+ return outputType->match(
+ [&](const type::NumberType&) -> ParseResult {
+ return interpolator->match([&](const auto& interpolator_) {
+ return ParseResult(std::make_unique<Interpolate<double>>(
+ *outputType, interpolator_, std::move(*input), std::move(stops)
+ ));
+ });
+ },
+ [&](const type::ColorType&) -> ParseResult {
+ return interpolator->match([&](const auto& interpolator_) {
+ return ParseResult(std::make_unique<Interpolate<Color>>(
+ *outputType, interpolator_, std::move(*input), std::move(stops)
+ ));
+ });
+ },
+ [&](const type::Array& arrayType) -> ParseResult {
+ return interpolator->match(
+ [&](const auto& continuousInterpolator) {
+ if (arrayType.itemType != type::Number || !arrayType.N) {
+ assert(false); // interpolability already checked above.
+ return ParseResult();
+ }
+ return ParseResult(std::make_unique<Interpolate<std::vector<Value>>>(
+ *outputType, continuousInterpolator, std::move(*input), std::move(stops)
+ ));
+ }
+ );
+ },
+ [&](const auto&) {
+ // unreachable: Null, Boolean, String, Object, Value output types
+ // are not interpolatable, and interpolability was already checked above
+ assert(false);
+ return ParseResult();
+ }
+ );
+}
+
+std::vector<optional<Value>> InterpolateBase::possibleOutputs() const {
+ std::vector<optional<Value>> result;
+ for (const auto& stop : stops) {
+ for (auto& output : stop.second->possibleOutputs()) {
+ result.push_back(std::move(output));
+ }
+ }
+ return result;
+}
+
+template <typename T>
+mbgl::Value Interpolate<T>::serialize() const {
+ std::vector<mbgl::Value> serialized;
+ serialized.emplace_back(getOperator());
+
+ interpolator.match(
+ [&](const ExponentialInterpolator& exponential) {
+ serialized.emplace_back(std::vector<mbgl::Value>{{ std::string("exponential"), exponential.base }});
+ },
+ [&](const CubicBezierInterpolator& cubicBezier) {
+ static const std::string cubicBezierTag("cubic-bezier");
+ auto p1 = cubicBezier.ub.getP1();
+ auto p2 = cubicBezier.ub.getP2();
+ serialized.emplace_back(std::vector<mbgl::Value>{{ cubicBezierTag, p1.first, p1.second, p2.first, p2.second }});
+ }
+ );
+ serialized.emplace_back(input->serialize());
+ for (auto& entry : stops) {
+ serialized.emplace_back(entry.first);
+ serialized.emplace_back(entry.second->serialize());
+ };
+ return serialized;
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/is_constant.cpp b/src/mbgl/style/expression/is_constant.cpp
new file mode 100644
index 0000000000..0ebb37faa9
--- /dev/null
+++ b/src/mbgl/style/expression/is_constant.cpp
@@ -0,0 +1,40 @@
+#include <mbgl/style/expression/is_constant.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+bool isFeatureConstant(const Expression& expression) {
+ if (auto e = dynamic_cast<const CompoundExpressionBase*>(&expression)) {
+ const std::string name = e->getName();
+ optional<std::size_t> parameterCount = e->getParameterCount();
+ if (name == "get" && parameterCount && *parameterCount == 1) {
+ return false;
+ } else if (name == "has" && parameterCount && *parameterCount == 1) {
+ return false;
+ } else if (
+ name == "properties" ||
+ name == "geometry-type" ||
+ name == "id"
+ ) {
+ return false;
+ }
+ }
+
+ bool featureConstant = true;
+ expression.eachChild([&](const Expression& e) {
+ if (featureConstant && !isFeatureConstant(e)) {
+ featureConstant = false;
+ }
+ });
+ return featureConstant;
+}
+
+bool isZoomConstant(const Expression& e) {
+ return isGlobalPropertyConstant(e, std::array<std::string, 1>{{"zoom"}});
+}
+
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/is_expression.cpp b/src/mbgl/style/expression/is_expression.cpp
new file mode 100644
index 0000000000..77212f6a1e
--- /dev/null
+++ b/src/mbgl/style/expression/is_expression.cpp
@@ -0,0 +1,29 @@
+#include <mbgl/style/expression/is_expression.hpp>
+#include <mbgl/style/expression/compound_expression.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+
+#include <mbgl/style/conversion.hpp>
+
+#include <unordered_set>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+using namespace mbgl::style::conversion;
+
+bool isExpression(const Convertible& value) {
+ const ExpressionRegistry& registry = getExpressionRegistry();
+
+ if (!isArray(value) || arrayLength(value) == 0) return false;
+ optional<std::string> name = toString(arrayMember(value, 0));
+ if (!name) return false;
+
+ return (registry.find(*name) != registry.end()) ||
+ (CompoundExpressionRegistry::definitions.find(*name) != CompoundExpressionRegistry::definitions.end());
+}
+
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/let.cpp b/src/mbgl/style/expression/let.cpp
new file mode 100644
index 0000000000..242a995b0b
--- /dev/null
+++ b/src/mbgl/style/expression/let.cpp
@@ -0,0 +1,115 @@
+#include <mbgl/style/expression/let.hpp>
+#include <mbgl/style/conversion/get_json_type.hpp>
+#include <mbgl/util/string.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+EvaluationResult Let::evaluate(const EvaluationContext& params) const {
+ return result->evaluate(params);
+}
+
+void Let::eachChild(const std::function<void(const Expression&)>& visit) const {
+ for (auto it = bindings.begin(); it != bindings.end(); it++) {
+ visit(*it->second);
+ }
+ visit(*result);
+}
+
+std::vector<optional<Value>> Let::possibleOutputs() const {
+ return result->possibleOutputs();
+}
+
+using namespace mbgl::style::conversion;
+
+ParseResult Let::parse(const Convertible& value, ParsingContext& ctx) {
+ assert(isArray(value));
+
+ std::size_t length = arrayLength(value);
+
+ if (length < 4) {
+ ctx.error("Expected at least 3 arguments, but found " + util::toString(length - 1) + " instead.");
+ return ParseResult();
+ }
+
+ std::map<std::string, std::shared_ptr<Expression>> bindings_;
+ for(std::size_t i = 1; i < length - 1; i += 2) {
+ optional<std::string> name = toString(arrayMember(value, i));
+ if (!name) {
+ ctx.error("Expected string, but found " + getJSONType(arrayMember(value, i)) + " instead.", i);
+ return ParseResult();
+ }
+
+ bool isValidName = std::all_of(name->begin(), name->end(), [](unsigned char c) {
+ return ::isalnum(c) || c == '_';
+ });
+ if (!isValidName) {
+ ctx.error("Variable names must contain only alphanumeric characters or '_'.", 1);
+ return ParseResult();
+ }
+
+ ParseResult bindingValue = ctx.parse(arrayMember(value, i + 1), i + 1);
+ if (!bindingValue) {
+ return ParseResult();
+ }
+
+ bindings_.emplace(*name, std::move(*bindingValue));
+ }
+
+ ParseResult result_ = ctx.parse(arrayMember(value, length - 1), length - 1, ctx.getExpected(), bindings_);
+ if (!result_) {
+ return ParseResult();
+ }
+
+ return ParseResult(std::make_unique<Let>(std::move(bindings_), std::move(*result_)));
+}
+
+mbgl::Value Let::serialize() const {
+ std::vector<mbgl::Value> serialized;
+ serialized.emplace_back(getOperator());
+ for (auto entry : bindings) {
+ serialized.emplace_back(entry.first);
+ serialized.emplace_back(entry.second->serialize());
+ }
+ serialized.emplace_back(result->serialize());
+ return serialized;
+}
+
+EvaluationResult Var::evaluate(const EvaluationContext& params) const {
+ return value->evaluate(params);
+}
+
+void Var::eachChild(const std::function<void(const Expression&)>&) const {}
+
+std::vector<optional<Value>> Var::possibleOutputs() const {
+ return { nullopt };
+}
+
+ParseResult Var::parse(const Convertible& value_, ParsingContext& ctx) {
+ assert(isArray(value_));
+
+ if (arrayLength(value_) != 2 || !toString(arrayMember(value_, 1))) {
+ ctx.error("'var' expression requires exactly one string literal argument.");
+ return ParseResult();
+ }
+
+ std::string name_ = *toString(arrayMember(value_, 1));
+
+ optional<std::shared_ptr<Expression>> bindingValue = ctx.getBinding(name_);
+ if (!bindingValue) {
+ ctx.error(R"(Unknown variable ")" + name_ + R"(". Make sure ")" +
+ name_ + R"(" has been bound in an enclosing "let" expression before using it.)", 1);
+ return ParseResult();
+ }
+
+ return ParseResult(std::make_unique<Var>(name_, std::move(*bindingValue)));
+}
+
+mbgl::Value Var::serialize() const {
+ return std::vector<mbgl::Value>{{ getOperator(), name }};
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/literal.cpp b/src/mbgl/style/expression/literal.cpp
new file mode 100644
index 0000000000..8a63980dba
--- /dev/null
+++ b/src/mbgl/style/expression/literal.cpp
@@ -0,0 +1,116 @@
+#include <mbgl/style/expression/literal.hpp>
+#include <mbgl/util/string.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+template <typename T>
+optional<Value> checkNumber(T n) {
+ if (n > std::numeric_limits<double>::max()) {
+ return {std::numeric_limits<double>::infinity()};
+ } else {
+ return {static_cast<double>(n)};
+ }
+}
+
+using namespace mbgl::style::conversion;
+optional<Value> parseValue(const Convertible& value, ParsingContext& ctx) {
+ if (isUndefined(value)) return {Null};
+ if (isObject(value)) {
+ std::unordered_map<std::string, Value> result;
+ bool error = false;
+ eachMember(value, [&] (const std::string& k, const mbgl::style::conversion::Convertible& v) -> optional<conversion::Error> {
+ if (!error) {
+ optional<Value> memberValue = parseValue(v, ctx);
+ if (memberValue) {
+ result.emplace(k, *memberValue);
+ } else {
+ error = true;
+ }
+ }
+ return {};
+ });
+ return error ? optional<Value>() : optional<Value>(result);
+ }
+
+ if (isArray(value)) {
+ std::vector<Value> result;
+ const auto length = arrayLength(value);
+ for(std::size_t i = 0; i < length; i++) {
+ optional<Value> item = parseValue(arrayMember(value, i), ctx);
+ if (item) {
+ result.emplace_back(*item);
+ } else {
+ return optional<Value>();
+ }
+ }
+ return optional<Value>(result);
+ }
+
+ optional<mbgl::Value> v = toValue(value);
+ // since value represents a JSON value, if it's not undefined, object, or
+ // array, it must be convertible to mbgl::Value
+ assert(v);
+
+ return v->match(
+ [&](uint64_t n) { return checkNumber(n); },
+ [&](int64_t n) { return checkNumber(n); },
+ [&](double n) { return checkNumber(n); },
+ [&](const auto&) {
+ return optional<Value>(toExpressionValue(*v));
+ }
+ );
+}
+
+ParseResult Literal::parse(const Convertible& value, ParsingContext& ctx) {
+ if (isObject(value)) {
+ ctx.error(R"(Bare objects invalid. Use ["literal", {...}] instead.)");
+ return ParseResult();
+ } else if (isArray(value)) {
+ // object or array value, quoted with ["literal", value]
+ if (arrayLength(value) != 2) {
+ ctx.error("'literal' expression requires exactly one argument, but found " + util::toString(arrayLength(value) - 1) + " instead.");
+ return ParseResult();
+ }
+ const optional<Value> parsedValue = parseValue(arrayMember(value, 1), ctx);
+ if (!parsedValue) {
+ return ParseResult();
+ }
+
+ // special case: infer the item type if possible for zero-length arrays
+ if (
+ ctx.getExpected() &&
+ ctx.getExpected()->template is<type::Array>() &&
+ parsedValue->template is<std::vector<Value>>()
+ ) {
+ auto type = typeOf(*parsedValue).template get<type::Array>();
+ auto expected = ctx.getExpected()->template get<type::Array>();
+ if (
+ type.N && (*type.N == 0) &&
+ (!expected.N || (*expected.N == 0))
+ ) {
+ return ParseResult(std::make_unique<Literal>(expected, parsedValue->template get<std::vector<Value>>()));
+ }
+ }
+
+ return ParseResult(std::make_unique<Literal>(*parsedValue));
+ } else {
+ // bare primitive value (string, number, boolean, null)
+ const optional<Value> parsedValue = parseValue(value, ctx);
+ return ParseResult(std::make_unique<Literal>(*parsedValue));
+ }
+}
+
+mbgl::Value Literal::serialize() const {
+ if (getType().is<type::Array>() || getType().is<type::ObjectType>()) {
+ return std::vector<mbgl::Value>{{ getOperator(), *fromExpressionValue<mbgl::Value>(value) }};
+ } else {
+ return *fromExpressionValue<mbgl::Value>(value);
+ }
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
+
diff --git a/src/mbgl/style/expression/match.cpp b/src/mbgl/style/expression/match.cpp
new file mode 100644
index 0000000000..3d41f0bdd3
--- /dev/null
+++ b/src/mbgl/style/expression/match.cpp
@@ -0,0 +1,313 @@
+#include <mbgl/style/expression/match.hpp>
+#include <mbgl/style/expression/check_subtype.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/util/string.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+template <typename T>
+void Match<T>::eachChild(const std::function<void(const Expression&)>& visit) const {
+ visit(*input);
+ for (const std::pair<T, std::shared_ptr<Expression>>& branch : branches) {
+ visit(*branch.second);
+ }
+ visit(*otherwise);
+}
+
+template <typename T>
+bool Match<T>::operator==(const Expression& e) const {
+ if (auto rhs = dynamic_cast<const Match*>(&e)) {
+ return (*input == *(rhs->input) &&
+ *otherwise == *(rhs->otherwise) &&
+ Expression::childrenEqual(branches, rhs->branches));
+ }
+ return false;
+}
+
+template <typename T>
+std::vector<optional<Value>> Match<T>::possibleOutputs() const {
+ std::vector<optional<Value>> result;
+ for (const auto& branch : branches) {
+ for (auto& output : branch.second->possibleOutputs()) {
+ result.push_back(std::move(output));
+ }
+ }
+ for (auto& output : otherwise->possibleOutputs()) {
+ result.push_back(std::move(output));
+ }
+ return result;
+}
+
+template <typename T>
+mbgl::Value Match<T>::serialize() const {
+ std::vector<mbgl::Value> serialized;
+ serialized.emplace_back(getOperator());
+ serialized.emplace_back(input->serialize());
+
+ // Sort so serialization has an arbitrary defined order, even though branch order doesn't affect evaluation
+ std::map<T, std::shared_ptr<Expression>> sortedBranches(branches.begin(), branches.end());
+
+ // Group branches by unique match expression to support condensed serializations
+ // of the form [case1, case2, ...] -> matchExpression
+ std::map<Expression*, size_t> outputLookup;
+ std::vector<std::pair<Expression*, std::vector<mbgl::Value>>> groupedByOutput;
+ for (auto& entry : sortedBranches) {
+ auto outputIndex = outputLookup.find(entry.second.get());
+ if (outputIndex == outputLookup.end()) {
+ // First time seeing this output, add it to the end of the grouped list
+ outputLookup[entry.second.get()] = groupedByOutput.size();
+ groupedByOutput.emplace_back(entry.second.get(), std::vector<mbgl::Value>{{entry.first}});
+ } else {
+ // We've seen this expression before, add the label to that output's group
+ groupedByOutput[outputIndex->second].second.emplace_back(entry.first);
+ }
+ };
+
+ for (auto& entry : groupedByOutput) {
+ entry.second.size() == 1
+ ? serialized.emplace_back(entry.second[0]) // Only a single label matches this output expression
+ : serialized.emplace_back(entry.second); // Array of literal labels pointing to this output expression
+ serialized.emplace_back(entry.first->serialize()); // The output expression itself
+ }
+
+ serialized.emplace_back(otherwise->serialize());
+ return serialized;
+}
+
+
+template<> EvaluationResult Match<std::string>::evaluate(const EvaluationContext& params) const {
+ const EvaluationResult inputValue = input->evaluate(params);
+ if (!inputValue) {
+ return inputValue.error();
+ }
+
+ auto it = branches.find(inputValue->get<std::string>());
+ if (it != branches.end()) {
+ return (*it).second->evaluate(params);
+ }
+
+ return otherwise->evaluate(params);
+}
+
+template<> EvaluationResult Match<int64_t>::evaluate(const EvaluationContext& params) const {
+ const EvaluationResult inputValue = input->evaluate(params);
+ if (!inputValue) {
+ return inputValue.error();
+ }
+
+ const auto numeric = inputValue->get<double>();
+ int64_t rounded = std::floor(numeric);
+ if (numeric == rounded) {
+ auto it = branches.find(rounded);
+ if (it != branches.end()) {
+ return (*it).second->evaluate(params);
+ }
+ }
+
+ return otherwise->evaluate(params);
+}
+
+template class Match<int64_t>;
+template class Match<std::string>;
+
+using InputType = variant<int64_t, std::string>;
+
+using namespace mbgl::style::conversion;
+optional<InputType> parseInputValue(const Convertible& input, ParsingContext& parentContext, std::size_t index, optional<type::Type>& inputType) {
+ using namespace mbgl::style::conversion;
+ optional<InputType> result;
+ optional<type::Type> type;
+
+ auto value = toValue(input);
+
+ if (value) {
+ value->match(
+ [&] (uint64_t n) {
+ if (!Value::isSafeInteger(n)) {
+ parentContext.error("Branch labels must be integers no larger than " + util::toString(Value::maxSafeInteger()) + ".", index);
+ } else {
+ type = {type::Number};
+ result = {static_cast<int64_t>(n)};
+ }
+ },
+ [&] (int64_t n) {
+ if (!Value::isSafeInteger(n)) {
+ parentContext.error("Branch labels must be integers no larger than " + util::toString(Value::maxSafeInteger()) + ".", index);
+ } else {
+ type = {type::Number};
+ result = {n};
+ }
+ },
+ [&] (double n) {
+ if (!Value::isSafeInteger(n)) {
+ parentContext.error("Branch labels must be integers no larger than " + util::toString(Value::maxSafeInteger()) + ".", index);
+ } else if (n != std::floor(n)) {
+ parentContext.error("Numeric branch labels must be integer values.", index);
+ } else {
+ type = {type::Number};
+ result = {static_cast<int64_t>(n)};
+ }
+ },
+ [&] (const std::string& s) {
+ type = {type::String};
+ result = {s};
+ },
+ [&] (const auto&) {
+ parentContext.error("Branch labels must be numbers or strings.", index);
+ }
+ );
+ } else {
+ parentContext.error("Branch labels must be numbers or strings.", index);
+ }
+
+ if (!type) {
+ return result;
+ }
+
+ if (!inputType) {
+ inputType = type;
+ } else {
+ optional<std::string> err = type::checkSubtype(*inputType, *type);
+ if (err) {
+ parentContext.error(*err, index);
+ return optional<InputType>();
+ }
+ }
+
+ return result;
+}
+
+template <typename T>
+static ParseResult create(type::Type outputType,
+ std::unique_ptr<Expression>input,
+ std::vector<std::pair<std::vector<InputType>,
+ std::unique_ptr<Expression>>> branches,
+ std::unique_ptr<Expression> otherwise,
+ ParsingContext& ctx) {
+ typename Match<T>::Branches typedBranches;
+
+ std::size_t index = 2;
+
+ typedBranches.reserve(branches.size());
+ for (std::pair<std::vector<InputType>,
+ std::unique_ptr<Expression>>& pair : branches) {
+ std::shared_ptr<Expression> result = std::move(pair.second);
+ for (const InputType& label : pair.first) {
+ const auto& typedLabel = label.template get<T>();
+ if (typedBranches.find(typedLabel) != typedBranches.end()) {
+ ctx.error("Branch labels must be unique.", index);
+ return ParseResult();
+ }
+ typedBranches.emplace(typedLabel, result);
+ }
+
+ index += 2;
+ }
+ return ParseResult(std::make_unique<Match<T>>(
+ outputType,
+ std::move(input),
+ std::move(typedBranches),
+ std::move(otherwise)
+ ));
+}
+
+ParseResult parseMatch(const Convertible& value, ParsingContext& ctx) {
+ assert(isArray(value));
+ auto length = arrayLength(value);
+ if (length < 5) {
+ ctx.error(
+ "Expected at least 4 arguments, but found only " + util::toString(length - 1) + "."
+ );
+ return ParseResult();
+ }
+
+ // Expect odd-length array: ["match", input, 2 * (n pairs)..., otherwise]
+ if (length % 2 != 1) {
+ ctx.error("Expected an even number of arguments.");
+ return ParseResult();
+ }
+
+ optional<type::Type> inputType;
+ optional<type::Type> outputType;
+ if (ctx.getExpected() && *ctx.getExpected() != type::Value) {
+ outputType = ctx.getExpected();
+ }
+
+ std::vector<std::pair<std::vector<InputType>,
+ std::unique_ptr<Expression>>> branches;
+
+ branches.reserve((length - 3) / 2);
+ for (size_t i = 2; i + 1 < length; i += 2) {
+ const auto& label = arrayMember(value, i);
+
+ std::vector<InputType> labels;
+ // Match pair inputs are provided as either a literal value or a
+ // raw JSON array of string / number / boolean values.
+ if (isArray(label)) {
+ auto groupLength = arrayLength(label);
+ if (groupLength == 0) {
+ ctx.error("Expected at least one branch label.", i);
+ return ParseResult();
+ }
+
+ labels.reserve(groupLength);
+ for (size_t j = 0; j < groupLength; j++) {
+ const optional<InputType> inputValue = parseInputValue(arrayMember(label, j), ctx, i, inputType);
+ if (!inputValue) {
+ return ParseResult();
+ }
+ labels.push_back(*inputValue);
+ }
+ } else {
+ const optional<InputType> inputValue = parseInputValue(label, ctx, i, inputType);
+ if (!inputValue) {
+ return ParseResult();
+ }
+ labels.push_back(*inputValue);
+ }
+
+ ParseResult output = ctx.parse(arrayMember(value, i + 1), i + 1, outputType);
+ if (!output) {
+ return ParseResult();
+ }
+
+ if (!outputType) {
+ outputType = (*output)->getType();
+ }
+
+ branches.push_back(std::make_pair(std::move(labels), std::move(*output)));
+ }
+
+ auto input = ctx.parse(arrayMember(value, 1), 1, inputType);
+ if (!input) {
+ return ParseResult();
+ }
+
+ auto otherwise = ctx.parse(arrayMember(value, length - 1), length - 1, outputType);
+ if (!otherwise) {
+ return ParseResult();
+ }
+
+ assert(inputType && outputType);
+
+ return inputType->match(
+ [&](const type::NumberType&) {
+ return create<int64_t>(*outputType, std::move(*input), std::move(branches), std::move(*otherwise), ctx);
+ },
+ [&](const type::StringType&) {
+ return create<std::string>(*outputType, std::move(*input), std::move(branches), std::move(*otherwise), ctx);
+ },
+ [&](const auto&) {
+ // unreachable: inputType is set by parseInputValue(), which only
+ // accepts string and (integer) numeric values.
+ assert(false);
+ return ParseResult();
+ }
+ );
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/parsing_context.cpp b/src/mbgl/style/expression/parsing_context.cpp
new file mode 100644
index 0000000000..0215982209
--- /dev/null
+++ b/src/mbgl/style/expression/parsing_context.cpp
@@ -0,0 +1,220 @@
+
+#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/style/expression/check_subtype.hpp>
+#include <mbgl/style/expression/is_constant.hpp>
+#include <mbgl/style/expression/type.hpp>
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/expression/at.hpp>
+#include <mbgl/style/expression/array_assertion.hpp>
+#include <mbgl/style/expression/assertion.hpp>
+#include <mbgl/style/expression/boolean_operator.hpp>
+#include <mbgl/style/expression/case.hpp>
+#include <mbgl/style/expression/coalesce.hpp>
+#include <mbgl/style/expression/coercion.hpp>
+#include <mbgl/style/expression/compound_expression.hpp>
+#include <mbgl/style/expression/equals.hpp>
+#include <mbgl/style/expression/interpolate.hpp>
+#include <mbgl/style/expression/let.hpp>
+#include <mbgl/style/expression/literal.hpp>
+#include <mbgl/style/expression/match.hpp>
+#include <mbgl/style/expression/step.hpp>
+
+#include <mbgl/style/expression/find_zoom_curve.hpp>
+
+#include <mbgl/style/conversion/get_json_type.hpp>
+
+#include <mbgl/util/string.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+bool isConstant(const Expression& expression) {
+ if (dynamic_cast<const Var*>(&expression)) {
+ return false;
+ }
+
+ if (auto compound = dynamic_cast<const CompoundExpressionBase*>(&expression)) {
+ if (compound->getName() == "error") {
+ return false;
+ }
+ }
+
+ bool literalArgs = true;
+ expression.eachChild([&](const Expression& child) {
+ if (!dynamic_cast<const Literal*>(&child)) {
+ literalArgs = false;
+ }
+ });
+ if (!literalArgs) {
+ return false;
+ }
+
+ return isFeatureConstant(expression) &&
+ isGlobalPropertyConstant(expression, std::array<std::string, 2>{{"zoom", "heatmap-density"}});
+}
+
+using namespace mbgl::style::conversion;
+
+ParseResult ParsingContext::parse(const Convertible& value,
+ std::size_t index_,
+ optional<type::Type> expected_,
+ TypeAnnotationOption typeAnnotationOption) {
+ ParsingContext child(key + "[" + util::toString(index_) + "]",
+ errors,
+ std::move(expected_),
+ scope);
+ return child.parse(value, typeAnnotationOption);
+}
+
+ParseResult ParsingContext::parse(const Convertible& value, std::size_t index_, optional<type::Type> expected_,
+ const std::map<std::string, std::shared_ptr<Expression>>& bindings) {
+ ParsingContext child(key + "[" + util::toString(index_) + "]",
+ errors,
+ std::move(expected_),
+ std::make_shared<detail::Scope>(bindings, scope));
+ return child.parse(value);
+}
+
+const ExpressionRegistry& getExpressionRegistry() {
+ static ExpressionRegistry registry {{
+ {"==", Equals::parse},
+ {"!=", Equals::parse},
+ {"all", All::parse},
+ {"any", Any::parse},
+ {"array", ArrayAssertion::parse},
+ {"at", At::parse},
+ {"boolean", Assertion::parse},
+ {"case", Case::parse},
+ {"coalesce", Coalesce::parse},
+ {"interpolate", parseInterpolate},
+ {"let", Let::parse},
+ {"literal", Literal::parse},
+ {"match", parseMatch},
+ {"number", Assertion::parse},
+ {"object", Assertion::parse},
+ {"step", Step::parse},
+ {"string", Assertion::parse},
+ {"to-color", Coercion::parse},
+ {"to-number", Coercion::parse},
+ {"var", Var::parse}
+ }};
+ return registry;
+}
+
+ParseResult ParsingContext::parse(const Convertible& value, TypeAnnotationOption typeAnnotationOption) {
+ ParseResult parsed;
+
+ if (isArray(value)) {
+ const std::size_t length = arrayLength(value);
+ if (length == 0) {
+ error(R"(Expected an array with at least one element. If you wanted a literal array, use ["literal", []].)");
+ return ParseResult();
+ }
+
+ const optional<std::string> op = toString(arrayMember(value, 0));
+ if (!op) {
+ error(
+ "Expression name must be a string, but found " + getJSONType(arrayMember(value, 0)) +
+ R"( instead. If you wanted a literal array, use ["literal", [...]].)",
+ 0
+ );
+ return ParseResult();
+ }
+
+ const ExpressionRegistry& registry = getExpressionRegistry();
+ auto parseFunction = registry.find(*op);
+ if (parseFunction != registry.end()) {
+ parsed = parseFunction->second(value, *this);
+ } else {
+ parsed = parseCompoundExpression(*op, value, *this);
+ }
+ } else {
+ parsed = Literal::parse(value, *this);
+ }
+
+ if (!parsed) {
+ assert(errors->size() > 0);
+ return parsed;
+ }
+
+ if (expected) {
+ auto array = [&](std::unique_ptr<Expression> expression) {
+ std::vector<std::unique_ptr<Expression>> args;
+ args.push_back(std::move(expression));
+ return args;
+ };
+
+ const type::Type actual = (*parsed)->getType();
+ if ((*expected == type::String || *expected == type::Number || *expected == type::Boolean) && actual == type::Value) {
+ if (typeAnnotationOption == includeTypeAnnotations) {
+ parsed = { std::make_unique<Assertion>(*expected, array(std::move(*parsed))) };
+ }
+ } else if (expected->is<type::Array>() && actual == type::Value) {
+ if (typeAnnotationOption == includeTypeAnnotations) {
+ parsed = { std::make_unique<ArrayAssertion>(expected->get<type::Array>(), std::move(*parsed)) };
+ }
+ } else if (*expected == type::Color && (actual == type::Value || actual == type::String)) {
+ if (typeAnnotationOption == includeTypeAnnotations) {
+ parsed = { std::make_unique<Coercion>(*expected, array(std::move(*parsed))) };
+ }
+ } else {
+ checkType((*parsed)->getType());
+ if (errors->size() > 0) {
+ return ParseResult();
+ }
+ }
+ }
+
+ // If an expression's arguments are all literals, we can evaluate
+ // it immediately and replace it with a literal value in the
+ // parsed result.
+ if (!dynamic_cast<Literal *>(parsed->get()) && isConstant(**parsed)) {
+ EvaluationContext params(nullptr);
+ EvaluationResult evaluated((*parsed)->evaluate(params));
+ if (!evaluated) {
+ error(evaluated.error().message);
+ return ParseResult();
+ }
+
+ const type::Type type = (*parsed)->getType();
+ if (type.is<type::Array>()) {
+ // keep the original expression's array type, even if the evaluated
+ // type is more specific.
+ return ParseResult(std::make_unique<Literal>(
+ type.get<type::Array>(),
+ evaluated->get<std::vector<Value>>())
+ );
+ } else {
+ return ParseResult(std::make_unique<Literal>(*evaluated));
+ }
+ }
+
+ // if this is the root expression, enforce constraints on the use ["zoom"].
+ if (key.size() == 0 && !isZoomConstant(**parsed)) {
+ optional<variant<const InterpolateBase*, const Step*, ParsingError>> zoomCurve = findZoomCurve(parsed->get());
+ if (!zoomCurve) {
+ error(R"("zoom" expression may only be used as input to a top-level "step" or "interpolate" expression.)");
+ return ParseResult();
+ } else if (zoomCurve->is<ParsingError>()) {
+ error(zoomCurve->get<ParsingError>().message);
+ return ParseResult();
+ }
+ }
+
+ return parsed;
+}
+
+optional<std::string> ParsingContext::checkType(const type::Type& t) {
+ assert(expected);
+ optional<std::string> err = type::checkSubtype(*expected, t);
+ if (err) {
+ error(*err);
+ }
+ return err;
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/step.cpp b/src/mbgl/style/expression/step.cpp
new file mode 100644
index 0000000000..ddaf9417cb
--- /dev/null
+++ b/src/mbgl/style/expression/step.cpp
@@ -0,0 +1,187 @@
+#include <mbgl/style/expression/step.hpp>
+#include <mbgl/style/expression/get_covering_stops.hpp>
+#include <mbgl/util/string.hpp>
+
+#include <cmath>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+EvaluationResult Step::evaluate(const EvaluationContext& params) const {
+ const EvaluationResult evaluatedInput = input->evaluate(params);
+ if (!evaluatedInput) {
+ return evaluatedInput.error();
+ }
+
+ float x = *fromExpressionValue<float>(*evaluatedInput);
+ if (std::isnan(x)) {
+ return EvaluationError { "Input is not a number." };
+ }
+
+ if (stops.empty()) {
+ return EvaluationError { "No stops in step curve." };
+ }
+
+ auto it = stops.upper_bound(x);
+ if (it == stops.end()) {
+ return stops.rbegin()->second->evaluate(params);
+ } else if (it == stops.begin()) {
+ return stops.begin()->second->evaluate(params);
+ } else {
+ return std::prev(it)->second->evaluate(params);
+ }
+}
+
+void Step::eachChild(const std::function<void(const Expression&)>& visit) const {
+ visit(*input);
+ for (auto it = stops.begin(); it != stops.end(); it++) {
+ visit(*it->second);
+ }
+}
+
+void Step::eachStop(const std::function<void(double, const Expression&)>& visit) const {
+ for (const auto &stop : stops) {
+ visit(stop.first, *stop.second);
+ }
+}
+
+bool Step::operator==(const Expression& e) const {
+ if (auto rhs = dynamic_cast<const Step*>(&e)) {
+ return *input == *(rhs->input) && Expression::childrenEqual(stops, rhs->stops);
+ }
+ return false;
+}
+
+std::vector<optional<Value>> Step::possibleOutputs() const {
+ std::vector<optional<Value>> result;
+ for (const auto& stop : stops) {
+ for (auto& output : stop.second->possibleOutputs()) {
+ result.push_back(std::move(output));
+ }
+ }
+ return result;
+}
+
+Range<float> Step::getCoveringStops(const double lower, const double upper) const {
+ return ::mbgl::style::expression::getCoveringStops(stops, lower, upper);
+}
+
+
+ParseResult Step::parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx) {
+ assert(isArray(value));
+
+ auto length = arrayLength(value);
+
+ if (length - 1 < 4) {
+ ctx.error("Expected at least 4 arguments, but found only " + util::toString(length - 1) + ".");
+ return ParseResult();
+ }
+
+ // [step, input, firstOutput_value, 2 * (n pairs)...]
+ if ((length - 1) % 2 != 0) {
+ ctx.error("Expected an even number of arguments.");
+ return ParseResult();
+ }
+
+ ParseResult input = ctx.parse(arrayMember(value, 1), 1, {type::Number});
+ if (!input) {
+ return input;
+ }
+
+ std::map<double, std::unique_ptr<Expression>> stops;
+ optional<type::Type> outputType;
+ if (ctx.getExpected() && *ctx.getExpected() != type::Value) {
+ outputType = ctx.getExpected();
+ }
+
+ double previous = - std::numeric_limits<double>::infinity();
+
+ // consume the first output value, which doesn't have a corresponding input value,
+ // before proceeding into the "stops" loop below.
+ auto firstOutput = ctx.parse(arrayMember(value, 2), 2, outputType);
+ if (!firstOutput) {
+ return ParseResult();
+ }
+ if (!outputType) {
+ outputType = (*firstOutput)->getType();
+ }
+ stops.emplace(-std::numeric_limits<double>::infinity(), std::move(*firstOutput));
+
+
+ for (std::size_t i = 3; i + 1 < length; i += 2) {
+ const optional<mbgl::Value> labelValue = toValue(arrayMember(value, i));
+ optional<double> label;
+ if (labelValue) {
+ labelValue->match(
+ [&](uint64_t n) {
+ if (n > std::numeric_limits<double>::max()) {
+ label = {std::numeric_limits<double>::infinity()};
+ } else {
+ label = {static_cast<double>(n)};
+ }
+ },
+ [&](int64_t n) {
+ if (n > std::numeric_limits<double>::max()) {
+ label = {std::numeric_limits<double>::infinity()};
+ } else {
+ label = {static_cast<double>(n)};
+ }
+ },
+ [&](double n) {
+ if (n > std::numeric_limits<double>::max()) {
+ label = {std::numeric_limits<double>::infinity()};
+ } else {
+ label = {static_cast<double>(n)};
+ }
+ },
+ [&](const auto&) {}
+ );
+ }
+ if (!label) {
+ ctx.error(R"(Input/output pairs for "step" expressions must be defined using literal numeric values (not computed expressions) for the input values.)", i);
+ return ParseResult();
+ }
+
+ if (*label <= previous) {
+ ctx.error(
+ R"(Input/output pairs for "step" expressions must be arranged with input values in strictly ascending order.)",
+ i
+ );
+ return ParseResult();
+ }
+ previous = *label;
+
+ auto output = ctx.parse(arrayMember(value, i + 1), i + 1, outputType);
+ if (!output) {
+ return ParseResult();
+ }
+ if (!outputType) {
+ outputType = (*output)->getType();
+ }
+
+ stops.emplace(*label, std::move(*output));
+ }
+
+ assert(outputType);
+
+ return ParseResult(std::make_unique<Step>(*outputType, std::move(*input), std::move(stops)));
+}
+
+mbgl::Value Step::serialize() const {
+ std::vector<mbgl::Value> serialized;
+ serialized.emplace_back(getOperator());
+ serialized.emplace_back(input->serialize());
+ for (auto& entry : stops) {
+ if (entry.first > -std::numeric_limits<double>::infinity()) {
+ serialized.emplace_back(entry.first);
+ }
+ serialized.emplace_back(entry.second->serialize());
+ }
+ return serialized;
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
+
diff --git a/src/mbgl/style/expression/util.cpp b/src/mbgl/style/expression/util.cpp
new file mode 100644
index 0000000000..ee680dab08
--- /dev/null
+++ b/src/mbgl/style/expression/util.cpp
@@ -0,0 +1,37 @@
+#include <mbgl/style/expression/util.hpp>
+#include <mbgl/style/expression/value.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+std::string stringifyColor(double r, double g, double b, double a) {
+ return stringify(r) + ", " +
+ stringify(g) + ", " +
+ stringify(b) + ", " +
+ stringify(a);
+}
+
+Result<Color> rgba(double r, double g, double b, double a) {
+ if (
+ r < 0 || r > 255 ||
+ g < 0 || g > 255 ||
+ b < 0 || b > 255
+ ) {
+ return EvaluationError {
+ "Invalid rgba value [" + stringifyColor(r, g, b, a) +
+ "]: 'r', 'g', and 'b' must be between 0 and 255."
+ };
+ }
+ if (a < 0 || a > 1) {
+ return EvaluationError {
+ "Invalid rgba value [" + stringifyColor(r, g, b, a) +
+ "]: 'a' must be between 0 and 1."
+ };
+ }
+ return Color(r / 255 * a, g / 255 * a, b / 255 * a, a);
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/util.hpp b/src/mbgl/style/expression/util.hpp
new file mode 100644
index 0000000000..b6fc408ed9
--- /dev/null
+++ b/src/mbgl/style/expression/util.hpp
@@ -0,0 +1,14 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/util/color.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+Result<Color> rgba(double r, double g, double b, double a);
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/value.cpp b/src/mbgl/style/expression/value.cpp
new file mode 100644
index 0000000000..72779d4956
--- /dev/null
+++ b/src/mbgl/style/expression/value.cpp
@@ -0,0 +1,353 @@
+#include <rapidjson/writer.h>
+#include <rapidjson/stringbuffer.h>
+#include <mbgl/style/expression/value.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+type::Type typeOf(const Value& value) {
+ return value.match(
+ [&](bool) -> type::Type { return type::Boolean; },
+ [&](double) -> type::Type { return type::Number; },
+ [&](const std::string&) -> type::Type { return type::String; },
+ [&](const Color&) -> type::Type { return type::Color; },
+ [&](const NullValue&) -> type::Type { return type::Null; },
+ [&](const std::unordered_map<std::string, Value>&) -> type::Type { return type::Object; },
+ [&](const std::vector<Value>& arr) -> type::Type {
+ optional<type::Type> itemType;
+ for (const auto& item : arr) {
+ const type::Type t = typeOf(item);
+ if (!itemType) {
+ itemType = {t};
+ } else if (*itemType == t) {
+ continue;
+ } else {
+ itemType = {type::Value};
+ break;
+ }
+ }
+
+ return type::Array(itemType.value_or(type::Value), arr.size());
+ }
+ );
+}
+
+void writeJSON(rapidjson::Writer<rapidjson::StringBuffer>& writer, const Value& value) {
+ value.match(
+ [&] (const NullValue&) { writer.Null(); },
+ [&] (bool b) { writer.Bool(b); },
+ [&] (double f) {
+ // make sure integer values are stringified without trailing ".0".
+ f == std::floor(f) ? writer.Int(f) : writer.Double(f);
+ },
+ [&] (const std::string& s) { writer.String(s); },
+ [&] (const Color& c) { writer.String(c.stringify()); },
+ [&] (const std::vector<Value>& arr) {
+ writer.StartArray();
+ for(const auto& item : arr) {
+ writeJSON(writer, item);
+ }
+ writer.EndArray();
+ },
+ [&] (const std::unordered_map<std::string, Value>& obj) {
+ writer.StartObject();
+ for(const auto& entry : obj) {
+ writer.Key(entry.first.c_str());
+ writeJSON(writer, entry.second);
+ }
+ writer.EndObject();
+ }
+ );
+}
+
+std::string stringify(const Value& value) {
+ rapidjson::StringBuffer buffer;
+ rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
+ writeJSON(writer, value);
+ return buffer.GetString();
+}
+
+struct FromMBGLValue {
+ Value operator()(const std::vector<mbgl::Value>& v) {
+ std::vector<Value> result;
+ result.reserve(v.size());
+ for(const auto& item : v) {
+ result.emplace_back(toExpressionValue(item));
+ }
+ return result;
+ }
+
+ Value operator()(const std::unordered_map<std::string, mbgl::Value>& v) {
+ std::unordered_map<std::string, Value> result;
+ result.reserve(v.size());
+ for(const auto& entry : v) {
+ result.emplace(entry.first, toExpressionValue(entry.second));
+ }
+ return result;
+ }
+
+ Value operator()(const std::string& s) { return s; }
+ Value operator()(const bool b) { return b; }
+ Value operator()(const NullValue) { return Null; }
+ Value operator()(const double v) { return v; }
+ Value operator()(const uint64_t& v) {
+ return static_cast<double>(v);
+ }
+ Value operator()(const int64_t& v) {
+ return static_cast<double>(v);
+ }
+};
+
+Value ValueConverter<mbgl::Value>::toExpressionValue(const mbgl::Value& value) {
+ return mbgl::Value::visit(value, FromMBGLValue());
+}
+
+mbgl::Value ValueConverter<mbgl::Value>::fromExpressionValue(const Value& value) {
+ return value.match(
+ [&](const Color& color)->mbgl::Value {
+ return std::vector<mbgl::Value>{
+ std::string("rgba"),
+ double(255 * color.r / color.a),
+ double(255 * color.g / color.a),
+ double(255 * color.b / color.a),
+ double(color.a)
+ };
+ },
+ [&](const std::vector<Value>& values)->mbgl::Value {
+ std::vector<mbgl::Value> converted;
+ converted.reserve(values.size());
+ for (const Value& v : values) {
+ converted.emplace_back(fromExpressionValue(v));
+ }
+ return converted;
+ },
+ [&](const std::unordered_map<std::string, Value>& values)->mbgl::Value {
+ std::unordered_map<std::string, mbgl::Value> converted;
+ converted.reserve(values.size());
+ for(const auto& entry : values) {
+ converted.emplace(entry.first, fromExpressionValue(entry.second));
+ }
+ return converted;
+ },
+ [&](const auto& a)->mbgl::Value { return a; }
+ );
+}
+
+Value ValueConverter<float>::toExpressionValue(const float value) {
+ return static_cast<double>(value);
+}
+
+optional<float> ValueConverter<float>::fromExpressionValue(const Value& value) {
+ return value.template is<double>()
+ ? static_cast<float>(value.template get<double>())
+ : optional<float>();
+}
+
+
+template <typename T, typename Container>
+std::vector<Value> toArrayValue(const Container& value) {
+ std::vector<Value> result;
+ result.reserve(value.size());
+ for (const T& item : value) {
+ result.push_back(ValueConverter<T>::toExpressionValue(item));
+ }
+ return result;
+}
+
+template <typename T, std::size_t N>
+Value ValueConverter<std::array<T, N>>::toExpressionValue(const std::array<T, N>& value) {
+ return toArrayValue<T>(value);
+}
+
+template <typename T, std::size_t N>
+optional<std::array<T, N>> ValueConverter<std::array<T, N>>::fromExpressionValue(const Value& value) {
+ return value.match(
+ [&] (const std::vector<Value>& v) -> optional<std::array<T, N>> {
+ if (v.size() != N) return optional<std::array<T, N>>();
+ std::array<T, N> result;
+ auto it = result.begin();
+ for(const Value& item : v) {
+ optional<T> convertedItem = ValueConverter<T>::fromExpressionValue(item);
+ if (!convertedItem) {
+ return optional<std::array<T, N>>();
+ }
+ *it = *convertedItem;
+ it = std::next(it);
+ }
+ return result;
+ },
+ [&] (const auto&) { return optional<std::array<T, N>>(); }
+ );
+}
+
+
+template <typename T>
+Value ValueConverter<std::vector<T>>::toExpressionValue(const std::vector<T>& value) {
+ return toArrayValue<T>(value);
+}
+
+template <typename T>
+optional<std::vector<T>> ValueConverter<std::vector<T>>::fromExpressionValue(const Value& value) {
+ return value.match(
+ [&] (const std::vector<Value>& v) -> optional<std::vector<T>> {
+ std::vector<T> result;
+ result.reserve(v.size());
+ for(const Value& item : v) {
+ optional<T> convertedItem = ValueConverter<T>::fromExpressionValue(item);
+ if (!convertedItem) {
+ return optional<std::vector<T>>();
+ }
+ result.push_back(*convertedItem);
+ }
+ return result;
+ },
+ [&] (const auto&) { return optional<std::vector<T>>(); }
+ );
+}
+
+Value ValueConverter<Position>::toExpressionValue(const mbgl::style::Position& value) {
+ return ValueConverter<std::array<float, 3>>::toExpressionValue(value.getSpherical());
+}
+
+optional<Position> ValueConverter<Position>::fromExpressionValue(const Value& v) {
+ auto pos = ValueConverter<std::array<float, 3>>::fromExpressionValue(v);
+ return pos ? optional<Position>(Position(*pos)) : optional<Position>();
+}
+
+template <typename T>
+Value ValueConverter<T, std::enable_if_t< std::is_enum<T>::value >>::toExpressionValue(const T& value) {
+ return std::string(Enum<T>::toString(value));
+}
+
+template <typename T>
+optional<T> ValueConverter<T, std::enable_if_t< std::is_enum<T>::value >>::fromExpressionValue(const Value& value) {
+ return value.match(
+ [&] (const std::string& v) { return Enum<T>::toEnum(v); },
+ [&] (const auto&) { return optional<T>(); }
+ );
+}
+
+
+Value toExpressionValue(const Value& v) {
+ return v;
+}
+
+template <typename T, typename Enable>
+Value toExpressionValue(const T& value) {
+ return ValueConverter<T>::toExpressionValue(value);
+}
+
+optional<Value> fromExpressionValue(const Value& v) {
+ return optional<Value>(v);
+}
+
+template <typename T>
+std::enable_if_t< !std::is_convertible<T, Value>::value,
+optional<T>> fromExpressionValue(const Value& v)
+{
+ return ValueConverter<T>::fromExpressionValue(v);
+}
+
+template <typename T>
+type::Type valueTypeToExpressionType() {
+ return ValueConverter<T>::expressionType();
+}
+
+template <> type::Type valueTypeToExpressionType<Value>() { return type::Value; }
+template <> type::Type valueTypeToExpressionType<NullValue>() { return type::Null; }
+template <> type::Type valueTypeToExpressionType<bool>() { return type::Boolean; }
+template <> type::Type valueTypeToExpressionType<double>() { return type::Number; }
+template <> type::Type valueTypeToExpressionType<std::string>() { return type::String; }
+template <> type::Type valueTypeToExpressionType<Color>() { return type::Color; }
+template <> type::Type valueTypeToExpressionType<std::unordered_map<std::string, Value>>() { return type::Object; }
+template <> type::Type valueTypeToExpressionType<std::vector<Value>>() { return type::Array(type::Value); }
+
+// used only for the special (and private) "error" expression
+template <> type::Type valueTypeToExpressionType<type::ErrorType>() { return type::Error; }
+
+
+template Value toExpressionValue(const mbgl::Value&);
+template optional<mbgl::Value> fromExpressionValue<mbgl::Value>(const Value&);
+
+// for to_rgba expression
+template type::Type valueTypeToExpressionType<std::array<double, 4>>();
+template optional<std::array<double, 4>> fromExpressionValue<std::array<double, 4>>(const Value&);
+template Value toExpressionValue(const std::array<double, 4>&);
+
+// layout/paint property types
+template type::Type valueTypeToExpressionType<float>();
+template optional<float> fromExpressionValue<float>(const Value&);
+template Value toExpressionValue(const float&);
+
+template type::Type valueTypeToExpressionType<std::array<float, 2>>();
+template optional<std::array<float, 2>> fromExpressionValue<std::array<float, 2>>(const Value&);
+template Value toExpressionValue(const std::array<float, 2>&);
+
+template type::Type valueTypeToExpressionType<std::array<float, 4>>();
+template optional<std::array<float, 4>> fromExpressionValue<std::array<float, 4>>(const Value&);
+template Value toExpressionValue(const std::array<float, 4>&);
+
+template type::Type valueTypeToExpressionType<std::vector<float>>();
+template optional<std::vector<float>> fromExpressionValue<std::vector<float>>(const Value&);
+template Value toExpressionValue(const std::vector<float>&);
+
+template type::Type valueTypeToExpressionType<std::vector<std::string>>();
+template optional<std::vector<std::string>> fromExpressionValue<std::vector<std::string>>(const Value&);
+template Value toExpressionValue(const std::vector<std::string>&);
+
+template type::Type valueTypeToExpressionType<AlignmentType>();
+template optional<AlignmentType> fromExpressionValue<AlignmentType>(const Value&);
+template Value toExpressionValue(const AlignmentType&);
+
+template type::Type valueTypeToExpressionType<CirclePitchScaleType>();
+template optional<CirclePitchScaleType> fromExpressionValue<CirclePitchScaleType>(const Value&);
+template Value toExpressionValue(const CirclePitchScaleType&);
+
+template type::Type valueTypeToExpressionType<IconTextFitType>();
+template optional<IconTextFitType> fromExpressionValue<IconTextFitType>(const Value&);
+template Value toExpressionValue(const IconTextFitType&);
+
+template type::Type valueTypeToExpressionType<LineCapType>();
+template optional<LineCapType> fromExpressionValue<LineCapType>(const Value&);
+template Value toExpressionValue(const LineCapType&);
+
+template type::Type valueTypeToExpressionType<LineJoinType>();
+template optional<LineJoinType> fromExpressionValue<LineJoinType>(const Value&);
+template Value toExpressionValue(const LineJoinType&);
+
+template type::Type valueTypeToExpressionType<SymbolPlacementType>();
+template optional<SymbolPlacementType> fromExpressionValue<SymbolPlacementType>(const Value&);
+template Value toExpressionValue(const SymbolPlacementType&);
+
+template type::Type valueTypeToExpressionType<SymbolAnchorType>();
+template optional<SymbolAnchorType> fromExpressionValue<SymbolAnchorType>(const Value&);
+template Value toExpressionValue(const SymbolAnchorType&);
+
+template type::Type valueTypeToExpressionType<TextJustifyType>();
+template optional<TextJustifyType> fromExpressionValue<TextJustifyType>(const Value&);
+template Value toExpressionValue(const TextJustifyType&);
+
+template type::Type valueTypeToExpressionType<TextTransformType>();
+template optional<TextTransformType> fromExpressionValue<TextTransformType>(const Value&);
+template Value toExpressionValue(const TextTransformType&);
+
+template type::Type valueTypeToExpressionType<TranslateAnchorType>();
+template optional<TranslateAnchorType> fromExpressionValue<TranslateAnchorType>(const Value&);
+template Value toExpressionValue(const TranslateAnchorType&);
+
+template type::Type valueTypeToExpressionType<HillshadeIlluminationAnchorType>();
+template optional<HillshadeIlluminationAnchorType> fromExpressionValue<HillshadeIlluminationAnchorType>(const Value&);
+template Value toExpressionValue(const HillshadeIlluminationAnchorType&);
+
+template type::Type valueTypeToExpressionType<LightAnchorType>();
+template optional<LightAnchorType> fromExpressionValue<LightAnchorType>(const Value&);
+template Value toExpressionValue(const LightAnchorType&);
+
+template type::Type valueTypeToExpressionType<Position>();
+template optional<Position> fromExpressionValue<Position>(const Value&);
+template Value toExpressionValue(const Position&);
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/function/expression.cpp b/src/mbgl/style/function/expression.cpp
new file mode 100644
index 0000000000..d9dbbfa1d3
--- /dev/null
+++ b/src/mbgl/style/function/expression.cpp
@@ -0,0 +1,38 @@
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/expression/compound_expression.hpp>
+#include <mbgl/tile/geometry_tile_data.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+class GeoJSONFeature : public GeometryTileFeature {
+public:
+ const Feature& feature;
+
+ GeoJSONFeature(const Feature& feature_) : feature(feature_) {}
+
+ FeatureType getType() const override {
+ return apply_visitor(ToFeatureType(), feature.geometry);
+ }
+ PropertyMap getProperties() const override { return feature.properties; }
+ optional<FeatureIdentifier> getID() const override { return feature.id; }
+ GeometryCollection getGeometries() const override { return {}; }
+ optional<mbgl::Value> getValue(const std::string& key) const override {
+ auto it = feature.properties.find(key);
+ if (it != feature.properties.end()) {
+ return optional<mbgl::Value>(it->second);
+ }
+ return optional<mbgl::Value>();
+ }
+};
+
+
+EvaluationResult Expression::evaluate(optional<float> zoom, const Feature& feature, optional<double> heatmapDensity) const {
+ GeoJSONFeature f(feature);
+ return this->evaluate(EvaluationContext(zoom, &f, heatmapDensity));
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/layers/heatmap_layer.cpp b/src/mbgl/style/layers/heatmap_layer.cpp
new file mode 100644
index 0000000000..4989ff15f1
--- /dev/null
+++ b/src/mbgl/style/layers/heatmap_layer.cpp
@@ -0,0 +1,239 @@
+// This file is generated. Edit scripts/generate-style-code.js, then run `make style-code`.
+
+#include <mbgl/style/layers/heatmap_layer.hpp>
+#include <mbgl/style/layers/heatmap_layer_impl.hpp>
+#include <mbgl/style/layer_observer.hpp>
+// for constructing default heatmap-color ramp expression from style JSON
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/json.hpp>
+#include <mbgl/style/conversion/heatmap_color_property_value.hpp>
+
+namespace mbgl {
+namespace style {
+
+HeatmapLayer::HeatmapLayer(const std::string& layerID, const std::string& sourceID)
+ : Layer(makeMutable<Impl>(LayerType::Heatmap, layerID, sourceID)) {
+}
+
+HeatmapLayer::HeatmapLayer(Immutable<Impl> impl_)
+ : Layer(std::move(impl_)) {
+}
+
+HeatmapLayer::~HeatmapLayer() = default;
+
+const HeatmapLayer::Impl& HeatmapLayer::impl() const {
+ return static_cast<const Impl&>(*baseImpl);
+}
+
+Mutable<HeatmapLayer::Impl> HeatmapLayer::mutableImpl() const {
+ return makeMutable<Impl>(impl());
+}
+
+std::unique_ptr<Layer> HeatmapLayer::cloneRef(const std::string& id_) const {
+ auto impl_ = mutableImpl();
+ impl_->id = id_;
+ impl_->paint = HeatmapPaintProperties::Transitionable();
+ return std::make_unique<HeatmapLayer>(std::move(impl_));
+}
+
+void HeatmapLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const {
+}
+
+// Source
+
+const std::string& HeatmapLayer::getSourceID() const {
+ return impl().source;
+}
+
+void HeatmapLayer::setSourceLayer(const std::string& sourceLayer) {
+ auto impl_ = mutableImpl();
+ impl_->sourceLayer = sourceLayer;
+ baseImpl = std::move(impl_);
+}
+
+const std::string& HeatmapLayer::getSourceLayer() const {
+ return impl().sourceLayer;
+}
+
+// Filter
+
+void HeatmapLayer::setFilter(const Filter& filter) {
+ auto impl_ = mutableImpl();
+ impl_->filter = filter;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
+}
+
+const Filter& HeatmapLayer::getFilter() const {
+ return impl().filter;
+}
+
+// Visibility
+
+void HeatmapLayer::setVisibility(VisibilityType value) {
+ if (value == getVisibility())
+ return;
+ auto impl_ = mutableImpl();
+ impl_->visibility = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
+}
+
+// Zoom range
+
+void HeatmapLayer::setMinZoom(float minZoom) {
+ auto impl_ = mutableImpl();
+ impl_->minZoom = minZoom;
+ baseImpl = std::move(impl_);
+}
+
+void HeatmapLayer::setMaxZoom(float maxZoom) {
+ auto impl_ = mutableImpl();
+ impl_->maxZoom = maxZoom;
+ baseImpl = std::move(impl_);
+}
+
+// Layout properties
+
+
+// Paint properties
+
+DataDrivenPropertyValue<float> HeatmapLayer::getDefaultHeatmapRadius() {
+ return { 30 };
+}
+
+DataDrivenPropertyValue<float> HeatmapLayer::getHeatmapRadius() const {
+ return impl().paint.template get<HeatmapRadius>().value;
+}
+
+void HeatmapLayer::setHeatmapRadius(DataDrivenPropertyValue<float> value) {
+ if (value == getHeatmapRadius())
+ return;
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<HeatmapRadius>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
+}
+
+void HeatmapLayer::setHeatmapRadiusTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<HeatmapRadius>().options = options;
+ baseImpl = std::move(impl_);
+}
+
+TransitionOptions HeatmapLayer::getHeatmapRadiusTransition() const {
+ return impl().paint.template get<HeatmapRadius>().options;
+}
+
+DataDrivenPropertyValue<float> HeatmapLayer::getDefaultHeatmapWeight() {
+ return { 1 };
+}
+
+DataDrivenPropertyValue<float> HeatmapLayer::getHeatmapWeight() const {
+ return impl().paint.template get<HeatmapWeight>().value;
+}
+
+void HeatmapLayer::setHeatmapWeight(DataDrivenPropertyValue<float> value) {
+ if (value == getHeatmapWeight())
+ return;
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<HeatmapWeight>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
+}
+
+void HeatmapLayer::setHeatmapWeightTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<HeatmapWeight>().options = options;
+ baseImpl = std::move(impl_);
+}
+
+TransitionOptions HeatmapLayer::getHeatmapWeightTransition() const {
+ return impl().paint.template get<HeatmapWeight>().options;
+}
+
+PropertyValue<float> HeatmapLayer::getDefaultHeatmapIntensity() {
+ return { 1 };
+}
+
+PropertyValue<float> HeatmapLayer::getHeatmapIntensity() const {
+ return impl().paint.template get<HeatmapIntensity>().value;
+}
+
+void HeatmapLayer::setHeatmapIntensity(PropertyValue<float> value) {
+ if (value == getHeatmapIntensity())
+ return;
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<HeatmapIntensity>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
+}
+
+void HeatmapLayer::setHeatmapIntensityTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<HeatmapIntensity>().options = options;
+ baseImpl = std::move(impl_);
+}
+
+TransitionOptions HeatmapLayer::getHeatmapIntensityTransition() const {
+ return impl().paint.template get<HeatmapIntensity>().options;
+}
+
+HeatmapColorPropertyValue HeatmapLayer::getDefaultHeatmapColor() {
+ conversion::Error error;
+ std::string rawValue = R"JSON(["interpolate",["linear"],["heatmap-density"],0,"rgba(0, 0, 255, 0)",0.1,"royalblue",0.3,"cyan",0.5,"lime",0.7,"yellow",1,"red"])JSON";
+ return *conversion::convertJSON<HeatmapColorPropertyValue>(rawValue, error);
+}
+
+HeatmapColorPropertyValue HeatmapLayer::getHeatmapColor() const {
+ return impl().paint.template get<HeatmapColor>().value;
+}
+
+void HeatmapLayer::setHeatmapColor(HeatmapColorPropertyValue value) {
+ if (value == getHeatmapColor())
+ return;
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<HeatmapColor>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
+}
+
+void HeatmapLayer::setHeatmapColorTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<HeatmapColor>().options = options;
+ baseImpl = std::move(impl_);
+}
+
+TransitionOptions HeatmapLayer::getHeatmapColorTransition() const {
+ return impl().paint.template get<HeatmapColor>().options;
+}
+
+PropertyValue<float> HeatmapLayer::getDefaultHeatmapOpacity() {
+ return { 1 };
+}
+
+PropertyValue<float> HeatmapLayer::getHeatmapOpacity() const {
+ return impl().paint.template get<HeatmapOpacity>().value;
+}
+
+void HeatmapLayer::setHeatmapOpacity(PropertyValue<float> value) {
+ if (value == getHeatmapOpacity())
+ return;
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<HeatmapOpacity>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
+}
+
+void HeatmapLayer::setHeatmapOpacityTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<HeatmapOpacity>().options = options;
+ baseImpl = std::move(impl_);
+}
+
+TransitionOptions HeatmapLayer::getHeatmapOpacityTransition() const {
+ return impl().paint.template get<HeatmapOpacity>().options;
+}
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/layers/heatmap_layer_impl.cpp b/src/mbgl/style/layers/heatmap_layer_impl.cpp
new file mode 100644
index 0000000000..af20888d9d
--- /dev/null
+++ b/src/mbgl/style/layers/heatmap_layer_impl.cpp
@@ -0,0 +1,15 @@
+#include <mbgl/style/layers/heatmap_layer_impl.hpp>
+
+namespace mbgl {
+namespace style {
+
+bool HeatmapLayer::Impl::hasLayoutDifference(const Layer::Impl& other) const {
+ assert(dynamic_cast<const HeatmapLayer::Impl*>(&other));
+ const auto& impl = static_cast<const style::HeatmapLayer::Impl&>(other);
+ return filter != impl.filter ||
+ visibility != impl.visibility ||
+ paint.hasDataDrivenPropertyDifference(impl.paint);
+}
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/layers/heatmap_layer_impl.hpp b/src/mbgl/style/layers/heatmap_layer_impl.hpp
new file mode 100644
index 0000000000..cc27c3076a
--- /dev/null
+++ b/src/mbgl/style/layers/heatmap_layer_impl.hpp
@@ -0,0 +1,21 @@
+#pragma once
+
+#include <mbgl/style/layer_impl.hpp>
+#include <mbgl/style/layers/heatmap_layer.hpp>
+#include <mbgl/style/layers/heatmap_layer_properties.hpp>
+
+namespace mbgl {
+namespace style {
+
+class HeatmapLayer::Impl : public Layer::Impl {
+public:
+ using Layer::Impl::Impl;
+
+ bool hasLayoutDifference(const Layer::Impl&) const override;
+ void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override;
+
+ HeatmapPaintProperties::Transitionable paint;
+};
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/layers/heatmap_layer_properties.cpp b/src/mbgl/style/layers/heatmap_layer_properties.cpp
new file mode 100644
index 0000000000..2edb839589
--- /dev/null
+++ b/src/mbgl/style/layers/heatmap_layer_properties.cpp
@@ -0,0 +1,9 @@
+// This file is generated. Edit scripts/generate-style-code.js, then run `make style-code`.
+
+#include <mbgl/style/layers/heatmap_layer_properties.hpp>
+
+namespace mbgl {
+namespace style {
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/layers/heatmap_layer_properties.hpp b/src/mbgl/style/layers/heatmap_layer_properties.hpp
new file mode 100644
index 0000000000..f7afa5fbeb
--- /dev/null
+++ b/src/mbgl/style/layers/heatmap_layer_properties.hpp
@@ -0,0 +1,40 @@
+// This file is generated. Edit scripts/generate-style-code.js, then run `make style-code`.
+
+#pragma once
+
+#include <mbgl/style/types.hpp>
+#include <mbgl/style/layout_property.hpp>
+#include <mbgl/style/paint_property.hpp>
+#include <mbgl/style/properties.hpp>
+#include <mbgl/programs/attributes.hpp>
+#include <mbgl/programs/uniforms.hpp>
+
+namespace mbgl {
+namespace style {
+
+struct HeatmapRadius : DataDrivenPaintProperty<float, attributes::a_radius, uniforms::u_radius> {
+ static float defaultValue() { return 30; }
+};
+
+struct HeatmapWeight : DataDrivenPaintProperty<float, attributes::a_weight, uniforms::u_weight> {
+ static float defaultValue() { return 1; }
+};
+
+struct HeatmapIntensity : PaintProperty<float> {
+ static float defaultValue() { return 1; }
+};
+
+struct HeatmapOpacity : PaintProperty<float> {
+ static float defaultValue() { return 1; }
+};
+
+class HeatmapPaintProperties : public Properties<
+ HeatmapRadius,
+ HeatmapWeight,
+ HeatmapIntensity,
+ HeatmapColor,
+ HeatmapOpacity
+> {};
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/layers/hillshade_layer.cpp b/src/mbgl/style/layers/hillshade_layer.cpp
new file mode 100644
index 0000000000..ea736af1ad
--- /dev/null
+++ b/src/mbgl/style/layers/hillshade_layer.cpp
@@ -0,0 +1,238 @@
+// This file is generated. Edit scripts/generate-style-code.js, then run `make style-code`.
+
+#include <mbgl/style/layers/hillshade_layer.hpp>
+#include <mbgl/style/layers/hillshade_layer_impl.hpp>
+#include <mbgl/style/layer_observer.hpp>
+
+namespace mbgl {
+namespace style {
+
+HillshadeLayer::HillshadeLayer(const std::string& layerID, const std::string& sourceID)
+ : Layer(makeMutable<Impl>(LayerType::Hillshade, layerID, sourceID)) {
+}
+
+HillshadeLayer::HillshadeLayer(Immutable<Impl> impl_)
+ : Layer(std::move(impl_)) {
+}
+
+HillshadeLayer::~HillshadeLayer() = default;
+
+const HillshadeLayer::Impl& HillshadeLayer::impl() const {
+ return static_cast<const Impl&>(*baseImpl);
+}
+
+Mutable<HillshadeLayer::Impl> HillshadeLayer::mutableImpl() const {
+ return makeMutable<Impl>(impl());
+}
+
+std::unique_ptr<Layer> HillshadeLayer::cloneRef(const std::string& id_) const {
+ auto impl_ = mutableImpl();
+ impl_->id = id_;
+ impl_->paint = HillshadePaintProperties::Transitionable();
+ return std::make_unique<HillshadeLayer>(std::move(impl_));
+}
+
+void HillshadeLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const {
+}
+
+// Source
+
+const std::string& HillshadeLayer::getSourceID() const {
+ return impl().source;
+}
+
+
+// Visibility
+
+void HillshadeLayer::setVisibility(VisibilityType value) {
+ if (value == getVisibility())
+ return;
+ auto impl_ = mutableImpl();
+ impl_->visibility = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
+}
+
+// Zoom range
+
+void HillshadeLayer::setMinZoom(float minZoom) {
+ auto impl_ = mutableImpl();
+ impl_->minZoom = minZoom;
+ baseImpl = std::move(impl_);
+}
+
+void HillshadeLayer::setMaxZoom(float maxZoom) {
+ auto impl_ = mutableImpl();
+ impl_->maxZoom = maxZoom;
+ baseImpl = std::move(impl_);
+}
+
+// Layout properties
+
+
+// Paint properties
+
+PropertyValue<float> HillshadeLayer::getDefaultHillshadeIlluminationDirection() {
+ return { 335 };
+}
+
+PropertyValue<float> HillshadeLayer::getHillshadeIlluminationDirection() const {
+ return impl().paint.template get<HillshadeIlluminationDirection>().value;
+}
+
+void HillshadeLayer::setHillshadeIlluminationDirection(PropertyValue<float> value) {
+ if (value == getHillshadeIlluminationDirection())
+ return;
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<HillshadeIlluminationDirection>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
+}
+
+void HillshadeLayer::setHillshadeIlluminationDirectionTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<HillshadeIlluminationDirection>().options = options;
+ baseImpl = std::move(impl_);
+}
+
+TransitionOptions HillshadeLayer::getHillshadeIlluminationDirectionTransition() const {
+ return impl().paint.template get<HillshadeIlluminationDirection>().options;
+}
+
+PropertyValue<HillshadeIlluminationAnchorType> HillshadeLayer::getDefaultHillshadeIlluminationAnchor() {
+ return { HillshadeIlluminationAnchorType::Viewport };
+}
+
+PropertyValue<HillshadeIlluminationAnchorType> HillshadeLayer::getHillshadeIlluminationAnchor() const {
+ return impl().paint.template get<HillshadeIlluminationAnchor>().value;
+}
+
+void HillshadeLayer::setHillshadeIlluminationAnchor(PropertyValue<HillshadeIlluminationAnchorType> value) {
+ if (value == getHillshadeIlluminationAnchor())
+ return;
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<HillshadeIlluminationAnchor>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
+}
+
+void HillshadeLayer::setHillshadeIlluminationAnchorTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<HillshadeIlluminationAnchor>().options = options;
+ baseImpl = std::move(impl_);
+}
+
+TransitionOptions HillshadeLayer::getHillshadeIlluminationAnchorTransition() const {
+ return impl().paint.template get<HillshadeIlluminationAnchor>().options;
+}
+
+PropertyValue<float> HillshadeLayer::getDefaultHillshadeExaggeration() {
+ return { 0.5 };
+}
+
+PropertyValue<float> HillshadeLayer::getHillshadeExaggeration() const {
+ return impl().paint.template get<HillshadeExaggeration>().value;
+}
+
+void HillshadeLayer::setHillshadeExaggeration(PropertyValue<float> value) {
+ if (value == getHillshadeExaggeration())
+ return;
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<HillshadeExaggeration>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
+}
+
+void HillshadeLayer::setHillshadeExaggerationTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<HillshadeExaggeration>().options = options;
+ baseImpl = std::move(impl_);
+}
+
+TransitionOptions HillshadeLayer::getHillshadeExaggerationTransition() const {
+ return impl().paint.template get<HillshadeExaggeration>().options;
+}
+
+PropertyValue<Color> HillshadeLayer::getDefaultHillshadeShadowColor() {
+ return { Color::black() };
+}
+
+PropertyValue<Color> HillshadeLayer::getHillshadeShadowColor() const {
+ return impl().paint.template get<HillshadeShadowColor>().value;
+}
+
+void HillshadeLayer::setHillshadeShadowColor(PropertyValue<Color> value) {
+ if (value == getHillshadeShadowColor())
+ return;
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<HillshadeShadowColor>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
+}
+
+void HillshadeLayer::setHillshadeShadowColorTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<HillshadeShadowColor>().options = options;
+ baseImpl = std::move(impl_);
+}
+
+TransitionOptions HillshadeLayer::getHillshadeShadowColorTransition() const {
+ return impl().paint.template get<HillshadeShadowColor>().options;
+}
+
+PropertyValue<Color> HillshadeLayer::getDefaultHillshadeHighlightColor() {
+ return { Color::white() };
+}
+
+PropertyValue<Color> HillshadeLayer::getHillshadeHighlightColor() const {
+ return impl().paint.template get<HillshadeHighlightColor>().value;
+}
+
+void HillshadeLayer::setHillshadeHighlightColor(PropertyValue<Color> value) {
+ if (value == getHillshadeHighlightColor())
+ return;
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<HillshadeHighlightColor>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
+}
+
+void HillshadeLayer::setHillshadeHighlightColorTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<HillshadeHighlightColor>().options = options;
+ baseImpl = std::move(impl_);
+}
+
+TransitionOptions HillshadeLayer::getHillshadeHighlightColorTransition() const {
+ return impl().paint.template get<HillshadeHighlightColor>().options;
+}
+
+PropertyValue<Color> HillshadeLayer::getDefaultHillshadeAccentColor() {
+ return { Color::black() };
+}
+
+PropertyValue<Color> HillshadeLayer::getHillshadeAccentColor() const {
+ return impl().paint.template get<HillshadeAccentColor>().value;
+}
+
+void HillshadeLayer::setHillshadeAccentColor(PropertyValue<Color> value) {
+ if (value == getHillshadeAccentColor())
+ return;
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<HillshadeAccentColor>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
+}
+
+void HillshadeLayer::setHillshadeAccentColorTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<HillshadeAccentColor>().options = options;
+ baseImpl = std::move(impl_);
+}
+
+TransitionOptions HillshadeLayer::getHillshadeAccentColorTransition() const {
+ return impl().paint.template get<HillshadeAccentColor>().options;
+}
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/layers/hillshade_layer_impl.cpp b/src/mbgl/style/layers/hillshade_layer_impl.cpp
new file mode 100644
index 0000000000..ed5aa922bf
--- /dev/null
+++ b/src/mbgl/style/layers/hillshade_layer_impl.cpp
@@ -0,0 +1,11 @@
+#include <mbgl/style/layers/hillshade_layer_impl.hpp>
+
+namespace mbgl {
+namespace style {
+
+bool HillshadeLayer::Impl::hasLayoutDifference(const Layer::Impl&) const {
+ return false;
+}
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/layers/hillshade_layer_impl.hpp b/src/mbgl/style/layers/hillshade_layer_impl.hpp
new file mode 100644
index 0000000000..5618b7dfe2
--- /dev/null
+++ b/src/mbgl/style/layers/hillshade_layer_impl.hpp
@@ -0,0 +1,21 @@
+#pragma once
+
+#include <mbgl/style/layer_impl.hpp>
+#include <mbgl/style/layers/hillshade_layer.hpp>
+#include <mbgl/style/layers/hillshade_layer_properties.hpp>
+
+namespace mbgl {
+namespace style {
+
+class HillshadeLayer::Impl : public Layer::Impl {
+public:
+ using Layer::Impl::Impl;
+
+ bool hasLayoutDifference(const Layer::Impl&) const override;
+ void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override;
+
+ HillshadePaintProperties::Transitionable paint;
+};
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/layers/hillshade_layer_properties.cpp b/src/mbgl/style/layers/hillshade_layer_properties.cpp
new file mode 100644
index 0000000000..f296ab4520
--- /dev/null
+++ b/src/mbgl/style/layers/hillshade_layer_properties.cpp
@@ -0,0 +1,9 @@
+// This file is generated. Edit scripts/generate-style-code.js, then run `make style-code`.
+
+#include <mbgl/style/layers/hillshade_layer_properties.hpp>
+
+namespace mbgl {
+namespace style {
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/layers/hillshade_layer_properties.hpp b/src/mbgl/style/layers/hillshade_layer_properties.hpp
new file mode 100644
index 0000000000..260d7ea808
--- /dev/null
+++ b/src/mbgl/style/layers/hillshade_layer_properties.hpp
@@ -0,0 +1,49 @@
+// This file is generated. Edit scripts/generate-style-code.js, then run `make style-code`.
+
+#pragma once
+
+#include <mbgl/style/types.hpp>
+#include <mbgl/style/layout_property.hpp>
+#include <mbgl/style/paint_property.hpp>
+#include <mbgl/style/properties.hpp>
+#include <mbgl/programs/attributes.hpp>
+#include <mbgl/programs/uniforms.hpp>
+
+namespace mbgl {
+namespace style {
+
+struct HillshadeIlluminationDirection : PaintProperty<float> {
+ static float defaultValue() { return 335; }
+};
+
+struct HillshadeIlluminationAnchor : PaintProperty<HillshadeIlluminationAnchorType> {
+ static HillshadeIlluminationAnchorType defaultValue() { return HillshadeIlluminationAnchorType::Viewport; }
+};
+
+struct HillshadeExaggeration : PaintProperty<float> {
+ static float defaultValue() { return 0.5; }
+};
+
+struct HillshadeShadowColor : PaintProperty<Color> {
+ static Color defaultValue() { return Color::black(); }
+};
+
+struct HillshadeHighlightColor : PaintProperty<Color> {
+ static Color defaultValue() { return Color::white(); }
+};
+
+struct HillshadeAccentColor : PaintProperty<Color> {
+ static Color defaultValue() { return Color::black(); }
+};
+
+class HillshadePaintProperties : public Properties<
+ HillshadeIlluminationDirection,
+ HillshadeIlluminationAnchor,
+ HillshadeExaggeration,
+ HillshadeShadowColor,
+ HillshadeHighlightColor,
+ HillshadeAccentColor
+> {};
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/layers/layer.cpp.ejs b/src/mbgl/style/layers/layer.cpp.ejs
index 573aabda8b..657a7f5a8a 100644
--- a/src/mbgl/style/layers/layer.cpp.ejs
+++ b/src/mbgl/style/layers/layer.cpp.ejs
@@ -8,6 +8,12 @@
#include <mbgl/style/layers/<%- type.replace('-', '_') %>_layer.hpp>
#include <mbgl/style/layers/<%- type.replace('-', '_') %>_layer_impl.hpp>
#include <mbgl/style/layer_observer.hpp>
+<% if (type === 'heatmap') { -%>
+// for constructing default heatmap-color ramp expression from style JSON
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/json.hpp>
+#include <mbgl/style/conversion/heatmap_color_property_value.hpp>
+<% } -%>
namespace mbgl {
namespace style {
@@ -59,7 +65,7 @@ const std::string& <%- camelize(type) %>Layer::getSourceID() const {
return impl().source;
}
-<% if (type !== 'raster') { -%>
+<% if (type !== 'raster' && type !== 'hillshade') { -%>
void <%- camelize(type) %>Layer::setSourceLayer(const std::string& sourceLayer) {
auto impl_ = mutableImpl();
impl_->sourceLayer = sourceLayer;
@@ -134,7 +140,13 @@ void <%- camelize(type) %>Layer::set<%- camelize(property.name) %>(<%- propertyV
// Paint properties
<% for (const property of paintProperties) { %>
<%- propertyValueType(property) %> <%- camelize(type) %>Layer::getDefault<%- camelize(property.name) %>() {
+<% if (property.name === 'heatmap-color') { -%>
+ conversion::Error error;
+ std::string rawValue = R"JSON(<%- JSON.stringify(property.default) %>)JSON";
+ return *conversion::convertJSON<<%- propertyValueType(property)%>>(rawValue, error);
+<% } else { -%>
return { <%- defaultValue(property) %> };
+<% } -%>
}
<%- propertyValueType(property) %> <%- camelize(type) %>Layer::get<%- camelize(property.name) %>() const {
diff --git a/src/mbgl/style/layers/layer_properties.hpp.ejs b/src/mbgl/style/layers/layer_properties.hpp.ejs
index cde1b80b7b..1bceb84960 100644
--- a/src/mbgl/style/layers/layer_properties.hpp.ejs
+++ b/src/mbgl/style/layers/layer_properties.hpp.ejs
@@ -25,6 +25,7 @@ struct <%- camelize(property.name) %> : <%- layoutPropertyType(property, type) %
<% } -%>
<% for (const property of paintProperties) { -%>
+<% if (property.name === 'heatmap-color') continue; -%>
struct <%- camelize(property.name) %> : <%- paintPropertyType(property, type) %> {
static <%- evaluatedType(property) %> defaultValue() { return <%- defaultValue(property) %>; }
};
diff --git a/src/mbgl/style/layers/symbol_layer.cpp b/src/mbgl/style/layers/symbol_layer.cpp
index 9a944657ca..d1a1ba246e 100644
--- a/src/mbgl/style/layers/symbol_layer.cpp
+++ b/src/mbgl/style/layers/symbol_layer.cpp
@@ -412,15 +412,15 @@ void SymbolLayer::setTextField(DataDrivenPropertyValue<std::string> value) {
baseImpl = std::move(impl_);
observer->onLayerChanged(*this);
}
-PropertyValue<std::vector<std::string>> SymbolLayer::getDefaultTextFont() {
+DataDrivenPropertyValue<std::vector<std::string>> SymbolLayer::getDefaultTextFont() {
return TextFont::defaultValue();
}
-PropertyValue<std::vector<std::string>> SymbolLayer::getTextFont() const {
+DataDrivenPropertyValue<std::vector<std::string>> SymbolLayer::getTextFont() const {
return impl().layout.get<TextFont>();
}
-void SymbolLayer::setTextFont(PropertyValue<std::vector<std::string>> value) {
+void SymbolLayer::setTextFont(DataDrivenPropertyValue<std::vector<std::string>> value) {
if (value == getTextFont())
return;
auto impl_ = mutableImpl();
diff --git a/src/mbgl/style/layers/symbol_layer_properties.hpp b/src/mbgl/style/layers/symbol_layer_properties.hpp
index 436b5cbd4f..e70ac28d59 100644
--- a/src/mbgl/style/layers/symbol_layer_properties.hpp
+++ b/src/mbgl/style/layers/symbol_layer_properties.hpp
@@ -112,7 +112,7 @@ struct TextField : DataDrivenLayoutProperty<std::string> {
static std::string defaultValue() { return ""; }
};
-struct TextFont : LayoutProperty<std::vector<std::string>> {
+struct TextFont : DataDrivenLayoutProperty<std::vector<std::string>> {
static constexpr const char * key = "text-font";
static std::vector<std::string> defaultValue() { return { "Open Sans Regular", "Arial Unicode MS Regular" }; }
};
diff --git a/src/mbgl/style/paint_property.hpp b/src/mbgl/style/paint_property.hpp
index c4c996b3bd..195eb645a9 100644
--- a/src/mbgl/style/paint_property.hpp
+++ b/src/mbgl/style/paint_property.hpp
@@ -2,6 +2,7 @@
#include <mbgl/style/properties.hpp>
#include <mbgl/style/property_value.hpp>
+#include <mbgl/style/heatmap_color_property_value.hpp>
#include <mbgl/style/data_driven_property_value.hpp>
#include <mbgl/renderer/property_evaluator.hpp>
#include <mbgl/renderer/cross_faded_property_evaluator.hpp>
@@ -48,5 +49,27 @@ public:
static constexpr bool IsDataDriven = false;
};
+/*
+ * Special-case paint property traits for heatmap-color, needed because
+ * heatmap-color values do not fit into the
+ * Undefined | Value | {Camera,Source,Composite}Function taxonomy that applies
+ * to all other paint properties.
+ *
+ * These traits are provided here--despite the fact that heatmap-color
+ * is not used like other paint properties--to allow the parameter-pack-based
+ * batch evaluation of paint properties to compile properly.
+ */
+class HeatmapColor {
+public:
+ using TransitionableType = Transitionable<HeatmapColorPropertyValue>;
+ using UnevaluatedType = Transitioning<HeatmapColorPropertyValue>;
+ using EvaluatorType = PropertyEvaluator<Color>;
+ using PossiblyEvaluatedType = Color;
+ using Type = Color;
+ static constexpr bool IsDataDriven = false;
+
+ static Color defaultValue() { return {}; }
+};
+
} // namespace style
} // namespace mbgl
diff --git a/src/mbgl/style/parser.cpp b/src/mbgl/style/parser.cpp
index a83897dbf5..8d14d7972c 100644
--- a/src/mbgl/style/parser.cpp
+++ b/src/mbgl/style/parser.cpp
@@ -1,11 +1,13 @@
#include <mbgl/style/parser.hpp>
#include <mbgl/style/layer_impl.hpp>
+#include <mbgl/style/layers/symbol_layer.hpp>
#include <mbgl/style/rapidjson_conversion.hpp>
#include <mbgl/style/conversion.hpp>
#include <mbgl/style/conversion/coordinate.hpp>
#include <mbgl/style/conversion/source.hpp>
#include <mbgl/style/conversion/layer.hpp>
#include <mbgl/style/conversion/light.hpp>
+#include <mbgl/style/conversion/transition_options.hpp>
#include <mbgl/util/logging.hpp>
#include <mbgl/util/string.hpp>
@@ -117,6 +119,9 @@ StyleParseResult Parser::parse(const std::string& json) {
}
}
+ // Call for side effect of logging warnings for invalid values.
+ fontStacks();
+
return nullptr;
}
@@ -149,7 +154,7 @@ void Parser::parseSources(const JSValue& value) {
}
for (const auto& property : value.GetObject()) {
- std::string id = *conversion::toString(property.name);
+ std::string id { property.name.GetString(), property.name.GetStringLength() };
conversion::Error error;
optional<std::unique_ptr<Source>> source =
@@ -256,7 +261,7 @@ void Parser::parseLayer(const std::string& id, const JSValue& value, std::unique
}
layer = reference->cloneRef(id);
- conversion::setPaintProperties(*layer, value);
+ conversion::setPaintProperties(*layer, conversion::Convertible(&value));
} else {
conversion::Error error;
optional<std::unique_ptr<Layer>> converted = conversion::convert<std::unique_ptr<Layer>>(value, error);
@@ -269,28 +274,32 @@ void Parser::parseLayer(const std::string& id, const JSValue& value, std::unique
}
std::vector<FontStack> Parser::fontStacks() const {
- std::set<FontStack> optional;
+ std::set<FontStack> result;
for (const auto& layer : layers) {
- if (layer->is<SymbolLayer>()) {
- PropertyValue<FontStack> textFont = layer->as<SymbolLayer>()->getTextFont();
- if (textFont.isUndefined()) {
- optional.insert({"Open Sans Regular", "Arial Unicode MS Regular"});
- } else if (textFont.isConstant()) {
- optional.insert(textFont.asConstant());
- } else if (textFont.isCameraFunction()) {
- textFont.asCameraFunction().stops.match(
- [&] (const auto& stops) {
- for (const auto& stop : stops.stops) {
- optional.insert(stop.second);
+ if (layer->is<SymbolLayer>() && !layer->as<SymbolLayer>()->getTextField().isUndefined()) {
+ layer->as<SymbolLayer>()->getTextFont().match(
+ [&] (Undefined) {
+ result.insert({"Open Sans Regular", "Arial Unicode MS Regular"});
+ },
+ [&] (const FontStack& constant) {
+ result.insert(constant);
+ },
+ [&] (const auto& function) {
+ for (const auto& value : function.possibleOutputs()) {
+ if (value) {
+ result.insert(*value);
+ } else {
+ Log::Warning(Event::ParseStyle, "Layer '%s' has an invalid value for text-font and will not work offline. Output values must be contained as literals within the expression.", layer->getID().c_str());
+ break;
}
}
- );
- }
+ }
+ );
}
}
- return std::vector<FontStack>(optional.begin(), optional.end());
+ return std::vector<FontStack>(result.begin(), result.end());
}
} // namespace style
diff --git a/src/mbgl/style/rapidjson_conversion.hpp b/src/mbgl/style/rapidjson_conversion.hpp
index 48a764ccb4..79bd9c928b 100644
--- a/src/mbgl/style/rapidjson_conversion.hpp
+++ b/src/mbgl/style/rapidjson_conversion.hpp
@@ -1,103 +1,125 @@
#pragma once
#include <mbgl/util/rapidjson.hpp>
-#include <mbgl/util/feature.hpp>
#include <mbgl/style/conversion.hpp>
+#include <mapbox/geojson.hpp>
+#include <mapbox/geojson/rapidjson.hpp>
+
namespace mbgl {
namespace style {
namespace conversion {
-inline bool isUndefined(const JSValue& value) {
- return value.IsNull();
-}
-
-inline bool isArray(const JSValue& value) {
- return value.IsArray();
-}
+template <>
+class ConversionTraits<const JSValue*> {
+public:
+ static bool isUndefined(const JSValue* value) {
+ return value->IsNull();
+ }
-inline std::size_t arrayLength(const JSValue& value) {
- return value.Size();
-}
+ static bool isArray(const JSValue* value) {
+ return value->IsArray();
+ }
-inline const JSValue& arrayMember(const JSValue& value, std::size_t i) {
- return value[rapidjson::SizeType(i)];
-}
+ static std::size_t arrayLength(const JSValue* value) {
+ return value->Size();
+ }
-inline bool isObject(const JSValue& value) {
- return value.IsObject();
-}
+ static const JSValue* arrayMember(const JSValue* value, std::size_t i) {
+ return &(*value)[rapidjson::SizeType(i)];
+ }
-inline const JSValue* objectMember(const JSValue& value, const char * name) {
- if (!value.HasMember(name)) {
- return nullptr;
+ static bool isObject(const JSValue* value) {
+ return value->IsObject();
}
- return &value[name];
-}
-template <class Fn>
-optional<Error> eachMember(const JSValue& value, Fn&& fn) {
- assert(value.IsObject());
- for (const auto& property : value.GetObject()) {
- optional<Error> result =
- fn({ property.name.GetString(), property.name.GetStringLength() }, property.value);
- if (result) {
- return result;
+ static optional<const JSValue*> objectMember(const JSValue* value, const char * name) {
+ if (!value->HasMember(name)) {
+ return optional<const JSValue*>();
}
+ const JSValue* const& member = &(*value)[name];
+ return {member};
}
- return {};
-}
-inline optional<bool> toBool(const JSValue& value) {
- if (!value.IsBool()) {
+ template <class Fn>
+ static optional<Error> eachMember(const JSValue* value, Fn&& fn) {
+ assert(value->IsObject());
+ for (const auto& property : value->GetObject()) {
+ optional<Error> result =
+ fn({ property.name.GetString(), property.name.GetStringLength() }, &property.value);
+ if (result) {
+ return result;
+ }
+ }
return {};
}
- return value.GetBool();
-}
-inline optional<float> toNumber(const JSValue& value) {
- if (!value.IsNumber()) {
- return {};
+ static optional<bool> toBool(const JSValue* value) {
+ if (!value->IsBool()) {
+ return {};
+ }
+ return value->GetBool();
}
- return value.GetDouble();
-}
-inline optional<double> toDouble(const JSValue& value) {
- if (!value.IsNumber()) {
- return {};
+ static optional<float> toNumber(const JSValue* value) {
+ if (!value->IsNumber()) {
+ return {};
+ }
+ return value->GetDouble();
}
- return value.GetDouble();
-}
-inline optional<std::string> toString(const JSValue& value) {
- if (!value.IsString()) {
- return {};
+ static optional<double> toDouble(const JSValue* value) {
+ if (!value->IsNumber()) {
+ return {};
+ }
+ return value->GetDouble();
+ }
+
+ static optional<std::string> toString(const JSValue* value) {
+ if (!value->IsString()) {
+ return {};
+ }
+ return {{ value->GetString(), value->GetStringLength() }};
}
- return {{ value.GetString(), value.GetStringLength() }};
-}
-inline optional<Value> toValue(const JSValue& value) {
- switch (value.GetType()) {
- case rapidjson::kNullType:
- case rapidjson::kFalseType:
- return { false };
+ static optional<Value> toValue(const JSValue* value) {
+ switch (value->GetType()) {
+ case rapidjson::kNullType:
+ case rapidjson::kFalseType:
+ return { false };
- case rapidjson::kTrueType:
- return { true };
+ case rapidjson::kTrueType:
+ return { true };
- case rapidjson::kStringType:
- return { std::string { value.GetString(), value.GetStringLength() } };
+ case rapidjson::kStringType:
+ return { std::string { value->GetString(), value->GetStringLength() } };
- case rapidjson::kNumberType:
- if (value.IsUint64()) return { value.GetUint64() };
- if (value.IsInt64()) return { value.GetInt64() };
- return { value.GetDouble() };
+ case rapidjson::kNumberType:
+ if (value->IsUint64()) return { value->GetUint64() };
+ if (value->IsInt64()) return { value->GetInt64() };
+ return { value->GetDouble() };
- default:
+ default:
+ return {};
+ }
+ }
+
+ static optional<GeoJSON> toGeoJSON(const JSValue* value, Error& error) {
+ try {
+ return mapbox::geojson::convert(*value);
+ } catch (const std::exception& ex) {
+ error = { ex.what() };
return {};
+ }
}
+};
+
+template <class T, class...Args>
+optional<T> convert(const JSValue& value, Error& error, Args&&...args) {
+ return convert<T>(Convertible(&value), error, std::forward<Args>(args)...);
}
} // namespace conversion
} // namespace style
} // namespace mbgl
+
diff --git a/src/mbgl/style/sources/custom_geometry_source.cpp b/src/mbgl/style/sources/custom_geometry_source.cpp
new file mode 100644
index 0000000000..b37490a5ce
--- /dev/null
+++ b/src/mbgl/style/sources/custom_geometry_source.cpp
@@ -0,0 +1,45 @@
+#include <mbgl/style/sources/custom_geometry_source.hpp>
+#include <mbgl/style/custom_tile_loader.hpp>
+#include <mbgl/style/sources/custom_geometry_source_impl.hpp>
+#include <mbgl/actor/actor.hpp>
+#include <mbgl/actor/scheduler.hpp>
+#include <mbgl/tile/tile_id.hpp>
+#include <mbgl/util/shared_thread_pool.hpp>
+#include <tuple>
+#include <map>
+
+namespace mbgl {
+namespace style {
+
+CustomGeometrySource::CustomGeometrySource(std::string id,
+ const CustomGeometrySource::Options options)
+ : Source(makeMutable<CustomGeometrySource::Impl>(std::move(id), options)),
+ loader(std::make_unique<Actor<CustomTileLoader>>(*sharedThreadPool(), options.fetchTileFunction, options.cancelTileFunction)) {
+}
+
+CustomGeometrySource::~CustomGeometrySource() = default;
+
+const CustomGeometrySource::Impl& CustomGeometrySource::impl() const {
+ return static_cast<const CustomGeometrySource::Impl&>(*baseImpl);
+}
+
+void CustomGeometrySource::loadDescription(FileSource&) {
+ baseImpl = makeMutable<CustomGeometrySource::Impl>(impl(), loader->self());
+ loaded = true;
+}
+
+void CustomGeometrySource::setTileData(const CanonicalTileID& tileID,
+ const GeoJSON& data) {
+ loader->invoke(&CustomTileLoader::setTileData, tileID, data);
+}
+
+void CustomGeometrySource::invalidateTile(const CanonicalTileID& tileID) {
+ loader->invoke(&CustomTileLoader::invalidateTile, tileID);
+}
+
+void CustomGeometrySource::invalidateRegion(const LatLngBounds& bounds) {
+ loader->invoke(&CustomTileLoader::invalidateRegion, bounds, impl().getZoomRange());
+}
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/sources/custom_geometry_source_impl.cpp b/src/mbgl/style/sources/custom_geometry_source_impl.cpp
new file mode 100644
index 0000000000..67d52bdc24
--- /dev/null
+++ b/src/mbgl/style/sources/custom_geometry_source_impl.cpp
@@ -0,0 +1,40 @@
+#include <mbgl/style/sources/custom_geometry_source_impl.hpp>
+#include <mbgl/style/source_observer.hpp>
+
+namespace mbgl {
+namespace style {
+
+CustomGeometrySource::Impl::Impl(std::string id_,
+ const CustomGeometrySource::Options options)
+ : Source::Impl(SourceType::CustomVector, std::move(id_)),
+ tileOptions(options.tileOptions),
+ zoomRange(options.zoomRange),
+ loaderRef({}) {
+}
+
+CustomGeometrySource::Impl::Impl(const Impl& impl, ActorRef<CustomTileLoader> loaderRef_)
+ : Source::Impl(impl),
+ tileOptions(impl.tileOptions),
+ zoomRange(impl.zoomRange),
+ loaderRef(loaderRef_){
+
+}
+
+optional<std::string> CustomGeometrySource::Impl::getAttribution() const {
+ return {};
+}
+
+CustomGeometrySource::TileOptions CustomGeometrySource::Impl::getTileOptions() const {
+ return tileOptions;
+}
+
+Range<uint8_t> CustomGeometrySource::Impl::getZoomRange() const {
+ return zoomRange;
+}
+
+optional<ActorRef<CustomTileLoader>> CustomGeometrySource::Impl::getTileLoader() const {
+ return loaderRef;
+}
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/sources/custom_geometry_source_impl.hpp b/src/mbgl/style/sources/custom_geometry_source_impl.hpp
new file mode 100644
index 0000000000..ce7187202d
--- /dev/null
+++ b/src/mbgl/style/sources/custom_geometry_source_impl.hpp
@@ -0,0 +1,29 @@
+#pragma once
+
+#include <mbgl/style/source_impl.hpp>
+#include <mbgl/style/sources/custom_geometry_source.hpp>
+#include <mbgl/style/custom_tile_loader.hpp>
+#include <mbgl/actor/actor_ref.hpp>
+
+namespace mbgl {
+namespace style {
+
+class CustomGeometrySource::Impl : public Source::Impl {
+public:
+ Impl(std::string id, CustomGeometrySource::Options options);
+ Impl(const Impl&, ActorRef<CustomTileLoader>);
+
+ optional<std::string> getAttribution() const final;
+
+ CustomGeometrySource::TileOptions getTileOptions() const;
+ Range<uint8_t> getZoomRange() const;
+ optional<ActorRef<CustomTileLoader>> getTileLoader() const;
+
+private:
+ CustomGeometrySource::TileOptions tileOptions;
+ Range<uint8_t> zoomRange;
+ optional<ActorRef<CustomTileLoader>> loaderRef;
+};
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/sources/raster_dem_source.cpp b/src/mbgl/style/sources/raster_dem_source.cpp
new file mode 100644
index 0000000000..dc9feb8eeb
--- /dev/null
+++ b/src/mbgl/style/sources/raster_dem_source.cpp
@@ -0,0 +1,19 @@
+#include <mbgl/style/sources/raster_dem_source.hpp>
+#include <mbgl/style/sources/raster_source_impl.hpp>
+#include <mbgl/style/source_observer.hpp>
+#include <mbgl/style/conversion/json.hpp>
+#include <mbgl/style/conversion/tileset.hpp>
+#include <mbgl/storage/file_source.hpp>
+#include <mbgl/util/mapbox.hpp>
+
+namespace mbgl {
+namespace style {
+
+RasterDEMSource::RasterDEMSource(std::string id, variant<std::string, Tileset> urlOrTileset_, uint16_t tileSize)
+ : RasterSource(std::move(id), urlOrTileset_, tileSize, SourceType::RasterDEM){
+}
+
+
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/sources/raster_source.cpp b/src/mbgl/style/sources/raster_source.cpp
index 0a0412a4ed..53f29d660b 100644
--- a/src/mbgl/style/sources/raster_source.cpp
+++ b/src/mbgl/style/sources/raster_source.cpp
@@ -9,8 +9,8 @@
namespace mbgl {
namespace style {
-RasterSource::RasterSource(std::string id, variant<std::string, Tileset> urlOrTileset_, uint16_t tileSize)
- : Source(makeMutable<Impl>(std::move(id), tileSize)),
+RasterSource::RasterSource(std::string id, variant<std::string, Tileset> urlOrTileset_, uint16_t tileSize, SourceType sourceType)
+ : Source(makeMutable<Impl>(sourceType, std::move(id), tileSize)),
urlOrTileset(std::move(urlOrTileset_)) {
}
diff --git a/src/mbgl/style/sources/raster_source_impl.cpp b/src/mbgl/style/sources/raster_source_impl.cpp
index 50dae1f07e..4db25aafd1 100644
--- a/src/mbgl/style/sources/raster_source_impl.cpp
+++ b/src/mbgl/style/sources/raster_source_impl.cpp
@@ -3,8 +3,8 @@
namespace mbgl {
namespace style {
-RasterSource::Impl::Impl(std::string id_, uint16_t tileSize_)
- : Source::Impl(SourceType::Raster, std::move(id_)),
+RasterSource::Impl::Impl(SourceType sourceType, std::string id_, uint16_t tileSize_)
+ : Source::Impl(sourceType, std::move(id_)),
tileSize(tileSize_) {
}
diff --git a/src/mbgl/style/sources/raster_source_impl.hpp b/src/mbgl/style/sources/raster_source_impl.hpp
index c41d5485b2..96f59a2159 100644
--- a/src/mbgl/style/sources/raster_source_impl.hpp
+++ b/src/mbgl/style/sources/raster_source_impl.hpp
@@ -8,7 +8,7 @@ namespace style {
class RasterSource::Impl : public Source::Impl {
public:
- Impl(std::string id, uint16_t tileSize);
+ Impl(SourceType sourceType, std::string id, uint16_t tileSize);
Impl(const Impl&, Tileset);
optional<Tileset> getTileset() const;
diff --git a/src/mbgl/style/style_impl.cpp b/src/mbgl/style/style_impl.cpp
index 3214c6316e..d330b3120a 100644
--- a/src/mbgl/style/style_impl.cpp
+++ b/src/mbgl/style/style_impl.cpp
@@ -6,9 +6,11 @@
#include <mbgl/style/layers/background_layer.hpp>
#include <mbgl/style/layers/fill_layer.hpp>
#include <mbgl/style/layers/fill_extrusion_layer.hpp>
+#include <mbgl/style/layers/heatmap_layer.hpp>
#include <mbgl/style/layers/line_layer.hpp>
#include <mbgl/style/layers/circle_layer.hpp>
#include <mbgl/style/layers/raster_layer.hpp>
+#include <mbgl/style/layers/hillshade_layer.hpp>
#include <mbgl/style/layer_impl.hpp>
#include <mbgl/style/parser.hpp>
#include <mbgl/style/transition_options.hpp>
diff --git a/src/mbgl/style/types.cpp b/src/mbgl/style/types.cpp
index 0a1781e01b..bdfa20a047 100644
--- a/src/mbgl/style/types.cpp
+++ b/src/mbgl/style/types.cpp
@@ -12,6 +12,7 @@ MBGL_DEFINE_ENUM(SourceType, {
{ SourceType::Video, "video" },
{ SourceType::Annotations, "annotations" },
{ SourceType::Image, "image" },
+ { SourceType::CustomVector, "customvector" }
});
MBGL_DEFINE_ENUM(VisibilityType, {
@@ -24,6 +25,11 @@ MBGL_DEFINE_ENUM(TranslateAnchorType, {
{ TranslateAnchorType::Viewport, "viewport" },
});
+MBGL_DEFINE_ENUM(HillshadeIlluminationAnchorType, {
+ { HillshadeIlluminationAnchorType::Map, "map" },
+ { HillshadeIlluminationAnchorType::Viewport, "viewport" },
+});
+
MBGL_DEFINE_ENUM(RotateAnchorType, {
{ RotateAnchorType::Map, "map" },
{ RotateAnchorType::Viewport, "viewport" },
diff --git a/src/mbgl/text/collision_feature.cpp b/src/mbgl/text/collision_feature.cpp
index 3eb08da8d1..6d6f2aabc7 100644
--- a/src/mbgl/text/collision_feature.cpp
+++ b/src/mbgl/text/collision_feature.cpp
@@ -13,8 +13,9 @@ CollisionFeature::CollisionFeature(const GeometryCoordinates& line,
const float padding,
const style::SymbolPlacementType placement,
IndexedSubfeature indexedFeature_,
- const AlignmentType alignment)
- : indexedFeature(std::move(indexedFeature_)) {
+ const float overscaling)
+ : indexedFeature(std::move(indexedFeature_))
+ , alongLine(placement == style::SymbolPlacementType::Line) {
if (top == 0 && bottom == 0 && left == 0 && right == 0) return;
const float y1 = top * boxScale - padding;
@@ -22,7 +23,7 @@ CollisionFeature::CollisionFeature(const GeometryCoordinates& line,
const float x1 = left * boxScale - padding;
const float x2 = right * boxScale + padding;
- if (placement == style::SymbolPlacementType::Line) {
+ if (alongLine) {
float height = y2 - y1;
const double length = x2 - x1;
@@ -31,29 +32,26 @@ CollisionFeature::CollisionFeature(const GeometryCoordinates& line,
height = std::max(10.0f * boxScale, height);
GeometryCoordinate anchorPoint = convertPoint<int16_t>(anchor.point);
-
- if (alignment == AlignmentType::Straight) {
- // used for icon labels that are aligned with the line, but don't curve along it
- const GeometryCoordinate vector = convertPoint<int16_t>(util::unit(convertPoint<double>(line[anchor.segment + 1] - line[anchor.segment])) * length);
- const GeometryCoordinates newLine({ anchorPoint - vector, anchorPoint + vector });
- bboxifyLabel(newLine, anchorPoint, 0, length, height);
- } else {
- // used for text labels that curve along a line
- bboxifyLabel(line, anchorPoint, anchor.segment, length, height);
- }
+ bboxifyLabel(line, anchorPoint, anchor.segment, length, height, overscaling);
} else {
- boxes.emplace_back(anchor.point, Point<float>{ 0, 0 }, x1, y1, x2, y2, std::numeric_limits<float>::infinity());
+ boxes.emplace_back(anchor.point, Point<float>{ 0, 0 }, x1, y1, x2, y2);
}
}
void CollisionFeature::bboxifyLabel(const GeometryCoordinates& line, GeometryCoordinate& anchorPoint,
- const int segment, const float labelLength, const float boxSize) {
+ const int segment, const float labelLength, const float boxSize, const float overscaling) {
const float step = boxSize / 2;
const int nBoxes = std::floor(labelLength / step);
- // We calculate line collision boxes out to 300% of what would normally be our
+ // We calculate line collision circles out to 300% of what would normally be our
// max size, to allow collision detection to work on labels that expand as
// they move into the distance
- const int nPitchPaddingBoxes = std::floor(nBoxes / 2);
+ // Vertically oriented labels in the distant field can extend past this padding
+ // This is a noticeable problem in overscaled tiles where the pitch 0-based
+ // symbol spacing will put labels very close together in a pitched map.
+ // To reduce the cost of adding extra collision circles, we slowly increase
+ // them for overscaled tiles.
+ const float overscalingPaddingFactor = 1 + .4 * std::log(overscaling) / std::log(2);
+ const int nPitchPaddingBoxes = std::floor(nBoxes * overscalingPaddingFactor / 2);
// offset the center of the first box by half a box so that the edge of the
// box is at the edge of the label.
@@ -124,47 +122,18 @@ void CollisionFeature::bboxifyLabel(const GeometryCoordinates& line, GeometryCoo
p0.x + segmentBoxDistance / segmentLength * (p1.x - p0.x),
p0.y + segmentBoxDistance / segmentLength * (p1.y - p0.y)
};
-
- // Distance from label anchor point to inner (towards center) edge of this box
- // The tricky thing here is that box positioning doesn't change with scale,
- // but box size does change with scale.
- // Technically, distanceToInnerEdge should be:
- // Math.max(Math.abs(boxDistanceToAnchor - firstBoxOffset) - (step / scale), 0);
- // But using that formula would make solving for maxScale more difficult, so we
- // approximate with scale=2.
- // This makes our calculation spot-on at scale=2, and on the conservative side for
- // lower scales
- const float distanceToInnerEdge = std::max(std::fabs(boxDistanceToAnchor - firstBoxOffset) - step / 2, 0.0f);
- float maxScale = util::division(labelLength / 2, distanceToInnerEdge, std::numeric_limits<float>::infinity());
-
- // The box maxScale calculations are designed to be conservative on collisions in the scale range
- // [1,2]. At scale=1, each box has 50% overlap, and at scale=2, the boxes are lined up edge
- // to edge (beyond scale 2, gaps start to appear, which could potentially allow missed collisions).
- // We add "pitch padding" boxes to the left and right to handle effective underzooming
- // (scale < 1) when labels are in the distance. The overlap approximation could cause us to use
- // these boxes when the scale is greater than 1, but we prevent that because we know
- // they're only necessary for scales less than one.
- // This preserves the pre-pitch-padding behavior for unpitched maps.
- if (i < 0 || i >= nBoxes) {
- maxScale = std::min(maxScale, 0.99f);
- }
-
- boxes.emplace_back(boxAnchor, boxAnchor - convertPoint<float>(anchorPoint), -boxSize / 2, -boxSize / 2, boxSize / 2, boxSize / 2, maxScale);
+
+ // If the box is within boxSize of the anchor, force the box to be used
+ // (so even 0-width labels use at least one box)
+ // Otherwise, the .8 multiplication gives us a little bit of conservative
+ // padding in choosing which boxes to use (see CollisionIndex#placedCollisionCircles)
+ const float paddedAnchorDistance = std::abs(boxDistanceToAnchor - firstBoxOffset) < step ?
+ 0 :
+ (boxDistanceToAnchor - firstBoxOffset) * 0.8;
+
+ boxes.emplace_back(boxAnchor, boxAnchor - convertPoint<float>(anchorPoint), -boxSize / 2, -boxSize / 2, boxSize / 2, boxSize / 2, paddedAnchorDistance, boxSize / 2);
}
}
-float CollisionBox::adjustedMaxScale(const std::array<float, 4>& rotationMatrix, const float yStretch) const {
- // When the map is pitched the distance covered by a line changes.
- // Adjust the max scale by (approximatePitchedLength / approximateRegularLength)
- // to compensate for this.
- const Point<float> rotatedOffset = util::matrixMultiply(rotationMatrix, offset);
- const float xSqr = rotatedOffset.x * rotatedOffset.x;
- const float ySqr = rotatedOffset.y * rotatedOffset.y;
- const float yStretchSqr = ySqr * yStretch * yStretch;
- const float adjustmentFactor = xSqr + ySqr != 0 ?
- std::sqrt((xSqr + yStretchSqr) / (xSqr + ySqr)) :
- 1.0f;
- return maxScale * adjustmentFactor;
-}
} // namespace mbgl
diff --git a/src/mbgl/text/collision_feature.hpp b/src/mbgl/text/collision_feature.hpp
index 3b6e461a26..df1b12819c 100644
--- a/src/mbgl/text/collision_feature.hpp
+++ b/src/mbgl/text/collision_feature.hpp
@@ -11,10 +11,8 @@ namespace mbgl {
class CollisionBox {
public:
- CollisionBox(Point<float> _anchor, Point<float> _offset, float _x1, float _y1, float _x2, float _y2, float _maxScale) :
- anchor(std::move(_anchor)), offset(_offset), x1(_x1), y1(_y1), x2(_x2), y2(_y2), maxScale(_maxScale) {}
-
- float adjustedMaxScale(const std::array<float, 4>& rotationMatrix, const float yStretch) const;
+ CollisionBox(Point<float> _anchor, Point<float> _offset, float _x1, float _y1, float _x2, float _y2, float _signedDistanceFromAnchor = 0, float _radius = 0) :
+ anchor(std::move(_anchor)), offset(_offset), x1(_x1), y1(_y1), x2(_x2), y2(_y2), used(true), signedDistanceFromAnchor(_signedDistanceFromAnchor), radius(_radius) {}
// the box is centered around the anchor point
Point<float> anchor;
@@ -28,20 +26,23 @@ public:
float x2;
float y2;
- // the box is only valid for scales < maxScale.
- // The box does not block other boxes at scales >= maxScale;
- float maxScale;
+ // Projected box geometry: generated/updated at placement time
+ float px1;
+ float py1;
+ float px2;
+ float py2;
+
+ // Projected circle geometry: generated/updated at placement time
+ float px;
+ float py;
+ bool used;
- // the scale at which the label can first be shown
- float placementScale = 0.0f;
+ float signedDistanceFromAnchor;
+ float radius;
};
class CollisionFeature {
public:
- enum class AlignmentType : bool {
- Straight = false,
- Curved
- };
// for text
CollisionFeature(const GeometryCoordinates& line,
@@ -50,23 +51,31 @@ public:
const float boxScale,
const float padding,
const style::SymbolPlacementType placement,
- const IndexedSubfeature& indexedFeature_)
- : CollisionFeature(line, anchor, shapedText.top, shapedText.bottom, shapedText.left, shapedText.right, boxScale, padding, placement, indexedFeature_, AlignmentType::Curved) {}
+ const IndexedSubfeature& indexedFeature_,
+ const float overscaling)
+ : CollisionFeature(line, anchor, shapedText.top, shapedText.bottom, shapedText.left, shapedText.right, boxScale, padding, placement, indexedFeature_, overscaling) {}
// for icons
+ // Icons collision features are always SymbolPlacementType::Point, which means the collision feature
+ // will be viewport-rotation-aligned even if the icon is map-rotation-aligned (e.g. `icon-rotation-alignment: map`
+ // _or_ `symbol-placement: line`). We're relying on most icons being "close enough" to square that having
+ // incorrect rotation alignment doesn't throw off collision detection too much.
+ // See: https://github.com/mapbox/mapbox-gl-js/issues/4861
CollisionFeature(const GeometryCoordinates& line,
const Anchor& anchor,
optional<PositionedIcon> shapedIcon,
const float boxScale,
const float padding,
- const style::SymbolPlacementType placement,
const IndexedSubfeature& indexedFeature_)
: CollisionFeature(line, anchor,
(shapedIcon ? shapedIcon->top() : 0),
(shapedIcon ? shapedIcon->bottom() : 0),
(shapedIcon ? shapedIcon->left() : 0),
(shapedIcon ? shapedIcon->right() : 0),
- boxScale, padding, placement, indexedFeature_, AlignmentType::Straight) {}
+ boxScale,
+ padding,
+ style::SymbolPlacementType::Point,
+ indexedFeature_, 1) {}
CollisionFeature(const GeometryCoordinates& line,
const Anchor&,
@@ -78,14 +87,15 @@ public:
const float padding,
const style::SymbolPlacementType,
IndexedSubfeature,
- const AlignmentType);
+ const float overscaling);
std::vector<CollisionBox> boxes;
IndexedSubfeature indexedFeature;
+ bool alongLine;
private:
void bboxifyLabel(const GeometryCoordinates& line, GeometryCoordinate& anchorPoint,
- const int segment, const float length, const float height);
+ const int segment, const float length, const float height, const float overscaling);
};
} // namespace mbgl
diff --git a/src/mbgl/text/collision_index.cpp b/src/mbgl/text/collision_index.cpp
new file mode 100644
index 0000000000..833658c33e
--- /dev/null
+++ b/src/mbgl/text/collision_index.cpp
@@ -0,0 +1,364 @@
+#include <mbgl/text/collision_index.hpp>
+#include <mbgl/layout/symbol_instance.hpp>
+#include <mbgl/geometry/feature_index.hpp>
+#include <mbgl/math/log2.hpp>
+#include <mbgl/util/constants.hpp>
+#include <mbgl/util/math.hpp>
+#include <mbgl/math/minmax.hpp>
+#include <mbgl/util/intersection_tests.hpp>
+#include <mbgl/layout/symbol_projection.hpp>
+
+#include <mapbox/geometry/envelope.hpp>
+
+#include <mbgl/renderer/buckets/symbol_bucket.hpp> // For PlacedSymbol: pull out to another location
+
+#include <cmath>
+
+namespace mbgl {
+
+// When a symbol crosses the edge that causes it to be included in
+// collision detection, it will cause changes in the symbols around
+// it. This constant specifies how many pixels to pad the edge of
+// the viewport for collision detection so that the bulk of the changes
+// occur offscreen. Making this constant greater increases label
+// stability, but it's expensive.
+static const float viewportPadding = 100;
+
+CollisionIndex::CollisionIndex(const TransformState& transformState_)
+ : transformState(transformState_)
+ , collisionGrid(transformState.getSize().width + 2 * viewportPadding, transformState.getSize().height + 2 * viewportPadding, 25)
+ , ignoredGrid(transformState.getSize().width + 2 * viewportPadding, transformState.getSize().height + 2 * viewportPadding, 25)
+ , screenRightBoundary(transformState.getSize().width + viewportPadding)
+ , screenBottomBoundary(transformState.getSize().height + viewportPadding)
+ , gridRightBoundary(transformState.getSize().width + 2 * viewportPadding)
+ , gridBottomBoundary(transformState.getSize().height + 2 * viewportPadding)
+ , pitchFactor(std::cos(transformState.getPitch()) * transformState.getCameraToCenterDistance())
+{}
+
+float CollisionIndex::approximateTileDistance(const TileDistance& tileDistance, const float lastSegmentAngle, const float pixelsToTileUnits, const float cameraToAnchorDistance, const bool pitchWithMap) {
+ // This is a quick and dirty solution for chosing which collision circles to use (since collision circles are
+ // laid out in tile units). Ideally, I think we should generate collision circles on the fly in viewport coordinates
+ // at the time we do collision detection.
+
+ // incidenceStretch is the ratio of how much y space a label takes up on a tile while drawn perpendicular to the viewport vs
+ // how much space it would take up if it were drawn flat on the tile
+ // Using law of sines, camera_to_anchor/sin(ground_angle) = camera_to_center/sin(incidence_angle)
+ // Incidence angle 90 -> head on, sin(incidence_angle) = 1, no stretch
+ // Incidence angle 1 -> very oblique, sin(incidence_angle) =~ 0, lots of stretch
+ // ground_angle = u_pitch + PI/2 -> sin(ground_angle) = cos(u_pitch)
+ // incidenceStretch = 1 / sin(incidenceAngle)
+
+ const float incidenceStretch = pitchWithMap ? 1 : cameraToAnchorDistance / pitchFactor;
+ const float lastSegmentTile = tileDistance.lastSegmentViewportDistance * pixelsToTileUnits;
+ return tileDistance.prevTileDistance +
+ lastSegmentTile +
+ (incidenceStretch - 1) * lastSegmentTile * std::abs(std::sin(lastSegmentAngle));
+}
+
+bool CollisionIndex::isOffscreen(const CollisionBox& box) const {
+ return box.px2 < viewportPadding || box.px1 >= screenRightBoundary || box.py2 < viewportPadding || box.py1 >= screenBottomBoundary;
+}
+
+bool CollisionIndex::isInsideGrid(const CollisionBox& box) const {
+ return box.px2 >= 0 && box.px1 < gridRightBoundary && box.py2 >= 0 && box.py1 < gridBottomBoundary;
+}
+
+
+std::pair<bool,bool> CollisionIndex::placeFeature(CollisionFeature& feature,
+ const mat4& posMatrix,
+ const mat4& labelPlaneMatrix,
+ const float textPixelRatio,
+ PlacedSymbol& symbol,
+ const float scale,
+ const float fontSize,
+ const bool allowOverlap,
+ const bool pitchWithMap,
+ const bool collisionDebug) {
+ if (!feature.alongLine) {
+ CollisionBox& box = feature.boxes.front();
+ const auto projectedPoint = projectAndGetPerspectiveRatio(posMatrix, box.anchor);
+ const float tileToViewport = textPixelRatio * projectedPoint.second;
+ box.px1 = box.x1 * tileToViewport + projectedPoint.first.x;
+ box.py1 = box.y1 * tileToViewport + projectedPoint.first.y;
+ box.px2 = box.x2 * tileToViewport + projectedPoint.first.x;
+ box.py2 = box.y2 * tileToViewport + projectedPoint.first.y;
+
+ if (!isInsideGrid(box) ||
+ (!allowOverlap && collisionGrid.hitTest({{ box.px1, box.py1 }, { box.px2, box.py2 }}))) {
+ return { false, false };
+ }
+
+ return {true, isOffscreen(box)};
+ } else {
+ return placeLineFeature(feature, posMatrix, labelPlaneMatrix, textPixelRatio, symbol, scale, fontSize, allowOverlap, pitchWithMap, collisionDebug);
+ }
+}
+
+std::pair<bool,bool> CollisionIndex::placeLineFeature(CollisionFeature& feature,
+ const mat4& posMatrix,
+ const mat4& labelPlaneMatrix,
+ const float textPixelRatio,
+ PlacedSymbol& symbol,
+ const float scale,
+ const float fontSize,
+ const bool allowOverlap,
+ const bool pitchWithMap,
+ const bool collisionDebug) {
+
+ const auto tileUnitAnchorPoint = symbol.anchorPoint;
+ const auto projectedAnchor = projectAnchor(posMatrix, tileUnitAnchorPoint);
+
+ const float fontScale = fontSize / 24;
+ const float lineOffsetX = symbol.lineOffset[0] * fontSize;
+ const float lineOffsetY = symbol.lineOffset[1] * fontSize;
+
+ const auto labelPlaneAnchorPoint = project(tileUnitAnchorPoint, labelPlaneMatrix).first;
+
+ const auto firstAndLastGlyph = placeFirstAndLastGlyph(
+ fontScale,
+ lineOffsetX,
+ lineOffsetY,
+ /*flip*/ false,
+ labelPlaneAnchorPoint,
+ tileUnitAnchorPoint,
+ symbol,
+ labelPlaneMatrix,
+ /*return tile distance*/ true);
+
+ bool collisionDetected = false;
+ bool inGrid = false;
+ bool entirelyOffscreen = true;
+
+ const auto tileToViewport = projectedAnchor.first * textPixelRatio;
+ // pixelsToTileUnits is used for translating line geometry to tile units
+ // ... so we care about 'scale' but not 'perspectiveRatio'
+ // equivalent to pixel_to_tile_units
+ const auto pixelsToTileUnits = 1 / (textPixelRatio * scale);
+
+ float firstTileDistance = 0, lastTileDistance = 0;
+ if (firstAndLastGlyph) {
+ firstTileDistance = approximateTileDistance(*(firstAndLastGlyph->first.tileDistance), firstAndLastGlyph->first.angle, pixelsToTileUnits, projectedAnchor.second, pitchWithMap);
+ lastTileDistance = approximateTileDistance(*(firstAndLastGlyph->second.tileDistance), firstAndLastGlyph->second.angle, pixelsToTileUnits, projectedAnchor.second, pitchWithMap);
+ }
+
+ bool atLeastOneCirclePlaced = false;
+ for (size_t i = 0; i < feature.boxes.size(); i++) {
+ CollisionBox& circle = feature.boxes[i];
+ const float boxSignedDistanceFromAnchor = circle.signedDistanceFromAnchor;
+ if (!firstAndLastGlyph ||
+ (boxSignedDistanceFromAnchor < -firstTileDistance) ||
+ (boxSignedDistanceFromAnchor > lastTileDistance)) {
+ // The label either doesn't fit on its line or we
+ // don't need to use this circle because the label
+ // doesn't extend this far. Either way, mark the circle unused.
+ circle.used = false;
+ continue;
+ }
+
+ const auto projectedPoint = projectPoint(posMatrix, circle.anchor);
+ const float tileUnitRadius = (circle.x2 - circle.x1) / 2;
+ const float radius = tileUnitRadius * tileToViewport;
+
+ if (atLeastOneCirclePlaced) {
+ const CollisionBox& previousCircle = feature.boxes[i - 1];
+ const float dx = projectedPoint.x - previousCircle.px;
+ const float dy = projectedPoint.y - previousCircle.py;
+ // The circle edges touch when the distance between their centers is 2x the radius
+ // When the distance is 1x the radius, they're doubled up, and we could remove
+ // every other circle while keeping them all in touch.
+ // We actually start removing circles when the distance is √2x the radius:
+ // thinning the number of circles as much as possible is a major performance win,
+ // and the small gaps introduced don't make a very noticeable difference.
+ const bool placedTooDensely = radius * radius * 2 > dx * dx + dy * dy;
+ if (placedTooDensely) {
+ const bool atLeastOneMoreCircle = (i + 1) < feature.boxes.size();
+ if (atLeastOneMoreCircle) {
+ const CollisionBox& nextCircle = feature.boxes[i + 1];
+ const float nextBoxDistanceFromAnchor = nextCircle.signedDistanceFromAnchor;
+ if ((nextBoxDistanceFromAnchor > -firstTileDistance) &&
+ (nextBoxDistanceFromAnchor < lastTileDistance)) {
+ // Hide significantly overlapping circles, unless this is the last one we can
+ // use, in which case we want to keep it in place even if it's tightly packed
+ // with the one before it.
+ circle.used = false;
+ continue;
+ }
+ }
+ }
+ }
+
+ atLeastOneCirclePlaced = true;
+ circle.px1 = projectedPoint.x - radius;
+ circle.px2 = projectedPoint.x + radius;
+ circle.py1 = projectedPoint.y - radius;
+ circle.py2 = projectedPoint.y + radius;
+
+ circle.used = true;
+
+ circle.px = projectedPoint.x;
+ circle.py = projectedPoint.y;
+ circle.radius = radius;
+
+ entirelyOffscreen &= isOffscreen(circle);
+ inGrid |= isInsideGrid(circle);
+
+ if (!allowOverlap) {
+ if (collisionGrid.hitTest({{circle.px, circle.py}, circle.radius})) {
+ if (!collisionDebug) {
+ return {false, false};
+ } else {
+ // Don't early exit if we're showing the debug circles because we still want to calculate
+ // which circles are in use
+ collisionDetected = true;
+ }
+ }
+ }
+ }
+
+ return {!collisionDetected && firstAndLastGlyph && inGrid, entirelyOffscreen};
+}
+
+
+void CollisionIndex::insertFeature(CollisionFeature& feature, bool ignorePlacement) {
+ if (feature.alongLine) {
+ for (auto& circle : feature.boxes) {
+ if (!circle.used) {
+ continue;
+ }
+
+ if (ignorePlacement) {
+ ignoredGrid.insert(IndexedSubfeature(feature.indexedFeature), {{ circle.px, circle.py }, circle.radius});
+ } else {
+ collisionGrid.insert(IndexedSubfeature(feature.indexedFeature), {{ circle.px, circle.py }, circle.radius});
+ }
+ }
+ } else {
+ assert(feature.boxes.size() == 1);
+ auto& box = feature.boxes[0];
+ if (ignorePlacement) {
+ ignoredGrid.insert(IndexedSubfeature(feature.indexedFeature), {{ box.px1, box.py1 }, { box.px2, box.py2 }});
+ } else {
+ collisionGrid.insert(IndexedSubfeature(feature.indexedFeature), {{ box.px1, box.py1 }, { box.px2, box.py2 }});
+ }
+ }
+}
+
+bool polygonIntersectsBox(const LineString<float>& polygon, const GridIndex<IndexedSubfeature>::BBox& bbox) {
+ // This is just a wrapper that allows us to use the integer-based util::polygonIntersectsPolygon
+ // Conversion limits our query accuracy to single-pixel resolution
+ GeometryCoordinates integerPolygon;
+ for (const auto& point : polygon) {
+ integerPolygon.push_back(convertPoint<int16_t>(point));
+ }
+ int16_t minX1 = bbox.min.x;
+ int16_t maxY1 = bbox.max.y;
+ int16_t minY1 = bbox.min.y;
+ int16_t maxX1 = bbox.max.x;
+
+ auto bboxPoints = GeometryCoordinates {
+ { minX1, minY1 }, { maxX1, minY1 }, { maxX1, maxY1 }, { minX1, maxY1 }
+ };
+
+ return util::polygonIntersectsPolygon(integerPolygon, bboxPoints);
+}
+
+std::vector<IndexedSubfeature> CollisionIndex::queryRenderedSymbols(const GeometryCoordinates& queryGeometry, const UnwrappedTileID& tileID, const std::string& sourceID) const {
+ std::vector<IndexedSubfeature> result;
+ if (queryGeometry.empty() || (collisionGrid.empty() && ignoredGrid.empty())) {
+ return result;
+ }
+
+ mat4 posMatrix;
+ mat4 projMatrix;
+ transformState.getProjMatrix(projMatrix);
+ transformState.matrixFor(posMatrix, tileID);
+ matrix::multiply(posMatrix, projMatrix, posMatrix);
+
+ // queryGeometry is specified in integer tile units, but in projecting we switch to float pixels
+ LineString<float> projectedQuery;
+ for (const auto& point : queryGeometry) {
+ auto projected = projectPoint(posMatrix, convertPoint<float>(point));
+ projectedQuery.push_back(projected);
+ }
+
+ auto envelope = mapbox::geometry::envelope(projectedQuery);
+
+ using QueryResult = std::pair<IndexedSubfeature, GridIndex<IndexedSubfeature>::BBox>;
+
+ std::vector<QueryResult> thisTileFeatures;
+ std::vector<QueryResult> features = collisionGrid.queryWithBoxes(envelope);
+
+ for (auto& queryResult : features) {
+ auto& feature = queryResult.first;
+ if (feature.sourceID == sourceID && feature.tileID == tileID.canonical) {
+ // We only have to filter on the canonical ID because even if the feature is showing multiple times
+ // we treat it as one feature.
+ thisTileFeatures.push_back(queryResult);
+ }
+ }
+
+ std::vector<QueryResult> ignoredFeatures = ignoredGrid.queryWithBoxes(envelope);
+ for (auto& queryResult : ignoredFeatures) {
+ auto& feature = queryResult.first;
+ if (feature.sourceID == sourceID && feature.tileID == tileID.canonical) {
+ thisTileFeatures.push_back(queryResult);
+ }
+ }
+
+ std::unordered_map<std::string, std::unordered_map<std::string, std::unordered_set<std::size_t>>> sourceLayerFeatures;
+ for (auto& queryResult : thisTileFeatures) {
+ auto& feature = queryResult.first;
+ auto& bbox = queryResult.second;
+
+ // Skip already seen features.
+ auto& seenFeatures = sourceLayerFeatures[feature.sourceLayerName][feature.bucketName];
+ if (seenFeatures.find(feature.index) != seenFeatures.end())
+ continue;
+
+ seenFeatures.insert(feature.index);
+
+ if (!polygonIntersectsBox(projectedQuery, bbox)) {
+ continue;
+ }
+
+ result.push_back(feature);
+ }
+
+ return result;
+
+}
+
+std::pair<float,float> CollisionIndex::projectAnchor(const mat4& posMatrix, const Point<float>& point) const {
+ vec4 p = {{ point.x, point.y, 0, 1 }};
+ matrix::transformMat4(p, p, posMatrix);
+ return std::make_pair(
+ 0.5 + 0.5 * (transformState.getCameraToCenterDistance() / p[3]),
+ p[3]
+ );
+}
+
+std::pair<Point<float>,float> CollisionIndex::projectAndGetPerspectiveRatio(const mat4& posMatrix, const Point<float>& point) const {
+ vec4 p = {{ point.x, point.y, 0, 1 }};
+ matrix::transformMat4(p, p, posMatrix);
+ return std::make_pair(
+ Point<float>(
+ (((p[0] / p[3] + 1) / 2) * transformState.getSize().width) + viewportPadding,
+ (((-p[1] / p[3] + 1) / 2) * transformState.getSize().height) + viewportPadding
+ ),
+ // See perspective ratio comment in symbol_sdf.vertex
+ // We're doing collision detection in viewport space so we need
+ // to scale down boxes in the distance
+ 0.5 + 0.5 * (transformState.getCameraToCenterDistance() / p[3])
+ );
+}
+
+Point<float> CollisionIndex::projectPoint(const mat4& posMatrix, const Point<float>& point) const {
+ vec4 p = {{ point.x, point.y, 0, 1 }};
+ matrix::transformMat4(p, p, posMatrix);
+ return Point<float>(
+ (((p[0] / p[3] + 1) / 2) * transformState.getSize().width) + viewportPadding,
+ (((-p[1] / p[3] + 1) / 2) * transformState.getSize().height) + viewportPadding
+ );
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/text/collision_index.hpp b/src/mbgl/text/collision_index.hpp
new file mode 100644
index 0000000000..8653c1d76c
--- /dev/null
+++ b/src/mbgl/text/collision_index.hpp
@@ -0,0 +1,70 @@
+#pragma once
+
+#include <mbgl/geometry/feature_index.hpp>
+#include <mbgl/text/collision_feature.hpp>
+#include <mbgl/util/grid_index.hpp>
+#include <mbgl/map/transform_state.hpp>
+
+namespace mbgl {
+
+class PlacedSymbol;
+
+struct TileDistance;
+
+class CollisionIndex {
+public:
+ using CollisionGrid = GridIndex<IndexedSubfeature>;
+
+ explicit CollisionIndex(const TransformState&);
+
+ std::pair<bool,bool> placeFeature(CollisionFeature& feature,
+ const mat4& posMatrix,
+ const mat4& labelPlaneMatrix,
+ const float textPixelRatio,
+ PlacedSymbol& symbol,
+ const float scale,
+ const float fontSize,
+ const bool allowOverlap,
+ const bool pitchWithMap,
+ const bool collisionDebug);
+
+ void insertFeature(CollisionFeature& feature, bool ignorePlacement);
+
+ std::vector<IndexedSubfeature> queryRenderedSymbols(const GeometryCoordinates&, const UnwrappedTileID& tileID, const std::string& sourceID) const;
+
+
+private:
+ bool isOffscreen(const CollisionBox&) const;
+ bool isInsideGrid(const CollisionBox&) const;
+
+ std::pair<bool,bool> placeLineFeature(CollisionFeature& feature,
+ const mat4& posMatrix,
+ const mat4& labelPlaneMatrix,
+ const float textPixelRatio,
+ PlacedSymbol& symbol,
+ const float scale,
+ const float fontSize,
+ const bool allowOverlap,
+ const bool pitchWithMap,
+ const bool collisionDebug);
+
+ float approximateTileDistance(const TileDistance& tileDistance, const float lastSegmentAngle, const float pixelsToTileUnits, const float cameraToAnchorDistance, const bool pitchWithMap);
+
+ std::pair<float,float> projectAnchor(const mat4& posMatrix, const Point<float>& point) const;
+ std::pair<Point<float>,float> projectAndGetPerspectiveRatio(const mat4& posMatrix, const Point<float>& point) const;
+ Point<float> projectPoint(const mat4& posMatrix, const Point<float>& point) const;
+
+ const TransformState transformState;
+
+ CollisionGrid collisionGrid;
+ CollisionGrid ignoredGrid;
+
+ const float screenRightBoundary;
+ const float screenBottomBoundary;
+ const float gridRightBoundary;
+ const float gridBottomBoundary;
+
+ const float pitchFactor;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/text/collision_tile.cpp b/src/mbgl/text/collision_tile.cpp
deleted file mode 100644
index cc9b602f08..0000000000
--- a/src/mbgl/text/collision_tile.cpp
+++ /dev/null
@@ -1,267 +0,0 @@
-#include <mbgl/text/collision_tile.hpp>
-#include <mbgl/geometry/feature_index.hpp>
-#include <mbgl/math/log2.hpp>
-#include <mbgl/util/constants.hpp>
-#include <mbgl/util/math.hpp>
-#include <mbgl/math/minmax.hpp>
-#include <mbgl/util/intersection_tests.hpp>
-
-#include <mapbox/geometry/envelope.hpp>
-#include <mapbox/geometry/multi_point.hpp>
-
-#include <cmath>
-
-namespace mbgl {
-
-CollisionTile::CollisionTile(PlacementConfig config_) : config(std::move(config_)) {
- // Compute the transformation matrix.
- const float angle_sin = std::sin(config.angle);
- const float angle_cos = std::cos(config.angle);
- rotationMatrix = { { angle_cos, -angle_sin, angle_sin, angle_cos } };
- reverseRotationMatrix = { { angle_cos, angle_sin, -angle_sin, angle_cos } };
-
- perspectiveRatio =
- 1.0f +
- 0.5f * (util::division(config.cameraToTileDistance, config.cameraToCenterDistance, 1.0f) -
- 1.0f);
-
- minScale /= perspectiveRatio;
- maxScale /= perspectiveRatio;
-
- // We can only approximate here based on the y position of the tile
- // The shaders calculate a more accurate "incidence_stretch"
- // at render time to calculate an effective scale for collision
- // purposes, but we still want to use the yStretch approximation
- // here because we can't adjust the aspect ratio of the collision
- // boxes at render time.
- yStretch = util::max(
- 1.0f, util::division(config.cameraToTileDistance,
- config.cameraToCenterDistance * std::cos(config.pitch), 1.0f));
-}
-
-float CollisionTile::findPlacementScale(const Point<float>& anchor, const CollisionBox& box, const float boxMaxScale, const Point<float>& blockingAnchor, const CollisionBox& blocking) {
- float minPlacementScale = minScale;
-
- // Find the lowest scale at which the two boxes can fit side by side without overlapping.
- // Original algorithm:
-
- const float s1 = util::division(blocking.x1 - box.x2, anchor.x - blockingAnchor.x,
- 1.0f); // scale at which new box is to the left of old box
- const float s2 = util::division(blocking.x2 - box.x1, anchor.x - blockingAnchor.x,
- 1.0f); // scale at which new box is to the right of old box
- const float s3 = util::division((blocking.y1 - box.y2) * yStretch, anchor.y - blockingAnchor.y,
- 1.0f); // scale at which new box is to the top of old box
- const float s4 = util::division((blocking.y2 - box.y1) * yStretch, anchor.y - blockingAnchor.y,
- 1.0f); // scale at which new box is to the bottom of old box
-
- float collisionFreeScale = util::min(util::max(s1, s2), util::max(s3, s4));
-
- if (collisionFreeScale > blocking.maxScale) {
- // After a box's maxScale the label has shrunk enough that the box is no longer needed to cover it,
- // so unblock the new box at the scale that the old box disappears.
- collisionFreeScale = blocking.maxScale;
- }
-
- if (collisionFreeScale > boxMaxScale) {
- // If the box can only be shown after it is visible, then the box can never be shown.
- // But the label can be shown after this box is not visible.
- collisionFreeScale = boxMaxScale;
- }
-
- if (collisionFreeScale > minPlacementScale &&
- collisionFreeScale >= blocking.placementScale) {
- // If this collision occurs at a lower scale than previously found collisions
- // and the collision occurs while the other label is visible
-
- // this this is the lowest scale at which the label won't collide with anything
- minPlacementScale = collisionFreeScale;
- }
-
- return minPlacementScale;
-}
-
-float CollisionTile::placeFeature(const CollisionFeature& feature, bool allowOverlap, bool avoidEdges) {
- static const float infinity = std::numeric_limits<float>::infinity();
- static const std::array<CollisionBox, 4> edges {{
- // left
- CollisionBox(Point<float>(0, 0), { 0, 0 }, 0, -infinity, 0, infinity, infinity),
- // right
- CollisionBox(Point<float>(util::EXTENT, 0), { 0, 0 }, 0, -infinity, 0, infinity, infinity),
- // top
- CollisionBox(Point<float>(0, 0), { 0, 0 }, -infinity, 0, infinity, 0, infinity),
- // bottom
- CollisionBox(Point<float>(0, util::EXTENT), { 0, 0 }, -infinity, 0, infinity, 0, infinity)
- }};
-
- float minPlacementScale = minScale;
-
- for (auto& box : feature.boxes) {
- const auto anchor = util::matrixMultiply(rotationMatrix, box.anchor);
-
- const float boxMaxScale = box.adjustedMaxScale(rotationMatrix, yStretch);
-
- if (!allowOverlap) {
- for (auto it = tree.qbegin(bgi::intersects(getTreeBox(anchor, box))); it != tree.qend(); ++it) {
- const CollisionBox& blocking = std::get<1>(*it);
- Point<float> blockingAnchor = util::matrixMultiply(rotationMatrix, blocking.anchor);
-
- minPlacementScale = util::max(minPlacementScale, findPlacementScale(anchor, box, boxMaxScale, blockingAnchor, blocking));
- if (minPlacementScale >= maxScale) return minPlacementScale;
- }
- }
-
- if (avoidEdges) {
- const Point<float> rtl = util::matrixMultiply(reverseRotationMatrix, { box.x1, box.y1 });
- const Point<float> rtr = util::matrixMultiply(reverseRotationMatrix, { box.x2, box.y1 });
- const Point<float> rbl = util::matrixMultiply(reverseRotationMatrix, { box.x1, box.y2 });
- const Point<float> rbr = util::matrixMultiply(reverseRotationMatrix, { box.x2, box.y2 });
- CollisionBox rotatedBox(box.anchor,
- box.offset,
- util::min(rtl.x, rtr.x, rbl.x, rbr.x),
- util::min(rtl.y, rtr.y, rbl.y, rbr.y),
- util::max(rtl.x, rtr.x, rbl.x, rbr.x),
- util::max(rtl.y, rtr.y, rbl.y, rbr.y),
- boxMaxScale);
-
- for (auto& blocking : edges) {
- minPlacementScale = util::max(minPlacementScale, findPlacementScale(box.anchor, rotatedBox, boxMaxScale, blocking.anchor, blocking));
- if (minPlacementScale >= maxScale) return minPlacementScale;
- }
- }
- }
-
- return minPlacementScale;
-}
-
-void CollisionTile::insertFeature(CollisionFeature& feature, float minPlacementScale, bool ignorePlacement) {
- for (auto& box : feature.boxes) {
- box.placementScale = minPlacementScale;
- }
-
- if (minPlacementScale < maxScale) {
- std::vector<CollisionTreeBox> treeBoxes;
- for (auto& box : feature.boxes) {
- CollisionBox adjustedBox = box;
- box.maxScale = box.adjustedMaxScale(rotationMatrix, yStretch);
- treeBoxes.emplace_back(getTreeBox(util::matrixMultiply(rotationMatrix, box.anchor), box), std::move(adjustedBox), feature.indexedFeature);
- }
- if (ignorePlacement) {
- ignoredTree.insert(treeBoxes.begin(), treeBoxes.end());
- } else {
- tree.insert(treeBoxes.begin(), treeBoxes.end());
- }
- }
-
-}
-
-// +---------------------------+ As you zoom, the size of the symbol changes
-// |(x1,y1) | | relative to the tile e.g. when zooming in,
-// | | | the symbol gets smaller relative to the tile.
-// | (x1',y1') v |
-// | +-------+-------+ | The boxes inserted into the tree represents
-// | | | | | the bounds at the integer zoom level (where
-// | | | | | the symbol is biggest relative to the tile).
-// | | | | |
-// |---->+-------+-------+<----| This happens because placement is updated
-// | | |(xa,ya)| | once every new integer zoom level e.g.
-// | | | | | std::floor(oldZoom) != std::floor(newZoom).
-// | | | | |
-// | +-------+-------+ | Thus, they don't represent the exact bounds
-// | ^ (x2',y2') | of the symbol at the current zoom level. For
-// | | | calculating the bounds at current zoom level
-// | | (x2,y2)| we must unscale the box using its center as
-// +---------------------------+ transform origin.
-Box CollisionTile::getTreeBox(const Point<float>& anchor, const CollisionBox& box, const float scale) {
- assert(box.x1 <= box.x2 && box.y1 <= box.y2);
- return Box{
- // When the 'perspectiveRatio' is high, we're effectively underzooming
- // the tile because it's in the distance.
- // In order to detect collisions that only happen while underzoomed,
- // we have to query a larger portion of the grid.
- // This extra work is offset by having a lower 'maxScale' bound
- // Note that this adjustment ONLY affects the bounding boxes
- // in the grid. It doesn't affect the boxes used for the
- // minPlacementScale calculations.
- CollisionPoint{
- anchor.x + box.x1 / scale * perspectiveRatio,
- anchor.y + box.y1 / scale * yStretch * perspectiveRatio,
- },
- CollisionPoint{
- anchor.x + box.x2 / scale * perspectiveRatio,
- anchor.y + box.y2 / scale * yStretch * perspectiveRatio
- }
- };
-}
-
-std::vector<IndexedSubfeature> CollisionTile::queryRenderedSymbols(const GeometryCoordinates& queryGeometry, float scale) const {
- std::vector<IndexedSubfeature> result;
- if (queryGeometry.empty() || (tree.empty() && ignoredTree.empty())) {
- return result;
- }
-
- // Generate a rotated geometry out of the original query geometry.
- // Scale has already been handled by the prior conversions.
- GeometryCoordinates polygon;
- for (const auto& point : queryGeometry) {
- auto rotated = util::matrixMultiply(rotationMatrix, convertPoint<float>(point));
- polygon.push_back(convertPoint<int16_t>(rotated));
- }
-
- // Predicate for ruling out already seen features.
- std::unordered_map<std::string, std::unordered_set<std::size_t>> sourceLayerFeatures;
- auto seenFeature = [&] (const CollisionTreeBox& treeBox) -> bool {
- const IndexedSubfeature& feature = std::get<2>(treeBox);
- const auto& seenFeatures = sourceLayerFeatures[feature.sourceLayerName];
- return seenFeatures.find(feature.index) == seenFeatures.end();
- };
-
- // "perspectiveRatio" is a tile-based approximation of how much larger symbols will
- // be in the distance. It won't line up exactly with the actually rendered symbols
- // Being exact would require running the collision detection logic in symbol_sdf.vertex
- // in the CPU
- const float perspectiveScale = scale / perspectiveRatio;
-
- // Account for the rounding done when updating symbol shader variables.
- const float roundedScale = std::pow(2.0f, std::ceil(util::log2(perspectiveScale) * 10.0f) / 10.0f);
-
- // Check if feature is rendered (collision free) at current scale.
- auto visibleAtScale = [&] (const CollisionTreeBox& treeBox) -> bool {
- const CollisionBox& box = std::get<1>(treeBox);
- return roundedScale >= box.placementScale && roundedScale <= box.adjustedMaxScale(rotationMatrix, yStretch);
- };
-
- // Check if query polygon intersects with the feature box at current scale.
- auto intersectsAtScale = [&] (const CollisionTreeBox& treeBox) -> bool {
- const CollisionBox& collisionBox = std::get<1>(treeBox);
- const auto anchor = util::matrixMultiply(rotationMatrix, collisionBox.anchor);
-
- const int16_t x1 = anchor.x + (collisionBox.x1 / perspectiveScale);
- const int16_t y1 = anchor.y + (collisionBox.y1 / perspectiveScale) * yStretch;
- const int16_t x2 = anchor.x + (collisionBox.x2 / perspectiveScale);
- const int16_t y2 = anchor.y + (collisionBox.y2 / perspectiveScale) * yStretch;
- auto bbox = GeometryCoordinates {
- { x1, y1 }, { x2, y1 }, { x2, y2 }, { x1, y2 }
- };
- return util::polygonIntersectsPolygon(polygon, bbox);
- };
-
- auto predicates = bgi::satisfies(seenFeature)
- && bgi::satisfies(visibleAtScale)
- && bgi::satisfies(intersectsAtScale);
-
- auto queryTree = [&](const auto& tree_) {
- for (auto it = tree_.qbegin(predicates); it != tree_.qend(); ++it) {
- const IndexedSubfeature& feature = std::get<2>(*it);
- auto& seenFeatures = sourceLayerFeatures[feature.sourceLayerName];
- seenFeatures.insert(feature.index);
- result.push_back(feature);
- }
- };
-
- queryTree(tree);
- queryTree(ignoredTree);
-
- return result;
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/text/collision_tile.hpp b/src/mbgl/text/collision_tile.hpp
deleted file mode 100644
index 9868266aa2..0000000000
--- a/src/mbgl/text/collision_tile.hpp
+++ /dev/null
@@ -1,71 +0,0 @@
-#pragma once
-
-#include <mbgl/text/collision_feature.hpp>
-#include <mbgl/text/placement_config.hpp>
-#include <mbgl/tile/geometry_tile_data.hpp>
-
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunused-function"
-#pragma GCC diagnostic ignored "-Wunused-parameter"
-#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wshadow"
-#ifdef __clang__
-#pragma GCC diagnostic ignored "-Wunknown-pragmas"
-#endif
-#pragma GCC diagnostic ignored "-Wpragmas"
-#pragma GCC diagnostic ignored "-Wdeprecated-register"
-#pragma GCC diagnostic ignored "-Wshorten-64-to-32"
-#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
-#ifndef __clang__
-#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
-#pragma GCC diagnostic ignored "-Wmisleading-indentation"
-#endif
-#include <boost/geometry.hpp>
-#include <boost/geometry/geometries/point.hpp>
-#include <boost/geometry/geometries/box.hpp>
-#include <boost/geometry/index/rtree.hpp>
-#pragma GCC diagnostic pop
-
-namespace mbgl {
-
-namespace bg = boost::geometry;
-namespace bgm = bg::model;
-namespace bgi = bg::index;
-using CollisionPoint = bgm::point<float, 2, bg::cs::cartesian>;
-using Box = bgm::box<CollisionPoint>;
-using CollisionTreeBox = std::tuple<Box, CollisionBox, IndexedSubfeature>;
-using Tree = bgi::rtree<CollisionTreeBox, bgi::linear<16, 4>>;
-
-class IndexedSubfeature;
-
-class CollisionTile {
-public:
- explicit CollisionTile(PlacementConfig);
-
- float placeFeature(const CollisionFeature&, bool allowOverlap, bool avoidEdges);
- void insertFeature(CollisionFeature&, float minPlacementScale, bool ignorePlacement);
-
- std::vector<IndexedSubfeature> queryRenderedSymbols(const GeometryCoordinates&, float scale) const;
-
- const PlacementConfig config;
-
- float minScale = 0.5f;
- float maxScale = 2.0f;
- float yStretch;
-
- std::array<float, 4> rotationMatrix;
- std::array<float, 4> reverseRotationMatrix;
-
-private:
- float findPlacementScale(
- const Point<float>& anchor, const CollisionBox& box, const float boxMaxScale,
- const Point<float>& blockingAnchor, const CollisionBox& blocking);
- Box getTreeBox(const Point<float>& anchor, const CollisionBox& box, const float scale = 1.0);
-
- Tree tree;
- Tree ignoredTree;
-
- float perspectiveRatio;
-};
-
-} // namespace mbgl
diff --git a/src/mbgl/text/cross_tile_symbol_index.cpp b/src/mbgl/text/cross_tile_symbol_index.cpp
new file mode 100644
index 0000000000..f88bab9d6f
--- /dev/null
+++ b/src/mbgl/text/cross_tile_symbol_index.cpp
@@ -0,0 +1,189 @@
+#include <mbgl/text/cross_tile_symbol_index.hpp>
+#include <mbgl/layout/symbol_instance.hpp>
+#include <mbgl/renderer/buckets/symbol_bucket.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/tile/tile.hpp>
+
+namespace mbgl {
+
+
+TileLayerIndex::TileLayerIndex(OverscaledTileID coord_, std::vector<SymbolInstance>& symbolInstances, uint32_t bucketInstanceId_)
+ : coord(coord_), bucketInstanceId(bucketInstanceId_) {
+ for (SymbolInstance& symbolInstance : symbolInstances) {
+ indexedSymbolInstances[symbolInstance.key].emplace_back(symbolInstance.crossTileID, getScaledCoordinates(symbolInstance, coord));
+ }
+ }
+
+Point<int64_t> TileLayerIndex::getScaledCoordinates(SymbolInstance& symbolInstance, const OverscaledTileID& childTileCoord) {
+ // Round anchor positions to roughly 4 pixel grid
+ const double roundingFactor = 512.0 / util::EXTENT / 2.0;
+ const double scale = roundingFactor / std::pow(2, childTileCoord.canonical.z - coord.canonical.z);
+ return {
+ static_cast<int64_t>(std::floor((childTileCoord.canonical.x * util::EXTENT + symbolInstance.anchor.point.x) * scale)),
+ static_cast<int64_t>(std::floor((childTileCoord.canonical.y * util::EXTENT + symbolInstance.anchor.point.y) * scale))
+ };
+}
+
+void TileLayerIndex::findMatches(std::vector<SymbolInstance>& symbolInstances, const OverscaledTileID& newCoord, std::set<uint32_t>& zoomCrossTileIDs) {
+ float tolerance = coord.canonical.z < newCoord.canonical.z ? 1 : std::pow(2, coord.canonical.z - newCoord.canonical.z);
+
+ for (auto& symbolInstance : symbolInstances) {
+ if (symbolInstance.crossTileID) {
+ // already has a match, skip
+ continue;
+ }
+
+ auto it = indexedSymbolInstances.find(symbolInstance.key);
+ if (it == indexedSymbolInstances.end()) {
+ // No symbol with this key in this bucket
+ continue;
+ }
+
+ auto scaledSymbolCoord = getScaledCoordinates(symbolInstance, newCoord);
+
+ for (IndexedSymbolInstance& thisTileSymbol: it->second) {
+ // Return any symbol with the same keys whose coordinates are within 1
+ // grid unit. (with a 4px grid, this covers a 12px by 12px area)
+ if (std::abs(thisTileSymbol.coord.x - scaledSymbolCoord.x) <= tolerance &&
+ std::abs(thisTileSymbol.coord.y - scaledSymbolCoord.y) <= tolerance &&
+ zoomCrossTileIDs.find(thisTileSymbol.crossTileID) == zoomCrossTileIDs.end()) {
+ // Once we've marked ourselves duplicate against this parent symbol,
+ // don't let any other symbols at the same zoom level duplicate against
+ // the same parent (see issue #10844)
+ zoomCrossTileIDs.insert(thisTileSymbol.crossTileID);
+ symbolInstance.crossTileID = thisTileSymbol.crossTileID;
+ break;
+ }
+ }
+ }
+}
+
+CrossTileSymbolLayerIndex::CrossTileSymbolLayerIndex() {
+}
+
+bool CrossTileSymbolLayerIndex::addBucket(const OverscaledTileID& tileID, SymbolBucket& bucket, uint32_t& maxCrossTileID) {
+ auto thisZoomIndexes = indexes[tileID.overscaledZ];
+ auto previousIndex = thisZoomIndexes.find(tileID);
+ if (previousIndex != thisZoomIndexes.end()) {
+ if (previousIndex->second.bucketInstanceId == bucket.bucketInstanceId) {
+ return false;
+ } else {
+ // We're replacing this bucket with an updated version
+ // Remove the old bucket's "used crossTileIDs" now so that the new bucket can claim them.
+ // We have to keep the old index entries themselves until the end of 'addBucket' so
+ // that we can copy them with 'findMatches'.
+ removeBucketCrossTileIDs(tileID.overscaledZ, previousIndex->second);
+ }
+ }
+
+ for (auto& symbolInstance: bucket.symbolInstances) {
+ symbolInstance.crossTileID = 0;
+ }
+
+ for (auto& it : indexes) {
+ auto zoom = it.first;
+ auto zoomIndexes = it.second;
+ if (zoom > tileID.overscaledZ) {
+ for (auto& childIndex : zoomIndexes) {
+ if (childIndex.second.coord.isChildOf(tileID)) {
+ childIndex.second.findMatches(bucket.symbolInstances, tileID, usedCrossTileIDs[tileID.overscaledZ]);
+ }
+ }
+ } else {
+ auto parentTileID = tileID.scaledTo(zoom);
+ auto parentIndex = zoomIndexes.find(parentTileID);
+ if (parentIndex != zoomIndexes.end()) {
+ parentIndex->second.findMatches(bucket.symbolInstances, tileID, usedCrossTileIDs[tileID.overscaledZ]);
+ }
+ }
+ }
+
+ for (auto& symbolInstance : bucket.symbolInstances) {
+ if (!symbolInstance.crossTileID) {
+ // symbol did not match any known symbol, assign a new id
+ symbolInstance.crossTileID = ++maxCrossTileID;
+ usedCrossTileIDs[tileID.overscaledZ].insert(symbolInstance.crossTileID);
+ }
+ }
+
+
+ indexes[tileID.overscaledZ].erase(tileID);
+ indexes[tileID.overscaledZ].emplace(tileID, TileLayerIndex(tileID, bucket.symbolInstances, bucket.bucketInstanceId));
+ return true;
+}
+
+void CrossTileSymbolLayerIndex::removeBucketCrossTileIDs(uint8_t zoom, const TileLayerIndex& removedBucket) {
+ for (auto key : removedBucket.indexedSymbolInstances) {
+ for (auto indexedSymbolInstance : key.second) {
+ usedCrossTileIDs[zoom].erase(indexedSymbolInstance.crossTileID);
+ }
+ }
+}
+
+bool CrossTileSymbolLayerIndex::removeStaleBuckets(const std::unordered_set<uint32_t>& currentIDs) {
+ bool tilesChanged = false;
+ for (auto& zoomIndexes : indexes) {
+ for (auto it = zoomIndexes.second.begin(); it != zoomIndexes.second.end();) {
+ if (!currentIDs.count(it->second.bucketInstanceId)) {
+ removeBucketCrossTileIDs(zoomIndexes.first, it->second);
+ it = zoomIndexes.second.erase(it);
+ tilesChanged = true;
+ } else {
+ ++it;
+ }
+ }
+ }
+ return tilesChanged;
+}
+
+CrossTileSymbolIndex::CrossTileSymbolIndex() {}
+
+bool CrossTileSymbolIndex::addLayer(RenderSymbolLayer& symbolLayer) {
+
+ auto& layerIndex = layerIndexes[symbolLayer.getID()];
+
+ bool symbolBucketsChanged = false;
+ std::unordered_set<uint32_t> currentBucketIDs;
+
+ for (RenderTile& renderTile : symbolLayer.renderTiles) {
+ if (!renderTile.tile.isRenderable()) {
+ continue;
+ }
+
+ auto bucket = renderTile.tile.getBucket(*symbolLayer.baseImpl);
+ assert(dynamic_cast<SymbolBucket*>(bucket));
+ SymbolBucket& symbolBucket = *reinterpret_cast<SymbolBucket*>(bucket);
+
+ if (!symbolBucket.bucketInstanceId) {
+ symbolBucket.bucketInstanceId = ++maxBucketInstanceId;
+ }
+
+ const bool bucketAdded = layerIndex.addBucket(renderTile.tile.id, symbolBucket, maxCrossTileID);
+ symbolBucketsChanged = symbolBucketsChanged || bucketAdded;
+ currentBucketIDs.insert(symbolBucket.bucketInstanceId);
+ }
+
+ if (layerIndex.removeStaleBuckets(currentBucketIDs)) {
+ symbolBucketsChanged = true;
+ }
+ return symbolBucketsChanged;
+}
+
+void CrossTileSymbolIndex::pruneUnusedLayers(const std::set<std::string>& usedLayers) {
+ std::vector<std::string> unusedLayers;
+ for (auto layerIndex : layerIndexes) {
+ if (usedLayers.find(layerIndex.first) == usedLayers.end()) {
+ unusedLayers.push_back(layerIndex.first);
+ }
+ }
+ for (auto unusedLayer : unusedLayers) {
+ layerIndexes.erase(unusedLayer);
+ }
+}
+
+void CrossTileSymbolIndex::reset() {
+ layerIndexes.clear();
+}
+
+} // namespace mbgl
+
diff --git a/src/mbgl/text/cross_tile_symbol_index.hpp b/src/mbgl/text/cross_tile_symbol_index.hpp
new file mode 100644
index 0000000000..541c2e3661
--- /dev/null
+++ b/src/mbgl/text/cross_tile_symbol_index.hpp
@@ -0,0 +1,69 @@
+#pragma once
+
+#include <mbgl/tile/tile_id.hpp>
+#include <mbgl/util/geometry.hpp>
+#include <mbgl/util/constants.hpp>
+#include <mbgl/util/optional.hpp>
+
+#include <map>
+#include <set>
+#include <vector>
+#include <string>
+#include <memory>
+#include <unordered_set>
+
+namespace mbgl {
+
+class SymbolInstance;
+class RenderSymbolLayer;
+class SymbolBucket;
+
+class IndexedSymbolInstance {
+public:
+ IndexedSymbolInstance(uint32_t crossTileID_, Point<int64_t> coord_)
+ : crossTileID(crossTileID_), coord(coord_)
+ {}
+
+ uint32_t crossTileID;
+ Point<int64_t> coord;
+};
+
+class TileLayerIndex {
+public:
+ TileLayerIndex(OverscaledTileID coord, std::vector<SymbolInstance>&, uint32_t bucketInstanceId);
+
+ Point<int64_t> getScaledCoordinates(SymbolInstance&, const OverscaledTileID&);
+ void findMatches(std::vector<SymbolInstance>&, const OverscaledTileID&, std::set<uint32_t>&);
+
+ OverscaledTileID coord;
+ uint32_t bucketInstanceId;
+ std::map<std::u16string,std::vector<IndexedSymbolInstance>> indexedSymbolInstances;
+};
+
+class CrossTileSymbolLayerIndex {
+public:
+ CrossTileSymbolLayerIndex();
+ bool addBucket(const OverscaledTileID&, SymbolBucket&, uint32_t& maxCrossTileID);
+ bool removeStaleBuckets(const std::unordered_set<uint32_t>& currentIDs);
+private:
+ void removeBucketCrossTileIDs(uint8_t zoom, const TileLayerIndex& removedBucket);
+
+ std::map<uint8_t, std::map<OverscaledTileID,TileLayerIndex>> indexes;
+ std::map<uint8_t, std::set<uint32_t>> usedCrossTileIDs;
+};
+
+class CrossTileSymbolIndex {
+public:
+ CrossTileSymbolIndex();
+
+ bool addLayer(RenderSymbolLayer&);
+ void pruneUnusedLayers(const std::set<std::string>&);
+
+ void reset();
+private:
+ std::map<std::string, CrossTileSymbolLayerIndex> layerIndexes;
+ uint32_t maxCrossTileID = 0;
+ uint32_t maxBucketInstanceId = 0;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/text/glyph.hpp b/src/mbgl/text/glyph.hpp
index 6cccb72ebe..2c03da308a 100644
--- a/src/mbgl/text/glyph.hpp
+++ b/src/mbgl/text/glyph.hpp
@@ -13,6 +13,7 @@
#include <vector>
#include <string>
#include <map>
+#include <set>
namespace mbgl {
@@ -75,10 +76,10 @@ class Shaping {
explicit Shaping(float x, float y, WritingModeType writingMode_)
: top(y), bottom(y), left(x), right(x), writingMode(writingMode_) {}
std::vector<PositionedGlyph> positionedGlyphs;
- int32_t top = 0;
- int32_t bottom = 0;
- int32_t left = 0;
- int32_t right = 0;
+ float top = 0;
+ float bottom = 0;
+ float left = 0;
+ float right = 0;
WritingModeType writingMode;
explicit operator bool() const { return !positionedGlyphs.empty(); }
diff --git a/src/mbgl/text/glyph_manager.cpp b/src/mbgl/text/glyph_manager.cpp
index 59b019b547..3130418908 100644
--- a/src/mbgl/text/glyph_manager.cpp
+++ b/src/mbgl/text/glyph_manager.cpp
@@ -44,8 +44,9 @@ void GlyphManager::getGlyphs(GlyphRequestor& requestor, GlyphDependencies glyphD
for (const auto& range : ranges) {
auto it = entry.ranges.find(range);
if (it == entry.ranges.end() || !it->second.parsed) {
- GlyphRequest& request = requestRange(entry, fontStack, range);
+ GlyphRequest& request = entry.ranges[range];
request.requestors[&requestor] = dependencies;
+ requestRange(request, fontStack, range);
}
}
}
@@ -63,17 +64,14 @@ Glyph GlyphManager::generateLocalSDF(const FontStack& fontStack, GlyphID glyphID
return local;
}
-GlyphManager::GlyphRequest& GlyphManager::requestRange(Entry& entry, const FontStack& fontStack, const GlyphRange& range) {
- GlyphRequest& request = entry.ranges[range];
+void GlyphManager::requestRange(GlyphRequest& request, const FontStack& fontStack, const GlyphRange& range) {
if (request.req) {
- return request;
+ return;
}
request.req = fileSource.request(Resource::glyphs(glyphURL, fontStack, range), [this, fontStack, range](Response res) {
processResponse(res, fontStack, range);
});
-
- return request;
}
void GlyphManager::processResponse(const Response& res, const FontStack& fontStack, const GlyphRange& range) {
diff --git a/src/mbgl/text/glyph_manager.hpp b/src/mbgl/text/glyph_manager.hpp
index ccc8d7e16e..84db2c4be5 100644
--- a/src/mbgl/text/glyph_manager.hpp
+++ b/src/mbgl/text/glyph_manager.hpp
@@ -61,7 +61,7 @@ private:
std::unordered_map<FontStack, Entry, FontStackHash> entries;
- GlyphRequest& requestRange(Entry&, const FontStack&, const GlyphRange&);
+ void requestRange(GlyphRequest&, const FontStack&, const GlyphRange&);
void processResponse(const Response&, const FontStack&, const GlyphRange&);
void notify(GlyphRequestor&, const GlyphDependencies&);
diff --git a/src/mbgl/text/placement.cpp b/src/mbgl/text/placement.cpp
new file mode 100644
index 0000000000..fc24c78902
--- /dev/null
+++ b/src/mbgl/text/placement.cpp
@@ -0,0 +1,341 @@
+#include <mbgl/text/placement.hpp>
+#include <mbgl/renderer/render_layer.hpp>
+#include <mbgl/renderer/layers/render_symbol_layer.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/tile/geometry_tile.hpp>
+#include <mbgl/renderer/buckets/symbol_bucket.hpp>
+#include <mbgl/renderer/bucket.hpp>
+
+namespace mbgl {
+
+OpacityState::OpacityState(bool placed_, bool skipFade)
+ : opacity((skipFade && placed_) ? 1 : 0)
+ , placed(placed_)
+{
+}
+
+OpacityState::OpacityState(const OpacityState& prevState, float increment, bool placed_) :
+ opacity(::fmax(0, ::fmin(1, prevState.opacity + (prevState.placed ? increment : -increment)))),
+ placed(placed_) {}
+
+bool OpacityState::isHidden() const {
+ return opacity == 0 && !placed;
+}
+
+JointOpacityState::JointOpacityState(bool placedText, bool placedIcon, bool skipFade) :
+ icon(OpacityState(placedIcon, skipFade)),
+ text(OpacityState(placedText, skipFade)) {}
+
+JointOpacityState::JointOpacityState(const JointOpacityState& prevOpacityState, float increment, bool placedText, bool placedIcon) :
+ icon(OpacityState(prevOpacityState.icon, increment, placedIcon)),
+ text(OpacityState(prevOpacityState.text, increment, placedText)) {}
+
+bool JointOpacityState::isHidden() const {
+ return icon.isHidden() && text.isHidden();
+}
+
+Placement::Placement(const TransformState& state_, MapMode mapMode_)
+ : collisionIndex(state_)
+ , state(state_)
+ , mapMode(mapMode_)
+ , recentUntil(TimePoint::min())
+{}
+
+void Placement::placeLayer(RenderSymbolLayer& symbolLayer, const mat4& projMatrix, bool showCollisionBoxes) {
+
+ std::unordered_set<uint32_t> seenCrossTileIDs;
+
+ for (RenderTile& renderTile : symbolLayer.renderTiles) {
+ if (!renderTile.tile.isRenderable()) {
+ continue;
+ }
+
+ auto bucket = renderTile.tile.getBucket(*symbolLayer.baseImpl);
+ assert(dynamic_cast<SymbolBucket*>(bucket));
+ SymbolBucket& symbolBucket = *reinterpret_cast<SymbolBucket*>(bucket);
+
+ auto& layout = symbolBucket.layout;
+
+ const float pixelsToTileUnits = renderTile.id.pixelsToTileUnits(1, state.getZoom());
+
+ const float scale = std::pow(2, state.getZoom() - renderTile.tile.id.overscaledZ);
+ const float textPixelRatio = (util::tileSize * renderTile.tile.id.overscaleFactor()) / util::EXTENT;
+
+ mat4 posMatrix;
+ state.matrixFor(posMatrix, renderTile.id);
+ matrix::multiply(posMatrix, projMatrix, posMatrix);
+
+ mat4 textLabelPlaneMatrix = getLabelPlaneMatrix(posMatrix,
+ layout.get<style::TextPitchAlignment>() == style::AlignmentType::Map,
+ layout.get<style::TextRotationAlignment>() == style::AlignmentType::Map,
+ state,
+ pixelsToTileUnits);
+
+ mat4 iconLabelPlaneMatrix = getLabelPlaneMatrix(posMatrix,
+ layout.get<style::IconPitchAlignment>() == style::AlignmentType::Map,
+ layout.get<style::IconRotationAlignment>() == style::AlignmentType::Map,
+ state,
+ pixelsToTileUnits);
+
+ placeLayerBucket(symbolBucket, posMatrix, textLabelPlaneMatrix, iconLabelPlaneMatrix, scale, textPixelRatio, showCollisionBoxes, seenCrossTileIDs, renderTile.tile.holdForFade());
+ }
+}
+
+void Placement::placeLayerBucket(
+ SymbolBucket& bucket,
+ const mat4& posMatrix,
+ const mat4& textLabelPlaneMatrix,
+ const mat4& iconLabelPlaneMatrix,
+ const float scale,
+ const float textPixelRatio,
+ const bool showCollisionBoxes,
+ std::unordered_set<uint32_t>& seenCrossTileIDs,
+ const bool holdingForFade) {
+
+ 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) {
+ if (holdingForFade) {
+ // Mark all symbols from this tile as "not placed", but don't add to seenCrossTileIDs, because we don't
+ // know yet if we have a duplicate in a parent tile that _should_ be placed.
+ placements.emplace(symbolInstance.crossTileID, JointPlacement(false, false, false));
+ continue;
+ }
+
+ bool placeText = false;
+ bool placeIcon = false;
+ bool offscreen = true;
+
+ if (symbolInstance.placedTextIndex) {
+ PlacedSymbol& placedSymbol = bucket.text.placedSymbols.at(*symbolInstance.placedTextIndex);
+ const float fontSize = evaluateSizeForFeature(partiallyEvaluatedTextSize, placedSymbol);
+
+ auto placed = collisionIndex.placeFeature(symbolInstance.textCollisionFeature,
+ posMatrix, textLabelPlaneMatrix, textPixelRatio,
+ placedSymbol, scale, fontSize,
+ bucket.layout.get<style::TextAllowOverlap>(),
+ bucket.layout.get<style::TextPitchAlignment>() == style::AlignmentType::Map,
+ showCollisionBoxes);
+ placeText = placed.first;
+ offscreen &= placed.second;
+ }
+
+ if (symbolInstance.placedIconIndex) {
+ PlacedSymbol& placedSymbol = bucket.icon.placedSymbols.at(*symbolInstance.placedIconIndex);
+ const float fontSize = evaluateSizeForFeature(partiallyEvaluatedIconSize, placedSymbol);
+
+ auto placed = collisionIndex.placeFeature(symbolInstance.iconCollisionFeature,
+ posMatrix, iconLabelPlaneMatrix, textPixelRatio,
+ placedSymbol, scale, fontSize,
+ bucket.layout.get<style::IconAllowOverlap>(),
+ bucket.layout.get<style::IconPitchAlignment>() == style::AlignmentType::Map,
+ showCollisionBoxes);
+ placeIcon = placed.first;
+ offscreen &= placed.second;
+ }
+
+ // combine placements for icon and text
+ if (!iconWithoutText && !textWithoutIcon) {
+ placeText = placeIcon = placeText && placeIcon;
+ } else if (!textWithoutIcon) {
+ placeText = placeText && placeIcon;
+ } else if (!iconWithoutText) {
+ placeIcon = placeText && placeIcon;
+ }
+
+ if (placeText) {
+ collisionIndex.insertFeature(symbolInstance.textCollisionFeature, bucket.layout.get<style::TextIgnorePlacement>());
+ }
+
+ if (placeIcon) {
+ collisionIndex.insertFeature(symbolInstance.iconCollisionFeature, bucket.layout.get<style::IconIgnorePlacement>());
+ }
+
+ assert(symbolInstance.crossTileID != 0);
+
+ if (placements.find(symbolInstance.crossTileID) != placements.end()) {
+ // If there's a previous placement with this ID, it comes from a tile that's fading out
+ // Erase it so that the placement result from the non-fading tile supersedes it
+ placements.erase(symbolInstance.crossTileID);
+ }
+
+ placements.emplace(symbolInstance.crossTileID, JointPlacement(placeText, placeIcon, offscreen || bucket.justReloaded));
+ seenCrossTileIDs.insert(symbolInstance.crossTileID);
+ }
+ }
+
+ bucket.justReloaded = false;
+}
+
+bool Placement::commit(const Placement& prevPlacement, TimePoint now) {
+ commitTime = now;
+
+ bool placementChanged = false;
+
+ float increment = mapMode == MapMode::Continuous ?
+ std::chrono::duration<float>(commitTime - prevPlacement.commitTime) / Duration(std::chrono::milliseconds(300)) :
+ 1.0;
+
+ // add the opacities from the current placement, and copy their current values from the previous placement
+ for (auto& jointPlacement : placements) {
+ auto prevOpacity = prevPlacement.opacities.find(jointPlacement.first);
+ if (prevOpacity != prevPlacement.opacities.end()) {
+ opacities.emplace(jointPlacement.first, JointOpacityState(prevOpacity->second, increment, jointPlacement.second.text, jointPlacement.second.icon));
+ placementChanged = placementChanged ||
+ jointPlacement.second.icon != prevOpacity->second.icon.placed ||
+ jointPlacement.second.text != prevOpacity->second.text.placed;
+ } else {
+ opacities.emplace(jointPlacement.first, JointOpacityState(jointPlacement.second.text, jointPlacement.second.icon, jointPlacement.second.skipFade));
+ placementChanged = placementChanged || jointPlacement.second.icon || jointPlacement.second.text;
+ }
+ }
+
+ // copy and update values from the previous placement that aren't in the current placement but haven't finished fading
+ for (auto& prevOpacity : prevPlacement.opacities) {
+ if (opacities.find(prevOpacity.first) == opacities.end()) {
+ JointOpacityState jointOpacity(prevOpacity.second, increment, false, false);
+ if (!jointOpacity.isHidden()) {
+ opacities.emplace(prevOpacity.first, jointOpacity);
+ placementChanged = placementChanged || prevOpacity.second.icon.placed || prevOpacity.second.text.placed;
+ }
+ }
+ }
+
+ return placementChanged;
+}
+
+void Placement::updateLayerOpacities(RenderSymbolLayer& symbolLayer) {
+ std::set<uint32_t> seenCrossTileIDs;
+ for (RenderTile& renderTile : symbolLayer.renderTiles) {
+ if (!renderTile.tile.isRenderable()) {
+ continue;
+ }
+
+ auto bucket = renderTile.tile.getBucket(*symbolLayer.baseImpl);
+ assert(dynamic_cast<SymbolBucket*>(bucket));
+ SymbolBucket& symbolBucket = *reinterpret_cast<SymbolBucket*>(bucket);
+ updateBucketOpacities(symbolBucket, seenCrossTileIDs);
+ }
+}
+
+void Placement::updateBucketOpacities(SymbolBucket& bucket, std::set<uint32_t>& seenCrossTileIDs) {
+ if (bucket.hasTextData()) bucket.text.opacityVertices.clear();
+ if (bucket.hasIconData()) bucket.icon.opacityVertices.clear();
+ if (bucket.hasCollisionBoxData()) bucket.collisionBox.dynamicVertices.clear();
+ if (bucket.hasCollisionCircleData()) bucket.collisionCircle.dynamicVertices.clear();
+
+ JointOpacityState duplicateOpacityState(false, false, true);
+
+ JointOpacityState defaultOpacityState(
+ bucket.layout.get<style::TextAllowOverlap>(),
+ bucket.layout.get<style::IconAllowOverlap>(),
+ true);
+
+ for (SymbolInstance& symbolInstance : bucket.symbolInstances) {
+ bool isDuplicate = seenCrossTileIDs.count(symbolInstance.crossTileID) > 0;
+
+ auto it = opacities.find(symbolInstance.crossTileID);
+ auto opacityState = defaultOpacityState;
+ if (isDuplicate) {
+ opacityState = duplicateOpacityState;
+ } else if (it != opacities.end()) {
+ opacityState = it->second;
+ }
+
+ if (it == opacities.end()) {
+ opacities.emplace(symbolInstance.crossTileID, defaultOpacityState);
+ }
+
+ seenCrossTileIDs.insert(symbolInstance.crossTileID);
+
+ if (symbolInstance.hasText) {
+ auto opacityVertex = SymbolOpacityAttributes::vertex(opacityState.text.placed, opacityState.text.opacity);
+ for (size_t i = 0; i < symbolInstance.horizontalGlyphQuads.size() * 4; i++) {
+ bucket.text.opacityVertices.emplace_back(opacityVertex);
+ }
+ for (size_t i = 0; i < symbolInstance.verticalGlyphQuads.size() * 4; i++) {
+ bucket.text.opacityVertices.emplace_back(opacityVertex);
+ }
+ if (symbolInstance.placedTextIndex) {
+ bucket.text.placedSymbols[*symbolInstance.placedTextIndex].hidden = opacityState.isHidden();
+ }
+ if (symbolInstance.placedVerticalTextIndex) {
+ bucket.text.placedSymbols[*symbolInstance.placedVerticalTextIndex].hidden = opacityState.isHidden();
+ }
+ }
+ if (symbolInstance.hasIcon) {
+ auto opacityVertex = SymbolOpacityAttributes::vertex(opacityState.icon.placed, opacityState.icon.opacity);
+ if (symbolInstance.iconQuad) {
+ bucket.icon.opacityVertices.emplace_back(opacityVertex);
+ bucket.icon.opacityVertices.emplace_back(opacityVertex);
+ bucket.icon.opacityVertices.emplace_back(opacityVertex);
+ bucket.icon.opacityVertices.emplace_back(opacityVertex);
+ }
+ if (symbolInstance.placedIconIndex) {
+ bucket.icon.placedSymbols[*symbolInstance.placedIconIndex].hidden = opacityState.isHidden();
+ }
+ }
+
+ auto updateCollisionBox = [&](const auto& feature, const bool placed) {
+ for (const CollisionBox& box : feature.boxes) {
+ if (feature.alongLine) {
+ auto dynamicVertex = CollisionBoxDynamicAttributes::vertex(placed, !box.used);
+ bucket.collisionCircle.dynamicVertices.emplace_back(dynamicVertex);
+ bucket.collisionCircle.dynamicVertices.emplace_back(dynamicVertex);
+ bucket.collisionCircle.dynamicVertices.emplace_back(dynamicVertex);
+ bucket.collisionCircle.dynamicVertices.emplace_back(dynamicVertex);
+ } else {
+ auto dynamicVertex = CollisionBoxDynamicAttributes::vertex(placed, false);
+ bucket.collisionBox.dynamicVertices.emplace_back(dynamicVertex);
+ bucket.collisionBox.dynamicVertices.emplace_back(dynamicVertex);
+ bucket.collisionBox.dynamicVertices.emplace_back(dynamicVertex);
+ bucket.collisionBox.dynamicVertices.emplace_back(dynamicVertex);
+ }
+ }
+ };
+ updateCollisionBox(symbolInstance.textCollisionFeature, opacityState.text.placed);
+ updateCollisionBox(symbolInstance.iconCollisionFeature, opacityState.icon.placed);
+ }
+
+ bucket.updateOpacity();
+ bucket.sortFeatures(state.getAngle());
+}
+
+float Placement::symbolFadeChange(TimePoint now) const {
+ if (mapMode == MapMode::Continuous) {
+ return std::chrono::duration<float>(now - commitTime) / Duration(std::chrono::milliseconds(300));
+ } else {
+ return 1.0;
+ }
+}
+
+bool Placement::hasTransitions(TimePoint now) const {
+ return symbolFadeChange(now) < 1.0 || stale;
+}
+
+bool Placement::stillRecent(TimePoint now) const {
+ return mapMode == MapMode::Continuous && recentUntil > now;
+}
+void Placement::setRecent(TimePoint now) {
+ stale = false;
+ if (mapMode == MapMode::Continuous) {
+ // Only set in continuous mode because "now" isn't defined in still mode
+ recentUntil = now + Duration(std::chrono::milliseconds(300));
+ }
+}
+
+void Placement::setStale() {
+ stale = true;
+}
+
+const CollisionIndex& Placement::getCollisionIndex() const {
+ return collisionIndex;
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/text/placement.hpp b/src/mbgl/text/placement.hpp
new file mode 100644
index 0000000000..653ae352ed
--- /dev/null
+++ b/src/mbgl/text/placement.hpp
@@ -0,0 +1,90 @@
+#pragma once
+
+#include <string>
+#include <unordered_map>
+#include <mbgl/util/chrono.hpp>
+#include <mbgl/text/collision_index.hpp>
+#include <mbgl/layout/symbol_projection.hpp>
+#include <unordered_set>
+
+namespace mbgl {
+
+class RenderSymbolLayer;
+class SymbolBucket;
+
+class OpacityState {
+public:
+ OpacityState(bool placed, bool skipFade);
+ OpacityState(const OpacityState& prevOpacityState, float increment, bool placed);
+ bool isHidden() const;
+ float opacity;
+ bool placed;
+};
+
+class JointOpacityState {
+public:
+ JointOpacityState(bool placedIcon, bool placedText, bool skipFade);
+ JointOpacityState(const JointOpacityState& prevOpacityState, float increment, bool placedIcon, bool placedText);
+ bool isHidden() const;
+ OpacityState icon;
+ OpacityState text;
+};
+
+class JointPlacement {
+public:
+ JointPlacement(bool text_, bool icon_, bool skipFade_)
+ : text(text_), icon(icon_), skipFade(skipFade_)
+ {}
+
+ const bool text;
+ const bool icon;
+ // skipFade = outside viewport, but within CollisionIndex::viewportPadding px of the edge
+ // Because these symbols aren't onscreen yet, we can skip the "fade in" animation,
+ // and if a subsequent viewport change brings them into view, they'll be fully
+ // visible right away.
+ const bool skipFade;
+};
+
+class Placement {
+public:
+ Placement(const TransformState&, MapMode mapMode);
+ void placeLayer(RenderSymbolLayer&, const mat4&, bool showCollisionBoxes);
+ bool commit(const Placement& prevPlacement, TimePoint);
+ void updateLayerOpacities(RenderSymbolLayer&);
+ float symbolFadeChange(TimePoint now) const;
+ bool hasTransitions(TimePoint now) const;
+
+ const CollisionIndex& getCollisionIndex() const;
+
+ bool stillRecent(TimePoint now) const;
+ void setRecent(TimePoint now);
+ void setStale();
+private:
+
+ void placeLayerBucket(
+ SymbolBucket&,
+ const mat4& posMatrix,
+ const mat4& textLabelPlaneMatrix,
+ const mat4& iconLabelPlaneMatrix,
+ const float scale,
+ const float pixelRatio,
+ const bool showCollisionBoxes,
+ std::unordered_set<uint32_t>& seenCrossTileIDs,
+ const bool holdingForFade);
+
+ void updateBucketOpacities(SymbolBucket&, std::set<uint32_t>&);
+
+ CollisionIndex collisionIndex;
+
+ TransformState state;
+ MapMode mapMode;
+ TimePoint commitTime;
+
+ std::unordered_map<uint32_t, JointPlacement> placements;
+ std::unordered_map<uint32_t, JointOpacityState> opacities;
+
+ TimePoint recentUntil;
+ bool stale = false;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/text/placement_config.hpp b/src/mbgl/text/placement_config.hpp
deleted file mode 100644
index 48b24b5f41..0000000000
--- a/src/mbgl/text/placement_config.hpp
+++ /dev/null
@@ -1,33 +0,0 @@
-#pragma once
-
-#include <mbgl/util/constants.hpp>
-
-namespace mbgl {
-
-class PlacementConfig {
-public:
- PlacementConfig(float angle_ = 0, float pitch_ = 0, float cameraToCenterDistance_ = 0, float cameraToTileDistance_ = 0, bool debug_ = false)
- : angle(angle_), pitch(pitch_), cameraToCenterDistance(cameraToCenterDistance_), cameraToTileDistance(cameraToTileDistance_), debug(debug_) {
- }
-
- bool operator==(const PlacementConfig& rhs) const {
- return angle == rhs.angle &&
- pitch == rhs.pitch &&
- debug == rhs.debug &&
- ((pitch * util::RAD2DEG < 25) ||
- (cameraToCenterDistance == rhs.cameraToCenterDistance && cameraToTileDistance == rhs.cameraToTileDistance));
- }
-
- bool operator!=(const PlacementConfig& rhs) const {
- return !operator==(rhs);
- }
-
-public:
- float angle;
- float pitch;
- float cameraToCenterDistance;
- float cameraToTileDistance;
- bool debug;
-};
-
-} // namespace mbgl
diff --git a/src/mbgl/text/shaping.cpp b/src/mbgl/text/shaping.cpp
index 5d688ea539..a8232836b6 100644
--- a/src/mbgl/text/shaping.cpp
+++ b/src/mbgl/text/shaping.cpp
@@ -313,7 +313,7 @@ void shapeLines(Shaping& shaping,
align(shaping, justify, anchorAlign.horizontalAlign, anchorAlign.verticalAlign, maxLineLength,
lineHeight, lines.size());
- const uint32_t height = lines.size() * lineHeight;
+ const float height = lines.size() * lineHeight;
// Calculate the bounding box
shaping.top += -anchorAlign.verticalAlign * height;
diff --git a/src/mbgl/tile/custom_geometry_tile.cpp b/src/mbgl/tile/custom_geometry_tile.cpp
new file mode 100644
index 0000000000..33962ad87d
--- /dev/null
+++ b/src/mbgl/tile/custom_geometry_tile.cpp
@@ -0,0 +1,91 @@
+#include <mbgl/tile/custom_geometry_tile.hpp>
+#include <mbgl/tile/geojson_tile_data.hpp>
+#include <mbgl/renderer/query.hpp>
+#include <mbgl/renderer/tile_parameters.hpp>
+#include <mbgl/actor/scheduler.hpp>
+#include <mbgl/style/filter_evaluator.hpp>
+#include <mbgl/util/string.hpp>
+#include <mbgl/tile/tile_observer.hpp>
+#include <mbgl/style/custom_tile_loader.hpp>
+
+#include <mapbox/geojsonvt.hpp>
+
+namespace mbgl {
+
+CustomGeometryTile::CustomGeometryTile(const OverscaledTileID& overscaledTileID,
+ std::string sourceID_,
+ const TileParameters& parameters,
+ const style::CustomGeometrySource::TileOptions options_,
+ ActorRef<style::CustomTileLoader> loader_)
+ : GeometryTile(overscaledTileID, sourceID_, parameters),
+ necessity(TileNecessity::Optional),
+ options(options_),
+ loader(loader_),
+ mailbox(std::make_shared<Mailbox>(*Scheduler::GetCurrent())),
+ actorRef(*this, mailbox) {
+}
+
+CustomGeometryTile::~CustomGeometryTile() {
+ loader.invoke(&style::CustomTileLoader::removeTile, id);
+}
+
+void CustomGeometryTile::setTileData(const GeoJSON& geoJSON) {
+
+ auto featureData = mapbox::geometry::feature_collection<int16_t>();
+ if (geoJSON.is<FeatureCollection>() && !geoJSON.get<FeatureCollection>().empty()) {
+ const double scale = util::EXTENT / options.tileSize;
+
+ mapbox::geojsonvt::TileOptions vtOptions;
+ vtOptions.extent = util::EXTENT;
+ vtOptions.buffer = ::round(scale * options.buffer);
+ vtOptions.tolerance = scale * options.tolerance;
+ featureData = mapbox::geojsonvt::geoJSONToTile(geoJSON, id.canonical.z, id.canonical.x, id.canonical.y, vtOptions, options.wrap, options.clip).features;
+ } else {
+ setNecessity(TileNecessity::Optional);
+ }
+ setData(std::make_unique<GeoJSONTileData>(std::move(featureData)));
+}
+
+void CustomGeometryTile::invalidateTileData() {
+ stale = true;
+ observer->onTileChanged(*this);
+}
+
+//Fetching tile data for custom sources is assumed to be an expensive operation.
+// Only required tiles make fetchTile requests. Attempt to cancel a tile
+// that is no longer required.
+void CustomGeometryTile::setNecessity(TileNecessity newNecessity) {
+ if (newNecessity != necessity || stale ) {
+ necessity = newNecessity;
+ if (necessity == TileNecessity::Required) {
+ loader.invoke(&style::CustomTileLoader::fetchTile, id, actorRef);
+ stale = false;
+ } else if (!isRenderable()) {
+ loader.invoke(&style::CustomTileLoader::cancelTile, id);
+ }
+ }
+}
+
+void CustomGeometryTile::querySourceFeatures(
+ std::vector<Feature>& result,
+ const SourceQueryOptions& queryOptions) {
+
+ // Ignore the sourceLayer, there is only one
+ auto layer = getData()->getLayer({});
+
+ if (layer) {
+ auto featureCount = layer->featureCount();
+ for (std::size_t i = 0; i < featureCount; i++) {
+ auto feature = layer->getFeature(i);
+
+ // Apply filter, if any
+ if (queryOptions.filter && !(*queryOptions.filter)(*feature)) {
+ continue;
+ }
+
+ result.push_back(convertFeature(*feature, id.canonical));
+ }
+ }
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/tile/custom_geometry_tile.hpp b/src/mbgl/tile/custom_geometry_tile.hpp
new file mode 100644
index 0000000000..1df44e6b2a
--- /dev/null
+++ b/src/mbgl/tile/custom_geometry_tile.hpp
@@ -0,0 +1,44 @@
+#pragma once
+
+#include <mbgl/tile/geometry_tile.hpp>
+#include <mbgl/style/sources/custom_geometry_source.hpp>
+#include <mbgl/util/feature.hpp>
+#include <mbgl/util/geojson.hpp>
+#include <mbgl/actor/mailbox.hpp>
+
+namespace mbgl {
+
+class TileParameters;
+
+namespace style {
+class CustomTileLoader;
+} // namespace style
+
+class CustomGeometryTile: public GeometryTile {
+public:
+ CustomGeometryTile(const OverscaledTileID&,
+ std::string sourceID,
+ const TileParameters&,
+ const style::CustomGeometrySource::TileOptions,
+ ActorRef<style::CustomTileLoader> loader);
+ ~CustomGeometryTile() override;
+
+ void setTileData(const GeoJSON& data);
+ void invalidateTileData();
+
+ void setNecessity(TileNecessity) final;
+
+ void querySourceFeatures(
+ std::vector<Feature>& result,
+ const SourceQueryOptions&) override;
+
+private:
+ bool stale = true;
+ TileNecessity necessity;
+ const style::CustomGeometrySource::TileOptions options;
+ ActorRef<style::CustomTileLoader> loader;
+ std::shared_ptr<Mailbox> mailbox;
+ ActorRef<CustomGeometryTile> actorRef;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/tile/geometry_tile.cpp b/src/mbgl/tile/geometry_tile.cpp
index 8c018ce3aa..a58c744065 100644
--- a/src/mbgl/tile/geometry_tile.cpp
+++ b/src/mbgl/tile/geometry_tile.cpp
@@ -15,7 +15,6 @@
#include <mbgl/renderer/image_atlas.hpp>
#include <mbgl/storage/file_source.hpp>
#include <mbgl/geometry/feature_index.hpp>
-#include <mbgl/text/collision_tile.hpp>
#include <mbgl/map/transform_state.hpp>
#include <mbgl/style/filter_evaluator.hpp>
#include <mbgl/util/logging.hpp>
@@ -33,7 +32,7 @@ using namespace style;
GeometryTile's 'correlationID' is used for ensuring the tile will be flagged
as non-pending only when the placement coming from the last operation (as in
- 'setData', 'setLayers', 'setPlacementConfig') occurs. This is important for
+ 'setData', 'setLayers', 'setShowCollisionBoxes') occurs. This is important for
still mode rendering as we want to render only when all layout and placement
operations are completed.
@@ -52,13 +51,15 @@ GeometryTile::GeometryTile(const OverscaledTileID& id_,
worker(parameters.workerScheduler,
ActorRef<GeometryTile>(*this, mailbox),
id_,
+ sourceID,
obsolete,
parameters.mode,
- parameters.pixelRatio),
+ parameters.pixelRatio,
+ parameters.debugOptions & MapDebugOptions::Collision),
glyphManager(parameters.glyphManager),
imageManager(parameters.imageManager),
- lastYStretch(1.0f),
- mode(parameters.mode) {
+ mode(parameters.mode),
+ showCollisionBoxes(parameters.debugOptions & MapDebugOptions::Collision) {
}
GeometryTile::~GeometryTile() {
@@ -89,25 +90,6 @@ void GeometryTile::setData(std::unique_ptr<const GeometryTileData> data_) {
worker.invoke(&GeometryTileWorker::setData, std::move(data_), correlationID);
}
-void GeometryTile::setPlacementConfig(const PlacementConfig& desiredConfig) {
- if (requestedConfig == desiredConfig) {
- return;
- }
-
- // Mark the tile as pending again if it was complete before to prevent signaling a complete
- // state despite pending parse operations.
- pending = true;
-
- ++correlationID;
- requestedConfig = desiredConfig;
- invokePlacement();
-}
-
-void GeometryTile::invokePlacement() {
- if (requestedConfig) {
- worker.invoke(&GeometryTileWorker::setPlacementConfig, *requestedConfig, correlationID);
- }
-}
void GeometryTile::setLayers(const std::vector<Immutable<Layer::Impl>>& layers) {
// Mark the tile as pending again if it was complete before to prevent signaling a complete
@@ -134,14 +116,22 @@ void GeometryTile::setLayers(const std::vector<Immutable<Layer::Impl>>& layers)
worker.invoke(&GeometryTileWorker::setLayers, std::move(impls), correlationID);
}
+void GeometryTile::setShowCollisionBoxes(const bool showCollisionBoxes_) {
+ if (showCollisionBoxes != showCollisionBoxes_) {
+ showCollisionBoxes = showCollisionBoxes_;
+ ++correlationID;
+ worker.invoke(&GeometryTileWorker::setShowCollisionBoxes, showCollisionBoxes, correlationID);
+ }
+}
+
void GeometryTile::onLayout(LayoutResult result, const uint64_t resultCorrelationID) {
- loaded = true;
- renderable = true;
+ // Don't mark ourselves loaded or renderable until the first successful placement
+ // TODO: Ideally we'd render this tile without symbols as long as this tile wasn't
+ // replacing a tile at a different zoom that _did_ have symbols.
(void)resultCorrelationID;
nonSymbolBuckets = std::move(result.nonSymbolBuckets);
- featureIndex = std::move(result.featureIndex);
- data = std::move(result.tileData);
- collisionTile.reset();
+ pendingFeatureIndex = std::move(result.featureIndex);
+ pendingData = std::move(result.tileData);
observer->onTileChanged(*this);
}
@@ -152,16 +142,13 @@ void GeometryTile::onPlacement(PlacementResult result, const uint64_t resultCorr
pending = false;
}
symbolBuckets = std::move(result.symbolBuckets);
- collisionTile = std::move(result.collisionTile);
if (result.glyphAtlasImage) {
glyphAtlasImage = std::move(*result.glyphAtlasImage);
}
if (result.iconAtlasImage) {
iconAtlasImage = std::move(*result.iconAtlasImage);
}
- if (collisionTile.get()) {
- lastYStretch = collisionTile->yStretch;
- }
+
observer->onTileChanged(*this);
}
@@ -226,12 +213,22 @@ Bucket* GeometryTile::getBucket(const Layer::Impl& layer) const {
return it->second.get();
}
+void GeometryTile::commitFeatureIndex() {
+ if (pendingFeatureIndex) {
+ featureIndex = std::move(pendingFeatureIndex);
+ }
+ if (pendingData) {
+ data = std::move(pendingData);
+ }
+}
+
void GeometryTile::queryRenderedFeatures(
std::unordered_map<std::string, std::vector<Feature>>& result,
const GeometryCoordinates& queryGeometry,
const TransformState& transformState,
const std::vector<const RenderLayer*>& layers,
- const RenderedQueryOptions& options) {
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) {
if (!featureIndex || !data) return;
@@ -251,9 +248,10 @@ void GeometryTile::queryRenderedFeatures(
std::pow(2, transformState.getZoom() - id.overscaledZ),
options,
*data,
- id.canonical,
+ id.toUnwrapped(),
+ sourceID,
layers,
- collisionTile.get(),
+ collisionIndex,
additionalRadius);
}
@@ -293,11 +291,25 @@ void GeometryTile::querySourceFeatures(
}
}
-float GeometryTile::yStretch() const {
- // collisionTile gets reset in onLayout but we don't clear the symbolBuckets
- // until a new placement result comes along, so keep the yStretch value in
- // case we need to render them.
- return lastYStretch;
+bool GeometryTile::holdForFade() const {
+ return mode == MapMode::Continuous &&
+ (fadeState == FadeState::NeedsFirstPlacement || fadeState == FadeState::NeedsSecondPlacement);
+}
+
+void GeometryTile::markRenderedIdeal() {
+ fadeState = FadeState::Loaded;
+}
+void GeometryTile::markRenderedPreviously() {
+ if (fadeState == FadeState::Loaded) {
+ fadeState = FadeState::NeedsFirstPlacement;
+ }
+}
+void GeometryTile::performedFadePlacement() {
+ if (fadeState == FadeState::NeedsFirstPlacement) {
+ fadeState = FadeState::NeedsSecondPlacement;
+ } else if (fadeState == FadeState::NeedsSecondPlacement) {
+ fadeState = FadeState::CanRemove;
+ }
}
} // namespace mbgl
diff --git a/src/mbgl/tile/geometry_tile.hpp b/src/mbgl/tile/geometry_tile.hpp
index a478aad504..00a4aafadf 100644
--- a/src/mbgl/tile/geometry_tile.hpp
+++ b/src/mbgl/tile/geometry_tile.hpp
@@ -4,8 +4,6 @@
#include <mbgl/tile/geometry_tile_worker.hpp>
#include <mbgl/renderer/image_manager.hpp>
#include <mbgl/text/glyph_manager.hpp>
-#include <mbgl/text/placement_config.hpp>
-#include <mbgl/text/collision_tile.hpp>
#include <mbgl/util/feature.hpp>
#include <mbgl/util/throttler.hpp>
#include <mbgl/actor/actor.hpp>
@@ -36,9 +34,9 @@ public:
void setError(std::exception_ptr);
void setData(std::unique_ptr<const GeometryTileData>);
- void setPlacementConfig(const PlacementConfig&) override;
void setLayers(const std::vector<Immutable<style::Layer::Impl>>&) override;
-
+ void setShowCollisionBoxes(const bool showCollisionBoxes) override;
+
void onGlyphsAvailable(GlyphMap) override;
void onImagesAvailable(ImageMap, uint64_t imageCorrelationID) override;
@@ -56,7 +54,8 @@ public:
const GeometryCoordinates& queryGeometry,
const TransformState&,
const std::vector<const RenderLayer*>& layers,
- const RenderedQueryOptions& options) override;
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) override;
void querySourceFeatures(
std::vector<Feature>& result,
@@ -82,16 +81,13 @@ public:
class PlacementResult {
public:
std::unordered_map<std::string, std::shared_ptr<Bucket>> symbolBuckets;
- std::unique_ptr<CollisionTile> collisionTile;
optional<AlphaImage> glyphAtlasImage;
optional<PremultipliedImage> iconAtlasImage;
PlacementResult(std::unordered_map<std::string, std::shared_ptr<Bucket>> symbolBuckets_,
- std::unique_ptr<CollisionTile> collisionTile_,
optional<AlphaImage> glyphAtlasImage_,
optional<PremultipliedImage> iconAtlasImage_)
: symbolBuckets(std::move(symbolBuckets_)),
- collisionTile(std::move(collisionTile_)),
glyphAtlasImage(std::move(glyphAtlasImage_)),
iconAtlasImage(std::move(iconAtlasImage_)) {}
};
@@ -99,7 +95,12 @@ public:
void onError(std::exception_ptr, uint64_t correlationID);
- float yStretch() const override;
+ bool holdForFade() const override;
+ void markRenderedIdeal() override;
+ void markRenderedPreviously() override;
+ void performedFadePlacement() override;
+
+ void commitFeatureIndex() override;
protected:
const GeometryTileData* getData() {
@@ -108,7 +109,6 @@ protected:
private:
void markObsolete();
- void invokePlacement();
const std::string sourceID;
@@ -122,21 +122,30 @@ private:
ImageManager& imageManager;
uint64_t correlationID = 0;
- optional<PlacementConfig> requestedConfig;
std::unordered_map<std::string, std::shared_ptr<Bucket>> nonSymbolBuckets;
std::unique_ptr<FeatureIndex> featureIndex;
+ std::unique_ptr<FeatureIndex> pendingFeatureIndex;
std::unique_ptr<const GeometryTileData> data;
+ std::unique_ptr<const GeometryTileData> pendingData;
optional<AlphaImage> glyphAtlasImage;
optional<PremultipliedImage> iconAtlasImage;
std::unordered_map<std::string, std::shared_ptr<Bucket>> symbolBuckets;
- std::unique_ptr<CollisionTile> collisionTile;
- float lastYStretch;
const MapMode mode;
+
+ bool showCollisionBoxes;
+
+ enum class FadeState {
+ Loaded,
+ NeedsFirstPlacement,
+ NeedsSecondPlacement,
+ CanRemove
+ };
+ FadeState fadeState = FadeState::Loaded;
public:
optional<gl::Texture> glyphAtlasTexture;
optional<gl::Texture> iconAtlasTexture;
diff --git a/src/mbgl/tile/geometry_tile_worker.cpp b/src/mbgl/tile/geometry_tile_worker.cpp
index 50429420c3..24841dd125 100644
--- a/src/mbgl/tile/geometry_tile_worker.cpp
+++ b/src/mbgl/tile/geometry_tile_worker.cpp
@@ -1,7 +1,6 @@
#include <mbgl/tile/geometry_tile_worker.hpp>
#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/tile/geometry_tile.hpp>
-#include <mbgl/text/collision_tile.hpp>
#include <mbgl/layout/symbol_layout.hpp>
#include <mbgl/renderer/bucket_parameters.hpp>
#include <mbgl/renderer/group_by_layout.hpp>
@@ -24,20 +23,36 @@ using namespace style;
GeometryTileWorker::GeometryTileWorker(ActorRef<GeometryTileWorker> self_,
ActorRef<GeometryTile> parent_,
OverscaledTileID id_,
+ const std::string& sourceID_,
const std::atomic<bool>& obsolete_,
const MapMode mode_,
- const float pixelRatio_)
+ const float pixelRatio_,
+ const bool showCollisionBoxes_)
: self(std::move(self_)),
parent(std::move(parent_)),
id(std::move(id_)),
+ sourceID(sourceID_),
obsolete(obsolete_),
mode(mode_),
- pixelRatio(pixelRatio_) {
+ pixelRatio(pixelRatio_),
+ showCollisionBoxes(showCollisionBoxes_) {
}
GeometryTileWorker::~GeometryTileWorker() = default;
/*
+ NOTE: The comments below are technically correct, but currently
+ conceptually misleading. The change to foreground label placement
+ means that:
+ (1) "placement" here is a misnomer: the remaining role of
+ "attemptPlacement" is symbol buffer generation
+ (2) Once a tile has completed layout, we will only run
+ "attemptPlacement" once
+ (3) Tiles won't be rendered until "attemptPlacement" has run once
+
+ TODO: Simplify GeometryTileWorker to fit its new role
+ https://github.com/mapbox/mapbox-gl-native/issues/10457
+
GeometryTileWorker is a state machine. This is its transition diagram.
States are indicated by [state], lines are transitions triggered by
messages, (parentheses) are actions taken on transition.
@@ -116,9 +131,9 @@ void GeometryTileWorker::setLayers(std::vector<Immutable<Layer::Impl>> layers_,
}
}
-void GeometryTileWorker::setPlacementConfig(PlacementConfig placementConfig_, uint64_t correlationID_) {
+void GeometryTileWorker::setShowCollisionBoxes(bool showCollisionBoxes_, uint64_t correlationID_) {
try {
- placementConfig = std::move(placementConfig_);
+ showCollisionBoxes = showCollisionBoxes_;
correlationID = correlationID_;
switch (state) {
@@ -372,7 +387,7 @@ bool GeometryTileWorker::hasPendingSymbolDependencies() const {
}
void GeometryTileWorker::attemptPlacement() {
- if (!data || !layers || !placementConfig || hasPendingSymbolDependencies()) {
+ if (!data || !layers || hasPendingSymbolDependencies()) {
return;
}
@@ -392,13 +407,13 @@ void GeometryTileWorker::attemptPlacement() {
}
symbolLayout->prepare(glyphMap, glyphAtlas.positions,
- imageMap, imageAtlas.positions);
+ imageMap, imageAtlas.positions,
+ id, sourceID);
}
symbolLayoutsNeedPreparation = false;
}
- auto collisionTile = std::make_unique<CollisionTile>(*placementConfig);
std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets;
for (auto& symbolLayout : symbolLayouts) {
@@ -410,15 +425,19 @@ void GeometryTileWorker::attemptPlacement() {
continue;
}
- std::shared_ptr<Bucket> bucket = symbolLayout->place(*collisionTile);
+ std::shared_ptr<SymbolBucket> bucket = symbolLayout->place(showCollisionBoxes);
for (const auto& pair : symbolLayout->layerPaintProperties) {
+ if (!firstLoad) {
+ bucket->justReloaded = true;
+ }
buckets.emplace(pair.first, bucket);
}
}
+ firstLoad = false;
+
parent.invoke(&GeometryTile::onPlacement, GeometryTile::PlacementResult {
std::move(buckets),
- std::move(collisionTile),
std::move(glyphAtlasImage),
std::move(iconAtlasImage),
}, correlationID);
diff --git a/src/mbgl/tile/geometry_tile_worker.hpp b/src/mbgl/tile/geometry_tile_worker.hpp
index 1425daa7a1..0276392679 100644
--- a/src/mbgl/tile/geometry_tile_worker.hpp
+++ b/src/mbgl/tile/geometry_tile_worker.hpp
@@ -4,7 +4,6 @@
#include <mbgl/tile/tile_id.hpp>
#include <mbgl/style/image_impl.hpp>
#include <mbgl/text/glyph.hpp>
-#include <mbgl/text/placement_config.hpp>
#include <mbgl/actor/actor_ref.hpp>
#include <mbgl/util/optional.hpp>
#include <mbgl/util/immutable.hpp>
@@ -28,14 +27,16 @@ public:
GeometryTileWorker(ActorRef<GeometryTileWorker> self,
ActorRef<GeometryTile> parent,
OverscaledTileID,
+ const std::string&,
const std::atomic<bool>&,
const MapMode,
- const float pixelRatio);
+ const float pixelRatio,
+ const bool showCollisionBoxes_);
~GeometryTileWorker();
void setLayers(std::vector<Immutable<style::Layer::Impl>>, uint64_t correlationID);
void setData(std::unique_ptr<const GeometryTileData>, uint64_t correlationID);
- void setPlacementConfig(PlacementConfig, uint64_t correlationID);
+ void setShowCollisionBoxes(bool showCollisionBoxes_, uint64_t correlationID_);
void onGlyphsAvailable(GlyphMap glyphs);
void onImagesAvailable(ImageMap images, uint64_t imageCorrelationID);
@@ -57,6 +58,7 @@ private:
ActorRef<GeometryTile> parent;
const OverscaledTileID id;
+ const std::string sourceID;
const std::atomic<bool>& obsolete;
const MapMode mode;
const float pixelRatio;
@@ -75,7 +77,6 @@ private:
// Outer optional indicates whether we've received it or not.
optional<std::vector<Immutable<style::Layer::Impl>>> layers;
optional<std::unique_ptr<const GeometryTileData>> data;
- optional<PlacementConfig> placementConfig;
bool symbolLayoutsNeedPreparation = false;
std::vector<std::unique_ptr<SymbolLayout>> symbolLayouts;
@@ -83,6 +84,9 @@ private:
ImageDependencies pendingImageDependencies;
GlyphMap glyphMap;
ImageMap imageMap;
+
+ bool showCollisionBoxes;
+ bool firstLoad = true;
};
} // namespace mbgl
diff --git a/src/mbgl/tile/raster_dem_tile.cpp b/src/mbgl/tile/raster_dem_tile.cpp
new file mode 100644
index 0000000000..5db298cf4c
--- /dev/null
+++ b/src/mbgl/tile/raster_dem_tile.cpp
@@ -0,0 +1,125 @@
+#include <mbgl/tile/raster_dem_tile.hpp>
+#include <mbgl/tile/raster_dem_tile_worker.hpp>
+#include <mbgl/tile/tile_observer.hpp>
+#include <mbgl/tile/tile_loader_impl.hpp>
+#include <mbgl/style/source.hpp>
+#include <mbgl/storage/resource.hpp>
+#include <mbgl/storage/response.hpp>
+#include <mbgl/storage/file_source.hpp>
+#include <mbgl/renderer/tile_parameters.hpp>
+#include <mbgl/renderer/buckets/hillshade_bucket.hpp>
+#include <mbgl/actor/scheduler.hpp>
+
+namespace mbgl {
+
+RasterDEMTile::RasterDEMTile(const OverscaledTileID& id_,
+ const TileParameters& parameters,
+ const Tileset& tileset)
+ : Tile(id_),
+ loader(*this, id_, parameters, tileset),
+ mailbox(std::make_shared<Mailbox>(*Scheduler::GetCurrent())),
+ worker(parameters.workerScheduler,
+ ActorRef<RasterDEMTile>(*this, mailbox)) {
+
+ encoding = tileset.encoding;
+ if ( id.canonical.y == 0 ){
+ // this tile doesn't have upper neighboring tiles so marked those as backfilled
+ neighboringTiles = neighboringTiles | DEMTileNeighbors::NoUpper;
+ }
+
+ if (id.canonical.y + 1 == std::pow(2, id.canonical.z)){
+ // this tile doesn't have lower neighboring tiles so marked those as backfilled
+ neighboringTiles = neighboringTiles | DEMTileNeighbors::NoLower;
+ }
+}
+
+RasterDEMTile::~RasterDEMTile() = default;
+
+void RasterDEMTile::setError(std::exception_ptr err) {
+ loaded = true;
+ observer->onTileError(*this, err);
+}
+
+void RasterDEMTile::setMetadata(optional<Timestamp> modified_, optional<Timestamp> expires_) {
+ modified = modified_;
+ expires = expires_;
+}
+
+void RasterDEMTile::setData(std::shared_ptr<const std::string> data) {
+ pending = true;
+ ++correlationID;
+ worker.invoke(&RasterDEMTileWorker::parse, data, correlationID, encoding);
+}
+
+void RasterDEMTile::onParsed(std::unique_ptr<HillshadeBucket> result, const uint64_t resultCorrelationID) {
+ bucket = std::move(result);
+ loaded = true;
+ if (resultCorrelationID == correlationID) {
+ pending = false;
+ }
+ renderable = bucket ? true : false;
+ observer->onTileChanged(*this);
+}
+
+void RasterDEMTile::onError(std::exception_ptr err, const uint64_t resultCorrelationID) {
+ loaded = true;
+ if (resultCorrelationID == correlationID) {
+ pending = false;
+ }
+ observer->onTileError(*this, err);
+}
+
+void RasterDEMTile::upload(gl::Context& context) {
+ if (bucket) {
+ bucket->upload(context);
+ }
+}
+
+
+Bucket* RasterDEMTile::getBucket(const style::Layer::Impl&) const {
+ return bucket.get();
+}
+
+HillshadeBucket* RasterDEMTile::getBucket() const {
+ return bucket.get();
+}
+
+void RasterDEMTile::backfillBorder(const RasterDEMTile& borderTile, const DEMTileNeighbors mask) {
+ int32_t dx = borderTile.id.canonical.x - id.canonical.x;
+ const int8_t dy = borderTile.id.canonical.y - id.canonical.y;
+ const uint32_t dim = pow(2, id.canonical.z);
+ if (dx == 0 && dy == 0) return;
+ if (std::abs(dy) > 1) return;
+ // neighbor is in another world wrap
+ if (std::abs(dx) > 1) {
+ if (std::abs(int(dx + dim)) == 1) {
+ dx += dim;
+ } else if (std::abs(int(dx - dim)) == 1) {
+ dx -= dim;
+ }
+ }
+ const HillshadeBucket* borderBucket = borderTile.getBucket();
+ if (borderBucket) {
+ const DEMData& borderDEM = borderBucket->getDEMData();
+ DEMData& tileDEM = bucket->getDEMData();
+
+ tileDEM.backfillBorder(borderDEM, dx, dy);
+ // update the bitmask to indicate that this tiles have been backfilled by flipping the relevant bit
+ this->neighboringTiles = this->neighboringTiles | mask;
+ // mark HillshadeBucket.prepared as false so it runs through the prepare render pass
+ // with the new texture data we just backfilled
+ bucket->setPrepared(false);
+ }
+}
+
+void RasterDEMTile::setMask(TileMask&& mask) {
+ if (bucket) {
+ bucket->setMask(std::move(mask));
+ }
+}
+
+void RasterDEMTile::setNecessity(TileNecessity necessity) {
+ loader.setNecessity(necessity);
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/tile/raster_dem_tile.hpp b/src/mbgl/tile/raster_dem_tile.hpp
new file mode 100644
index 0000000000..0c8dd75961
--- /dev/null
+++ b/src/mbgl/tile/raster_dem_tile.hpp
@@ -0,0 +1,105 @@
+#pragma once
+
+#include <mbgl/tile/tile.hpp>
+#include <mbgl/tile/tile_loader.hpp>
+#include <mbgl/tile/raster_dem_tile_worker.hpp>
+#include <mbgl/actor/actor.hpp>
+
+namespace mbgl {
+
+class Tileset;
+class TileParameters;
+class HillshadeBucket;
+
+enum class DEMTileNeighbors : uint8_t {
+ // 0b00000000
+ Empty = 0 << 1,
+
+ // 0b00000001
+ Left = 1 << 0,
+ // 0b00000010
+ Right = 1 << 1,
+ // 0b00000100
+ TopLeft = 1 << 2,
+ // 0b00001000
+ TopCenter = 1 << 3,
+ // 0b00010000
+ TopRight = 1 << 4,
+ // 0b00100000
+ BottomLeft = 1 << 5,
+ // 0b01000000
+ BottomCenter = 1 << 6,
+ // 0b10000000
+ BottomRight = 1 << 7,
+
+ // helper enums for tiles with no upper/lower neighbors
+ // and completely backfilled tiles
+
+ // 0b00011100
+ NoUpper = 0b00011100,
+ // 0b11100000
+ NoLower = 0b11100000,
+ // 0b11111111
+ Complete = 0b11111111
+};
+
+inline DEMTileNeighbors operator|(DEMTileNeighbors a, DEMTileNeighbors b) {
+ return static_cast<DEMTileNeighbors>(int(a) | int(b));
+};
+
+inline DEMTileNeighbors operator&(DEMTileNeighbors a, DEMTileNeighbors b) {
+ return static_cast<DEMTileNeighbors>(int(a) & int(b));
+}
+
+inline bool operator!=(DEMTileNeighbors a, DEMTileNeighbors b) {
+ return static_cast<unsigned char>(a) != static_cast<unsigned char>(b);
+}
+
+namespace style {
+class Layer;
+} // namespace style
+
+class RasterDEMTile : public Tile {
+public:
+ RasterDEMTile(const OverscaledTileID&,
+ const TileParameters&,
+ const Tileset&);
+ ~RasterDEMTile() override;
+
+ void setNecessity(TileNecessity) final;
+
+ void setError(std::exception_ptr);
+ void setMetadata(optional<Timestamp> modified, optional<Timestamp> expires);
+ void setData(std::shared_ptr<const std::string> data);
+
+ void upload(gl::Context&) override;
+ Bucket* getBucket(const style::Layer::Impl&) const override;
+
+ HillshadeBucket* getBucket() const;
+ void backfillBorder(const RasterDEMTile& borderTile, const DEMTileNeighbors mask);
+
+ // neighboringTiles is a bitmask for which neighboring tiles have been backfilled
+ // there are max 8 possible neighboring tiles, so each bit represents one neighbor
+ DEMTileNeighbors neighboringTiles = DEMTileNeighbors::Empty;
+
+ void setMask(TileMask&&) override;
+
+ void onParsed(std::unique_ptr<HillshadeBucket> result, uint64_t correlationID);
+ void onError(std::exception_ptr, uint64_t correlationID);
+
+private:
+ TileLoader<RasterDEMTile> loader;
+
+ std::shared_ptr<Mailbox> mailbox;
+ Actor<RasterDEMTileWorker> worker;
+
+ uint64_t correlationID = 0;
+ Tileset::DEMEncoding encoding;
+
+ // Contains the Bucket object for the tile. Buckets are render
+ // objects and they get added by tile parsing operations.
+ std::unique_ptr<HillshadeBucket> bucket;
+
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/tile/raster_dem_tile_worker.cpp b/src/mbgl/tile/raster_dem_tile_worker.cpp
new file mode 100644
index 0000000000..7338e578c7
--- /dev/null
+++ b/src/mbgl/tile/raster_dem_tile_worker.cpp
@@ -0,0 +1,27 @@
+#include <mbgl/tile/raster_dem_tile_worker.hpp>
+#include <mbgl/tile/raster_dem_tile.hpp>
+#include <mbgl/renderer/buckets/hillshade_bucket.hpp>
+#include <mbgl/actor/actor.hpp>
+#include <mbgl/util/premultiply.hpp>
+
+namespace mbgl {
+
+RasterDEMTileWorker::RasterDEMTileWorker(ActorRef<RasterDEMTileWorker>, ActorRef<RasterDEMTile> parent_)
+ : parent(std::move(parent_)) {
+}
+
+void RasterDEMTileWorker::parse(std::shared_ptr<const std::string> data, uint64_t correlationID, Tileset::DEMEncoding encoding) {
+ if (!data) {
+ parent.invoke(&RasterDEMTile::onParsed, nullptr, correlationID); // No data; empty tile.
+ return;
+ }
+
+ try {
+ auto bucket = std::make_unique<HillshadeBucket>(decodeImage(*data), encoding);
+ parent.invoke(&RasterDEMTile::onParsed, std::move(bucket), correlationID);
+ } catch (...) {
+ parent.invoke(&RasterDEMTile::onError, std::current_exception(), correlationID);
+ }
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/tile/raster_dem_tile_worker.hpp b/src/mbgl/tile/raster_dem_tile_worker.hpp
new file mode 100644
index 0000000000..5a8222bc2d
--- /dev/null
+++ b/src/mbgl/tile/raster_dem_tile_worker.hpp
@@ -0,0 +1,23 @@
+#pragma once
+
+#include <mbgl/actor/actor_ref.hpp>
+#include <mbgl/util/tileset.hpp>
+
+#include <memory>
+#include <string>
+
+namespace mbgl {
+
+class RasterDEMTile;
+
+class RasterDEMTileWorker {
+public:
+ RasterDEMTileWorker(ActorRef<RasterDEMTileWorker>, ActorRef<RasterDEMTile>);
+
+ void parse(std::shared_ptr<const std::string> data, uint64_t correlationID, Tileset::DEMEncoding encoding);
+
+private:
+ ActorRef<RasterDEMTile> parent;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/tile/raster_tile.cpp b/src/mbgl/tile/raster_tile.cpp
index 85fcea77b7..ff23d4493e 100644
--- a/src/mbgl/tile/raster_tile.cpp
+++ b/src/mbgl/tile/raster_tile.cpp
@@ -24,9 +24,6 @@ RasterTile::RasterTile(const OverscaledTileID& id_,
RasterTile::~RasterTile() = default;
-void RasterTile::cancel() {
-}
-
void RasterTile::setError(std::exception_ptr err) {
loaded = true;
observer->onTileError(*this, err);
diff --git a/src/mbgl/tile/raster_tile.hpp b/src/mbgl/tile/raster_tile.hpp
index 192769ed8f..e25329119a 100644
--- a/src/mbgl/tile/raster_tile.hpp
+++ b/src/mbgl/tile/raster_tile.hpp
@@ -20,7 +20,7 @@ public:
RasterTile(const OverscaledTileID&,
const TileParameters&,
const Tileset&);
- ~RasterTile() final;
+ ~RasterTile() override;
void setNecessity(TileNecessity) final;
@@ -28,8 +28,6 @@ public:
void setMetadata(optional<Timestamp> modified, optional<Timestamp> expires);
void setData(std::shared_ptr<const std::string> data);
- void cancel() override;
-
void upload(gl::Context&) override;
Bucket* getBucket(const style::Layer::Impl&) const override;
diff --git a/src/mbgl/tile/tile.cpp b/src/mbgl/tile/tile.cpp
index f36a472e72..88db2ba07c 100644
--- a/src/mbgl/tile/tile.cpp
+++ b/src/mbgl/tile/tile.cpp
@@ -18,6 +18,9 @@ void Tile::setObserver(TileObserver* observer_) {
observer = observer_;
}
+void Tile::cancel() {
+}
+
void Tile::setTriedCache() {
triedOptional = true;
observer->onTileChanged(*this);
@@ -34,7 +37,8 @@ void Tile::queryRenderedFeatures(
const GeometryCoordinates&,
const TransformState&,
const std::vector<const RenderLayer*>&,
- const RenderedQueryOptions&) {}
+ const RenderedQueryOptions&,
+ const CollisionIndex&) {}
void Tile::querySourceFeatures(
std::vector<Feature>&,
diff --git a/src/mbgl/tile/tile.hpp b/src/mbgl/tile/tile.hpp
index 8be7c4d862..23365c6ae3 100644
--- a/src/mbgl/tile/tile.hpp
+++ b/src/mbgl/tile/tile.hpp
@@ -23,11 +23,12 @@ namespace mbgl {
class DebugBucket;
class TransformState;
class TileObserver;
-class PlacementConfig;
class RenderLayer;
class RenderedQueryOptions;
class SourceQueryOptions;
+class CollisionIndex;
+
namespace gl {
class Context;
} // namespace gl
@@ -42,12 +43,12 @@ public:
virtual void setNecessity(TileNecessity) {}
// Mark this tile as no longer needed and cancel any pending work.
- virtual void cancel() = 0;
+ virtual void cancel();
virtual void upload(gl::Context&) = 0;
virtual Bucket* getBucket(const style::Layer::Impl&) const = 0;
- virtual void setPlacementConfig(const PlacementConfig&) {}
+ virtual void setShowCollisionBoxes(const bool) {}
virtual void setLayers(const std::vector<Immutable<style::Layer::Impl>>&) {}
virtual void setMask(TileMask&&) {}
@@ -56,7 +57,8 @@ public:
const GeometryCoordinates& queryGeometry,
const TransformState&,
const std::vector<const RenderLayer*>&,
- const RenderedQueryOptions& options);
+ const RenderedQueryOptions& options,
+ const CollisionIndex&);
virtual void querySourceFeatures(
std::vector<Feature>& result,
@@ -92,7 +94,26 @@ public:
bool isComplete() const {
return loaded && !pending;
}
-
+
+ // "holdForFade" is used to keep tiles in the render tree after they're no longer
+ // ideal tiles in order to allow symbols to fade out
+ virtual bool holdForFade() const {
+ return false;
+ }
+ // Set whenever this tile is used as an ideal tile
+ virtual void markRenderedIdeal() {}
+ // Set when the tile is removed from the ideal render set but may still be held for fading
+ virtual void markRenderedPreviously() {}
+ // Placement operation performed while this tile is fading
+ // We hold onto a tile for two placements: fading starts with the first placement
+ // and will have time to finish by the second placement.
+ virtual void performedFadePlacement() {}
+
+ // FeatureIndexes are loaded asynchronously, but must be used with a CollisionIndex
+ // generated from the same data. Calling commitFeatureIndex signals the current
+ // CollisionIndex is up-to-date and allows us to start using the last loaded FeatureIndex
+ virtual void commitFeatureIndex() {}
+
void dumpDebugLogs() const;
const OverscaledTileID id;
@@ -101,8 +122,6 @@ public:
// Contains the tile ID string for painting debug information.
std::unique_ptr<DebugBucket> debugBucket;
-
- virtual float yStretch() const { return 1.0f; }
protected:
bool triedOptional = false;
diff --git a/src/mbgl/tile/tile_cache.cpp b/src/mbgl/tile/tile_cache.cpp
index 3fafb1259c..463d397608 100644
--- a/src/mbgl/tile/tile_cache.cpp
+++ b/src/mbgl/tile/tile_cache.cpp
@@ -33,13 +33,22 @@ void TileCache::add(const OverscaledTileID& key, std::unique_ptr<Tile> tile) {
// purge oldest key/tile if necessary
if (orderedKeys.size() > size) {
- get(orderedKeys.front());
+ pop(orderedKeys.front());
}
assert(orderedKeys.size() <= size);
}
-std::unique_ptr<Tile> TileCache::get(const OverscaledTileID& key) {
+Tile* TileCache::get(const OverscaledTileID& key) {
+ auto it = tiles.find(key);
+ if (it != tiles.end()) {
+ return it->second.get();
+ } else {
+ return nullptr;
+ }
+}
+
+std::unique_ptr<Tile> TileCache::pop(const OverscaledTileID& key) {
std::unique_ptr<Tile> tile;
diff --git a/src/mbgl/tile/tile_cache.hpp b/src/mbgl/tile/tile_cache.hpp
index 80fe98a20c..88358b8cdc 100644
--- a/src/mbgl/tile/tile_cache.hpp
+++ b/src/mbgl/tile/tile_cache.hpp
@@ -17,7 +17,8 @@ public:
void setSize(size_t);
size_t getSize() const { return size; };
void add(const OverscaledTileID& key, std::unique_ptr<Tile> data);
- std::unique_ptr<Tile> get(const OverscaledTileID& key);
+ std::unique_ptr<Tile> pop(const OverscaledTileID& key);
+ Tile* get(const OverscaledTileID& key);
bool has(const OverscaledTileID& key);
void clear();
diff --git a/src/mbgl/util/color.cpp b/src/mbgl/util/color.cpp
index fcb7f4ec16..c8145d36e7 100644
--- a/src/mbgl/util/color.cpp
+++ b/src/mbgl/util/color.cpp
@@ -24,9 +24,9 @@ optional<Color> Color::parse(const std::string& s) {
std::string Color::stringify() const {
return "rgba(" +
- util::toString(r * 255) + "," +
- util::toString(g * 255) + "," +
- util::toString(b * 255) + "," +
+ util::toString(r * 255 / a) + "," +
+ util::toString(g * 255 / a) + "," +
+ util::toString(b * 255 / a) + "," +
util::toString(a) + ")";
}
diff --git a/src/mbgl/util/compression.cpp b/src/mbgl/util/compression.cpp
index 94089c1b26..30e813cbb8 100644
--- a/src/mbgl/util/compression.cpp
+++ b/src/mbgl/util/compression.cpp
@@ -1,6 +1,10 @@
#include <mbgl/util/compression.hpp>
+#if defined(__QT__) && defined(_WINDOWS)
+#include <QtZlib/zlib.h>
+#else
#include <zlib.h>
+#endif
#include <cstdio>
#include <cstring>
diff --git a/src/mbgl/util/grid_index.cpp b/src/mbgl/util/grid_index.cpp
index b3afd3fdc8..afd469501d 100644
--- a/src/mbgl/util/grid_index.cpp
+++ b/src/mbgl/util/grid_index.cpp
@@ -3,83 +3,301 @@
#include <mbgl/math/minmax.hpp>
#include <unordered_set>
+#include <cmath>
namespace mbgl {
template <class T>
-GridIndex<T>::GridIndex(int32_t extent_, int32_t n_, int32_t padding_) :
- extent(extent_),
- n(n_),
- padding(padding_),
- d(n + 2 * padding),
- scale(double(n) / double(extent)),
- min(-double(padding) / n * extent),
- max(extent + double(padding) / n * extent)
+GridIndex<T>::GridIndex(const float width_, const float height_, const int16_t cellSize_) :
+ width(width_),
+ height(height_),
+ xCellCount(std::ceil(width_ / cellSize_)),
+ yCellCount(std::ceil(height_ / cellSize_)),
+ xScale(xCellCount / width_),
+ yScale(yCellCount / height_)
{
- cells.resize(d * d);
+ boxCells.resize(xCellCount * yCellCount);
+ circleCells.resize(xCellCount * yCellCount);
}
template <class T>
void GridIndex<T>::insert(T&& t, const BBox& bbox) {
- size_t uid = elements.size();
+ size_t uid = boxElements.size();
- auto cx1 = convertToCellCoord(bbox.min.x);
- auto cy1 = convertToCellCoord(bbox.min.y);
- auto cx2 = convertToCellCoord(bbox.max.x);
- auto cy2 = convertToCellCoord(bbox.max.y);
+ auto cx1 = convertToXCellCoord(bbox.min.x);
+ auto cy1 = convertToYCellCoord(bbox.min.y);
+ auto cx2 = convertToXCellCoord(bbox.max.x);
+ auto cy2 = convertToYCellCoord(bbox.max.y);
- int32_t x, y, cellIndex;
+ int16_t x, y, cellIndex;
for (x = cx1; x <= cx2; ++x) {
for (y = cy1; y <= cy2; ++y) {
- cellIndex = d * y + x;
- cells[cellIndex].push_back(uid);
+ cellIndex = xCellCount * y + x;
+ boxCells[cellIndex].push_back(uid);
}
}
- elements.emplace_back(t, bbox);
+ boxElements.emplace_back(t, bbox);
+}
+
+template <class T>
+void GridIndex<T>::insert(T&& t, const BCircle& bcircle) {
+ size_t uid = circleElements.size();
+
+ auto cx1 = convertToXCellCoord(bcircle.center.x - bcircle.radius);
+ auto cy1 = convertToYCellCoord(bcircle.center.y - bcircle.radius);
+ auto cx2 = convertToXCellCoord(bcircle.center.x + bcircle.radius);
+ auto cy2 = convertToYCellCoord(bcircle.center.y + bcircle.radius);
+
+ int16_t x, y, cellIndex;
+ for (x = cx1; x <= cx2; ++x) {
+ for (y = cy1; y <= cy2; ++y) {
+ cellIndex = xCellCount * y + x;
+ circleCells[cellIndex].push_back(uid);
+ }
+ }
+
+ circleElements.emplace_back(t, bcircle);
}
template <class T>
std::vector<T> GridIndex<T>::query(const BBox& queryBBox) const {
std::vector<T> result;
- std::unordered_set<size_t> seenUids;
+ query(queryBBox, [&](const T& t, const BBox&) -> bool {
+ result.push_back(t);
+ return false;
+ });
+ return result;
+}
+
+template <class T>
+std::vector<std::pair<T, typename GridIndex<T>::BBox>> GridIndex<T>::queryWithBoxes(const BBox& queryBBox) const {
+ std::vector<std::pair<T, BBox>> result;
+ query(queryBBox, [&](const T& t, const BBox& bbox) -> bool {
+ result.push_back(std::make_pair(t, bbox));
+ return false;
+ });
+ return result;
+}
+
+template <class T>
+bool GridIndex<T>::hitTest(const BBox& queryBBox) const {
+ bool hit = false;
+ query(queryBBox, [&](const T&, const BBox&) -> bool {
+ hit = true;
+ return true;
+ });
+ return hit;
+}
+
+template <class T>
+bool GridIndex<T>::hitTest(const BCircle& queryBCircle) const {
+ bool hit = false;
+ query(queryBCircle, [&](const T&, const BBox&) -> bool {
+ hit = true;
+ return true;
+ });
+ return hit;
+}
- auto cx1 = convertToCellCoord(queryBBox.min.x);
- auto cy1 = convertToCellCoord(queryBBox.min.y);
- auto cx2 = convertToCellCoord(queryBBox.max.x);
- auto cy2 = convertToCellCoord(queryBBox.max.y);
+template <class T>
+bool GridIndex<T>::noIntersection(const BBox& queryBBox) const {
+ return queryBBox.max.x < 0 || queryBBox.min.x >= width || queryBBox.max.y < 0 || queryBBox.min.y >= height;
+}
+
+template <class T>
+bool GridIndex<T>::completeIntersection(const BBox& queryBBox) const {
+ return queryBBox.min.x <= 0 && queryBBox.min.y <= 0 && width <= queryBBox.max.x && height <= queryBBox.max.y;
+}
+
+template <class T>
+typename GridIndex<T>::BBox GridIndex<T>::convertToBox(const BCircle& circle) const {
+ return BBox{{circle.center.x - circle.radius, circle.center.y - circle.radius},
+ {circle.center.x + circle.radius, circle.center.y + circle.radius}};
+}
+
+template <class T>
+void GridIndex<T>::query(const BBox& queryBBox, std::function<bool (const T&, const BBox&)> resultFn) const {
+ std::unordered_set<size_t> seenBoxes;
+ std::unordered_set<size_t> seenCircles;
+
+ if (noIntersection(queryBBox)) {
+ return;
+ } else if (completeIntersection(queryBBox)) {
+ for (auto& element : boxElements) {
+ if (resultFn(element.first, element.second)) {
+ return;
+ }
+ }
+ for (auto& element : circleElements) {
+ if (resultFn(element.first, convertToBox(element.second))) {
+ return;
+ }
+ }
+ return;
+ }
+
+ auto cx1 = convertToXCellCoord(queryBBox.min.x);
+ auto cy1 = convertToYCellCoord(queryBBox.min.y);
+ auto cx2 = convertToXCellCoord(queryBBox.max.x);
+ auto cy2 = convertToYCellCoord(queryBBox.max.y);
- int32_t x, y, cellIndex;
+ int16_t x, y, cellIndex;
for (x = cx1; x <= cx2; ++x) {
for (y = cy1; y <= cy2; ++y) {
- cellIndex = d * y + x;
- for (auto uid : cells[cellIndex]) {
- if (seenUids.count(uid) == 0) {
- seenUids.insert(uid);
+ cellIndex = xCellCount * y + x;
+ // Look up other boxes
+ for (auto uid : boxCells[cellIndex]) {
+ if (seenBoxes.count(uid) == 0) {
+ seenBoxes.insert(uid);
- auto& pair = elements.at(uid);
+ auto& pair = boxElements.at(uid);
auto& bbox = pair.second;
- if (queryBBox.min.x <= bbox.max.x &&
- queryBBox.min.y <= bbox.max.y &&
- queryBBox.max.x >= bbox.min.x &&
- queryBBox.max.y >= bbox.min.y) {
+ if (boxesCollide(queryBBox, bbox)) {
+ if (resultFn(pair.first, bbox)) {
+ return;
+ }
+ }
+ }
+ }
+
+ // Look up circles
+ for (auto uid : circleCells[cellIndex]) {
+ if (seenCircles.count(uid) == 0) {
+ seenCircles.insert(uid);
- result.push_back(pair.first);
+ auto& pair = circleElements.at(uid);
+ auto& bcircle = pair.second;
+ if (circleAndBoxCollide(bcircle, queryBBox)) {
+ if (resultFn(pair.first, convertToBox(bcircle))) {
+ return;
+ }
}
}
}
}
}
+}
- return result;
+template <class T>
+void GridIndex<T>::query(const BCircle& queryBCircle, std::function<bool (const T&, const BBox&)> resultFn) const {
+ std::unordered_set<size_t> seenBoxes;
+ std::unordered_set<size_t> seenCircles;
+
+ BBox queryBBox = convertToBox(queryBCircle);
+ if (noIntersection(queryBBox)) {
+ return;
+ } else if (completeIntersection(queryBBox)) {
+ for (auto& element : boxElements) {
+ if (resultFn(element.first, element.second)) {
+ return;
+ }
+ }
+ for (auto& element : circleElements) {
+ if (resultFn(element.first, convertToBox(element.second))) {
+ return;
+ }
+ }
+ }
+
+ auto cx1 = convertToXCellCoord(queryBCircle.center.x - queryBCircle.radius);
+ auto cy1 = convertToYCellCoord(queryBCircle.center.y - queryBCircle.radius);
+ auto cx2 = convertToXCellCoord(queryBCircle.center.x + queryBCircle.radius);
+ auto cy2 = convertToYCellCoord(queryBCircle.center.y + queryBCircle.radius);
+
+ int16_t x, y, cellIndex;
+ for (x = cx1; x <= cx2; ++x) {
+ for (y = cy1; y <= cy2; ++y) {
+ cellIndex = xCellCount * y + x;
+ // Look up boxes
+ for (auto uid : boxCells[cellIndex]) {
+ if (seenBoxes.count(uid) == 0) {
+ seenBoxes.insert(uid);
+
+ auto& pair = boxElements.at(uid);
+ auto& bbox = pair.second;
+ if (circleAndBoxCollide(queryBCircle, bbox)) {
+ if (resultFn(pair.first, bbox)) {
+ return;
+ }
+ }
+ }
+ }
+
+ // Look up other circles
+ for (auto uid : circleCells[cellIndex]) {
+ if (seenCircles.count(uid) == 0) {
+ seenCircles.insert(uid);
+
+ auto& pair = circleElements.at(uid);
+ auto& bcircle = pair.second;
+ if (circlesCollide(queryBCircle, bcircle)) {
+ if (resultFn(pair.first, convertToBox(bcircle))) {
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
}
+template <class T>
+int16_t GridIndex<T>::convertToXCellCoord(const float x) const {
+ return util::max(0.0, util::min(xCellCount - 1.0, std::floor(x * xScale)));
+}
+
+template <class T>
+int16_t GridIndex<T>::convertToYCellCoord(const float y) const {
+ return util::max(0.0, util::min(yCellCount - 1.0, std::floor(y * yScale)));
+}
template <class T>
-int32_t GridIndex<T>::convertToCellCoord(int32_t x) const {
- return util::max(0.0, util::min(d - 1.0, std::floor(x * scale) + padding));
+bool GridIndex<T>::boxesCollide(const BBox& first, const BBox& second) const {
+ return first.min.x <= second.max.x &&
+ first.min.y <= second.max.y &&
+ first.max.x >= second.min.x &&
+ first.max.y >= second.min.y;
}
+template <class T>
+bool GridIndex<T>::circlesCollide(const BCircle& first, const BCircle& second) const {
+ auto dx = second.center.x - first.center.x;
+ auto dy = second.center.y - first.center.y;
+ auto bothRadii = first.radius + second.radius;
+ return (bothRadii * bothRadii) > (dx * dx + dy * dy);
+}
+
+template <class T>
+bool GridIndex<T>::circleAndBoxCollide(const BCircle& circle, const BBox& box) const {
+ auto halfRectWidth = (box.max.x - box.min.x) / 2;
+ auto distX = std::abs(circle.center.x - (box.min.x + halfRectWidth));
+ if (distX > (halfRectWidth + circle.radius)) {
+ return false;
+ }
+
+ auto halfRectHeight = (box.max.y - box.min.y) / 2;
+ auto distY = std::abs(circle.center.y - (box.min.y + halfRectHeight));
+ if (distY > (halfRectHeight + circle.radius)) {
+ return false;
+ }
+
+ if (distX <= halfRectWidth || distY <= halfRectHeight) {
+ return true;
+ }
+
+ auto dx = distX - halfRectWidth;
+ auto dy = distY - halfRectHeight;
+ return (dx * dx + dy * dy) <= (circle.radius * circle.radius);
+}
+
+template <class T>
+bool GridIndex<T>::empty() const {
+ return boxElements.empty() && circleElements.empty();
+}
+
+
template class GridIndex<IndexedSubfeature>;
+
} // namespace mbgl
diff --git a/src/mbgl/util/grid_index.hpp b/src/mbgl/util/grid_index.hpp
index 8ef8fb35b7..6ef2966bee 100644
--- a/src/mbgl/util/grid_index.hpp
+++ b/src/mbgl/util/grid_index.hpp
@@ -6,32 +6,100 @@
#include <cstdint>
#include <cstddef>
#include <vector>
+#include <functional>
namespace mbgl {
+namespace geometry {
+
+template <typename T>
+struct circle
+{
+ using point_type = mapbox::geometry::point<T>;
+
+ constexpr circle(point_type const& center_, T const& radius_)
+ : center(center_), radius(radius_)
+ {}
+
+ point_type center;
+ T radius;
+};
+
+template <typename T>
+constexpr bool operator==(circle<T> const& lhs, circle<T> const& rhs)
+{
+ return lhs.center == rhs.center && lhs.radius == rhs.radius;
+}
+
+template <typename T>
+constexpr bool operator!=(circle<T> const& lhs, circle<T> const& rhs)
+{
+ return lhs.center != rhs.center || lhs.radius != rhs.radius;
+}
+
+} // namespace geometry
+
+
+/*
+ GridIndex is a data structure for testing the intersection of
+ circles and rectangles in a 2d plane.
+ It is optimized for rapid insertion and querying.
+ GridIndex splits the plane into a set of "cells" and keeps track
+ of which geometries intersect with each cell. At query time,
+ full geometry comparisons are only done for items that share
+ at least one cell. As long as the geometries are relatively
+ uniformly distributed across the plane, this greatly reduces
+ the number of comparisons necessary.
+*/
+
template <class T>
class GridIndex {
public:
- GridIndex(int32_t extent_, int32_t n_, int32_t padding_);
- using BBox = mapbox::geometry::box<int16_t>;
+ GridIndex(const float width_, const float height_, const int16_t cellSize_);
+
+ using BBox = mapbox::geometry::box<float>;
+ using BCircle = geometry::circle<float>;
void insert(T&& t, const BBox&);
+ void insert(T&& t, const BCircle&);
+
std::vector<T> query(const BBox&) const;
+ std::vector<std::pair<T,BBox>> queryWithBoxes(const BBox&) const;
+
+ bool hitTest(const BBox&) const;
+ bool hitTest(const BCircle&) const;
+
+ bool empty() const;
private:
- int32_t convertToCellCoord(int32_t x) const;
-
- const int32_t extent;
- const int32_t n;
- const int32_t padding;
- const int32_t d;
- const double scale;
- const int32_t min;
- const int32_t max;
-
- std::vector<std::pair<T, BBox>> elements;
- std::vector<std::vector<size_t>> cells;
+ bool noIntersection(const BBox& queryBBox) const;
+ bool completeIntersection(const BBox& queryBBox) const;
+ BBox convertToBox(const BCircle& circle) const;
+
+ void query(const BBox&, std::function<bool (const T&, const BBox&)>) const;
+ void query(const BCircle&, std::function<bool (const T&, const BBox&)>) const;
+
+ int16_t convertToXCellCoord(const float x) const;
+ int16_t convertToYCellCoord(const float y) const;
+
+ bool boxesCollide(const BBox&, const BBox&) const;
+ bool circlesCollide(const BCircle&, const BCircle&) const;
+ bool circleAndBoxCollide(const BCircle&, const BBox&) const;
+
+ const float width;
+ const float height;
+
+ const int16_t xCellCount;
+ const int16_t yCellCount;
+ const double xScale;
+ const double yScale;
+
+ std::vector<std::pair<T, BBox>> boxElements;
+ std::vector<std::pair<T, BCircle>> circleElements;
+
+ std::vector<std::vector<size_t>> boxCells;
+ std::vector<std::vector<size_t>> circleCells;
};
diff --git a/src/mbgl/util/http_header.cpp b/src/mbgl/util/http_header.cpp
index ce31a06c5e..5921edfb14 100644
--- a/src/mbgl/util/http_header.cpp
+++ b/src/mbgl/util/http_header.cpp
@@ -6,9 +6,12 @@
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wshadow"
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wshorten-64-to-32"
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
+#pragma clang diagnostic pop
#pragma GCC diagnostic pop
namespace mbgl {
diff --git a/src/mbgl/util/http_timeout.cpp b/src/mbgl/util/http_timeout.cpp
index 3456369250..04842e48be 100644
--- a/src/mbgl/util/http_timeout.cpp
+++ b/src/mbgl/util/http_timeout.cpp
@@ -1,6 +1,7 @@
#include <mbgl/util/http_timeout.hpp>
#include <mbgl/util/constants.hpp>
+#include <algorithm>
#include <cassert>
namespace mbgl {
diff --git a/src/mbgl/util/mapbox.cpp b/src/mbgl/util/mapbox.cpp
index 8cbc85d492..cdd51a293d 100644
--- a/src/mbgl/util/mapbox.cpp
+++ b/src/mbgl/util/mapbox.cpp
@@ -114,7 +114,7 @@ std::string normalizeTileURL(const std::string& baseURL,
}
std::string
-canonicalizeTileURL(const std::string& str, const SourceType type, const uint16_t tileSize) {
+canonicalizeTileURL(const std::string& str, const style::SourceType type, const uint16_t tileSize) {
const char* version = "/v4/";
const size_t versionLen = strlen(version);
@@ -133,7 +133,7 @@ canonicalizeTileURL(const std::string& str, const SourceType type, const uint16_
std::string result = "mapbox://tiles/";
result.append(str, path.directory.first + versionLen, path.directory.second - versionLen);
result.append(str, path.filename.first, path.filename.second);
- if (type == SourceType::Raster) {
+ if (type == style::SourceType::Raster || type == style::SourceType::RasterDEM) {
result += tileSize == util::tileSize ? "@2x" : "{ratio}";
}
@@ -171,7 +171,7 @@ canonicalizeTileURL(const std::string& str, const SourceType type, const uint16_
return result;
}
-void canonicalizeTileset(Tileset& tileset, const std::string& sourceURL, SourceType type, uint16_t tileSize) {
+void canonicalizeTileset(Tileset& tileset, const std::string& sourceURL, style::SourceType type, uint16_t tileSize) {
// TODO: Remove this hack by delivering proper URLs in the TileJSON to begin with.
if (isMapboxURL(sourceURL)) {
for (auto& url : tileset.tiles) {
diff --git a/src/mbgl/util/mapbox.hpp b/src/mbgl/util/mapbox.hpp
index f3dfdd0b01..aa128f2667 100644
--- a/src/mbgl/util/mapbox.hpp
+++ b/src/mbgl/util/mapbox.hpp
@@ -19,10 +19,10 @@ std::string normalizeGlyphsURL(const std::string& baseURL, const std::string& ur
std::string normalizeTileURL(const std::string& baseURL, const std::string& url, const std::string& accessToken);
// Return a "mapbox://tiles/..." URL (suitable for normalizeTileURL) for the given Mapbox tile URL.
-std::string canonicalizeTileURL(const std::string& url, SourceType, uint16_t tileSize);
+std::string canonicalizeTileURL(const std::string& url, style::SourceType, uint16_t tileSize);
// Replace URL templates with "mapbox://tiles/..." URLs (suitable for normalizeTileURL).
-void canonicalizeTileset(Tileset&, const std::string& url, SourceType, uint16_t tileSize);
+void canonicalizeTileset(Tileset&, const std::string& url, style::SourceType, uint16_t tileSize);
extern const uint64_t DEFAULT_OFFLINE_TILE_COUNT_LIMIT;
diff --git a/src/mbgl/util/offscreen_texture.cpp b/src/mbgl/util/offscreen_texture.cpp
index 339e74b250..03f555eae0 100644
--- a/src/mbgl/util/offscreen_texture.cpp
+++ b/src/mbgl/util/offscreen_texture.cpp
@@ -11,20 +11,21 @@ OffscreenTexture& OffscreenTexture::operator=(OffscreenTexture&&) = default;
class OffscreenTexture::Impl {
public:
- Impl(gl::Context& context_, const Size size_)
- : context(context_), size(std::move(size_)) {
+ Impl(gl::Context& context_, const Size size_, const gl::TextureType type_)
+ : context(context_), size(std::move(size_)), type(type_) {
assert(!size.isEmpty());
}
Impl(gl::Context& context_,
const Size size_,
- gl::Renderbuffer<gl::RenderbufferType::DepthComponent>& depth_)
- : context(context_), size(std::move(size_)), depth(&depth_) {
+ gl::Renderbuffer<gl::RenderbufferType::DepthComponent>& depth_,
+ const gl::TextureType type_)
+ : context(context_), size(std::move(size_)), depth(&depth_), type(type_) {
assert(!size.isEmpty());
}
void bind() {
if (!framebuffer) {
- texture = context.createTexture(size, gl::TextureFormat::RGBA);
+ texture = context.createTexture(size, gl::TextureFormat::RGBA, 0, type);
if (depth) {
framebuffer = context.createFramebuffer(*texture, *depth);
} else {
@@ -58,18 +59,21 @@ private:
optional<gl::Framebuffer> framebuffer;
optional<gl::Texture> texture;
gl::Renderbuffer<gl::RenderbufferType::DepthComponent>* depth = nullptr;
+ const gl::TextureType type;
};
OffscreenTexture::OffscreenTexture(gl::Context& context,
- const Size size)
- : impl(std::make_unique<Impl>(context, std::move(size))) {
+ const Size size,
+ const gl::TextureType type)
+ : impl(std::make_unique<Impl>(context, std::move(size), type)) {
assert(!size.isEmpty());
}
OffscreenTexture::OffscreenTexture(gl::Context& context,
const Size size,
- gl::Renderbuffer<gl::RenderbufferType::DepthComponent>& renderbuffer)
- : impl(std::make_unique<Impl>(context, std::move(size), renderbuffer)) {
+ gl::Renderbuffer<gl::RenderbufferType::DepthComponent>& renderbuffer,
+ const gl::TextureType type)
+ : impl(std::make_unique<Impl>(context, std::move(size), renderbuffer, type)) {
assert(!size.isEmpty());
}
diff --git a/src/mbgl/util/offscreen_texture.hpp b/src/mbgl/util/offscreen_texture.hpp
index 7f7e0f0338..36f24f16d3 100644
--- a/src/mbgl/util/offscreen_texture.hpp
+++ b/src/mbgl/util/offscreen_texture.hpp
@@ -12,10 +12,12 @@ class Texture;
class OffscreenTexture {
public:
OffscreenTexture(gl::Context&,
- Size size = { 256, 256 });
+ Size size = { 256, 256 },
+ gl::TextureType type = gl::TextureType::UnsignedByte);
OffscreenTexture(gl::Context&,
Size size,
- gl::Renderbuffer<gl::RenderbufferType::DepthComponent>&);
+ gl::Renderbuffer<gl::RenderbufferType::DepthComponent>&,
+ gl::TextureType type = gl::TextureType::UnsignedByte);
~OffscreenTexture();
OffscreenTexture(OffscreenTexture&&);
OffscreenTexture& operator=(OffscreenTexture&&);
diff --git a/src/mbgl/util/tile_cover.cpp b/src/mbgl/util/tile_cover.cpp
index a5a1b1d70c..39b562d811 100644
--- a/src/mbgl/util/tile_cover.cpp
+++ b/src/mbgl/util/tile_cover.cpp
@@ -126,9 +126,9 @@ std::vector<UnwrappedTileID> tileCover(const Point<double>& tl,
} // namespace
-int32_t coveringZoomLevel(double zoom, SourceType type, uint16_t size) {
+int32_t coveringZoomLevel(double zoom, style::SourceType type, uint16_t size) {
zoom += std::log(util::tileSize / size) / std::log(2);
- if (type == SourceType::Raster || type == SourceType::Video) {
+ if (type == style::SourceType::Raster || type == style::SourceType::Video) {
return ::round(zoom);
} else {
return std::floor(zoom);
@@ -182,10 +182,10 @@ uint64_t tileCount(const LatLngBounds& bounds, uint8_t zoom, uint16_t tileSize_)
auto y1 = floor(sw.y/ tileSize_);
auto y2 = floor((ne.y - 1) / tileSize_);
- auto minX = std::fmax(std::min(x1, x2), 0);
+ auto minX = ::fmax(std::min(x1, x2), 0);
auto maxX = std::max(x1, x2);
auto minY = (std::pow(2, zoom) - 1) - std::max(y1, y2);
- auto maxY = (std::pow(2, zoom) - 1) - std::fmax(std::min(y1, y2), 0);
+ auto maxY = (std::pow(2, zoom) - 1) - ::fmax(std::min(y1, y2), 0);
return (maxX - minX + 1) * (maxY - minY + 1);
}
diff --git a/src/mbgl/util/tile_cover.hpp b/src/mbgl/util/tile_cover.hpp
index 3c7a4ee44a..b2098b59b8 100644
--- a/src/mbgl/util/tile_cover.hpp
+++ b/src/mbgl/util/tile_cover.hpp
@@ -13,7 +13,7 @@ class LatLngBounds;
namespace util {
-int32_t coveringZoomLevel(double z, SourceType type, uint16_t tileSize);
+int32_t coveringZoomLevel(double z, style::SourceType type, uint16_t tileSize);
std::vector<UnwrappedTileID> tileCover(const TransformState&, int32_t z);
std::vector<UnwrappedTileID> tileCover(const LatLngBounds&, int32_t z);
diff --git a/src/parsedate/parsedate.c b/src/parsedate/parsedate.c
index 46acceed75..7228c4edbc 100644
--- a/src/parsedate/parsedate.c
+++ b/src/parsedate/parsedate.c
@@ -418,7 +418,7 @@ static time_t my_timegm(struct my_tm *tm)
{
static const int month_days_cumulative [12] =
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
- int month, year, leap_days;
+ int month_, year, leap_days;
if(tm->tm_year < 70)
/* we don't support years before 1970 as they will cause this function
@@ -426,14 +426,14 @@ static time_t my_timegm(struct my_tm *tm)
return -1;
year = tm->tm_year + 1900;
- month = tm->tm_mon;
- if(month < 0) {
- year += (11 - month) / 12;
- month = 11 - (11 - month) % 12;
+ month_ = tm->tm_mon;
+ if(month_ < 0) {
+ year += (11 - month_) / 12;
+ month_ = 11 - (11 - month_) % 12;
}
- else if(month >= 12) {
- year -= month / 12;
- month = month % 12;
+ else if(month_ >= 12) {
+ year -= month_ / 12;
+ month_ = month_ % 12;
}
leap_days = year - (tm->tm_mon <= 1);
@@ -441,7 +441,7 @@ static time_t my_timegm(struct my_tm *tm)
- (1969 / 4) + (1969 / 100) - (1969 / 400));
return ((((time_t) (year - 1970) * 365
- + leap_days + month_days_cumulative [month] + tm->tm_mday - 1) * 24
+ + leap_days + month_days_cumulative [month_] + tm->tm_mday - 1) * 24
+ tm->tm_hour) * 60 + tm->tm_min) * 60 + tm->tm_sec;
}
diff --git a/test/api/annotations.test.cpp b/test/api/annotations.test.cpp
index b777615b4c..07257851ac 100644
--- a/test/api/annotations.test.cpp
+++ b/test/api/annotations.test.cpp
@@ -32,7 +32,7 @@ public:
float pixelRatio { 1 };
HeadlessFrontend frontend { pixelRatio, fileSource, threadPool };
Map map { frontend, MapObserver::nullObserver(), frontend.getSize(), pixelRatio, fileSource,
- threadPool, MapMode::Still };
+ threadPool, MapMode::Static};
void checkRendering(const char * name) {
test::checkImage(std::string("test/fixtures/annotations/") + name,
@@ -366,8 +366,8 @@ TEST(Annotations, QueryFractionalZoomLevels) {
test.map.addAnnotationImage(namedMarker("default_marker"));
std::vector<mbgl::AnnotationID> ids;
- for (int longitude = 0; longitude < 10; ++longitude) {
- for (int latitude = 0; latitude < 10; ++latitude) {
+ for (int longitude = 0; longitude < 10; longitude += 2) {
+ for (int latitude = 0; latitude < 10; latitude += 2) {
ids.push_back(test.map.addAnnotation(SymbolAnnotation { { double(latitude), double(longitude) }, "default_marker" }));
}
}
@@ -399,8 +399,8 @@ TEST(Annotations, VisibleFeatures) {
test.map.setLatLngZoom({ 5, 5 }, 3);
std::vector<mbgl::AnnotationID> ids;
- for (int longitude = 0; longitude < 10; ++longitude) {
- for (int latitude = 0; latitude <= 10; ++latitude) {
+ for (int longitude = 0; longitude < 10; longitude += 2) {
+ for (int latitude = 0; latitude <= 10; latitude += 2) {
ids.push_back(test.map.addAnnotation(SymbolAnnotation { { double(latitude), double(longitude) }, "default_marker" }));
}
}
diff --git a/test/api/api_misuse.test.cpp b/test/api/api_misuse.test.cpp
index 690c1548e5..363958451b 100644
--- a/test/api/api_misuse.test.cpp
+++ b/test/api/api_misuse.test.cpp
@@ -27,7 +27,7 @@ TEST(API, RenderWithoutCallback) {
HeadlessFrontend frontend { pixelRatio, fileSource, threadPool };
auto map = std::make_unique<Map>(frontend, MapObserver::nullObserver(), frontend.getSize(),
- pixelRatio, fileSource, threadPool, MapMode::Still);
+ pixelRatio, fileSource, threadPool, MapMode::Static);
map->renderStill(nullptr);
// Force Map thread to join.
diff --git a/test/api/custom_geometry_source.test.cpp b/test/api/custom_geometry_source.test.cpp
new file mode 100644
index 0000000000..83d1543a0a
--- /dev/null
+++ b/test/api/custom_geometry_source.test.cpp
@@ -0,0 +1,71 @@
+#include <mbgl/test/util.hpp>
+
+#include <mbgl/gl/gl.hpp>
+#include <mbgl/map/map.hpp>
+#include <mbgl/util/shared_thread_pool.hpp>
+#include <mbgl/storage/default_file_source.hpp>
+#include <mbgl/gl/headless_frontend.hpp>
+#include <mbgl/style/style.hpp>
+#include <mbgl/style/sources/custom_geometry_source.hpp>
+#include <mbgl/style/layers/fill_layer.hpp>
+#include <mbgl/style/layers/line_layer.hpp>
+#include <mbgl/util/geojson.hpp>
+#include <mbgl/util/io.hpp>
+#include <mbgl/util/mat4.hpp>
+#include <mbgl/util/run_loop.hpp>
+
+using namespace mbgl;
+using namespace mbgl::style;
+
+TEST(CustomGeometrySource, Grid) {
+ util::RunLoop loop;
+
+ DefaultFileSource fileSource(":memory:", "test/fixtures/api/assets");
+ auto threadPool = sharedThreadPool();
+ float pixelRatio { 1 };
+ HeadlessFrontend frontend { pixelRatio, fileSource, *threadPool };
+ Map map(frontend, MapObserver::nullObserver(), frontend.getSize(), pixelRatio, fileSource,
+ *threadPool, MapMode::Static);
+ map.getStyle().loadJSON(util::read_file("test/fixtures/api/water.json"));
+ map.setLatLngZoom({ 37.8, -122.5 }, 10);
+
+ CustomGeometrySource::Options options;
+ options.fetchTileFunction = [&map](const mbgl::CanonicalTileID& tileID) {
+ double gridSpacing = 0.1;
+ FeatureCollection features;
+ const LatLngBounds bounds(tileID);
+ for (double y = ceil(bounds.north() / gridSpacing) * gridSpacing; y >= floor(bounds.south() / gridSpacing) * gridSpacing; y -= gridSpacing) {
+
+ mapbox::geojson::line_string gridLine;
+ gridLine.emplace_back(bounds.west(), y);
+ gridLine.emplace_back(bounds.east(), y);
+
+ features.emplace_back(gridLine);
+ }
+
+ for (double x = floor(bounds.west() / gridSpacing) * gridSpacing; x <= ceil(bounds.east() / gridSpacing) * gridSpacing; x += gridSpacing) {
+ mapbox::geojson::line_string gridLine;
+ gridLine.emplace_back(x, bounds.south());
+ gridLine.emplace_back(x, bounds.north());
+
+ features.emplace_back(gridLine);
+ }
+ auto source = static_cast<CustomGeometrySource *>(map.getStyle().getSource("custom"));
+ if (source) {
+ source->setTileData(tileID, features);
+ }
+ };
+
+ map.getStyle().addSource(std::make_unique<CustomGeometrySource>("custom", options));
+
+ auto fillLayer = std::make_unique<FillLayer>("landcover", "mapbox");
+ fillLayer->setSourceLayer("landcover");
+ fillLayer->setFillColor(Color{ 1.0, 1.0, 0.0, 1.0 });
+ map.getStyle().addLayer(std::move(fillLayer));
+
+ auto layer = std::make_unique<LineLayer>("grid", "custom");
+ layer->setLineColor(Color{ 1.0, 1.0, 1.0, 1.0 });
+ map.getStyle().addLayer(std::move(layer));
+
+ test::checkImage("test/fixtures/custom_geometry_source/grid", frontend.render(map), 0.0006, 0.1);
+}
diff --git a/test/api/custom_layer.test.cpp b/test/api/custom_layer.test.cpp
index 1c514aeca2..eb1d7e0d3a 100644
--- a/test/api/custom_layer.test.cpp
+++ b/test/api/custom_layer.test.cpp
@@ -90,7 +90,7 @@ TEST(CustomLayer, Basic) {
float pixelRatio { 1 };
HeadlessFrontend frontend { pixelRatio, fileSource, threadPool };
Map map(frontend, MapObserver::nullObserver(), frontend.getSize(), pixelRatio, fileSource,
- threadPool, MapMode::Still);
+ threadPool, MapMode::Static);
map.getStyle().loadJSON(util::read_file("test/fixtures/api/water.json"));
map.setLatLngZoom({ 37.8, -122.5 }, 10);
map.getStyle().addLayer(std::make_unique<CustomLayer>(
diff --git a/test/api/query.test.cpp b/test/api/query.test.cpp
index 3f3825eb72..c67ff9064c 100644
--- a/test/api/query.test.cpp
+++ b/test/api/query.test.cpp
@@ -32,7 +32,7 @@ public:
float pixelRatio { 1 };
HeadlessFrontend frontend { pixelRatio, fileSource, threadPool };
Map map { frontend, MapObserver::nullObserver(), frontend.getSize(), pixelRatio, fileSource,
- threadPool, MapMode::Still };
+ threadPool, MapMode::Static};
};
} // end namespace
diff --git a/test/api/recycle_map.cpp b/test/api/recycle_map.cpp
index 8cd622fe79..ca6abac8c1 100644
--- a/test/api/recycle_map.cpp
+++ b/test/api/recycle_map.cpp
@@ -29,7 +29,7 @@ TEST(API, RecycleMapUpdateImages) {
HeadlessFrontend frontend { pixelRatio, fileSource, threadPool };
auto map = std::make_unique<Map>(frontend, MapObserver::nullObserver(), frontend.getSize(),
- pixelRatio, fileSource, threadPool, MapMode::Still);
+ pixelRatio, fileSource, threadPool, MapMode::Static);
EXPECT_TRUE(map);
diff --git a/test/api/zoom_history.cpp b/test/api/zoom_history.cpp
index 1b1159bf52..df9b6ff2a3 100644
--- a/test/api/zoom_history.cpp
+++ b/test/api/zoom_history.cpp
@@ -30,7 +30,7 @@ TEST(API, ZoomHistory) {
HeadlessFrontend frontend { pixelRatio, fileSource, threadPool };
auto map = std::make_unique<Map>(frontend, MapObserver::nullObserver(), frontend.getSize(),
- pixelRatio, fileSource, threadPool, MapMode::Still);
+ pixelRatio, fileSource, threadPool, MapMode::Static);
EXPECT_TRUE(map);
diff --git a/test/fixtures/annotations/debug_sparse/expected.png b/test/fixtures/annotations/debug_sparse/expected.png
index 3c1ad1599c..493928998d 100644
--- a/test/fixtures/annotations/debug_sparse/expected.png
+++ b/test/fixtures/annotations/debug_sparse/expected.png
Binary files differ
diff --git a/test/fixtures/api/query_style.json b/test/fixtures/api/query_style.json
index 2e499c383d..fd147f7899 100644
--- a/test/fixtures/api/query_style.json
+++ b/test/fixtures/api/query_style.json
@@ -67,7 +67,8 @@
"type": "symbol",
"source": "source1",
"layout": {
- "icon-image": "test-icon"
+ "icon-image": "test-icon",
+ "icon-allow-overlap": true
}
},
{
@@ -75,7 +76,8 @@
"type": "symbol",
"source": "source2",
"layout": {
- "icon-image": "test-icon"
+ "icon-image": "test-icon",
+ "icon-allow-overlap": true
}
},
{
@@ -83,7 +85,8 @@
"type": "symbol",
"source": "source3",
"layout": {
- "icon-image": "test-icon"
+ "icon-image": "test-icon",
+ "icon-allow-overlap": true
}
},
{
@@ -91,7 +94,8 @@
"type": "symbol",
"source": "source4",
"layout": {
- "icon-image": "test-icon"
+ "icon-image": "test-icon",
+ "icon-allow-overlap": true
}
}
]
diff --git a/test/fixtures/custom_geometry_source/grid/expected.png b/test/fixtures/custom_geometry_source/grid/expected.png
new file mode 100644
index 0000000000..628a3b11fb
--- /dev/null
+++ b/test/fixtures/custom_geometry_source/grid/expected.png
Binary files differ
diff --git a/test/fixtures/expression_equality/acos.a.json b/test/fixtures/expression_equality/acos.a.json
new file mode 100644
index 0000000000..1e9bb752ca
--- /dev/null
+++ b/test/fixtures/expression_equality/acos.a.json
@@ -0,0 +1,4 @@
+[
+ "acos",
+ 0.5
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/acos.b.json b/test/fixtures/expression_equality/acos.b.json
new file mode 100644
index 0000000000..54e035cb7e
--- /dev/null
+++ b/test/fixtures/expression_equality/acos.b.json
@@ -0,0 +1,4 @@
+[
+ "acos",
+ 1.5
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/all.a.json b/test/fixtures/expression_equality/all.a.json
new file mode 100644
index 0000000000..ec7154b7b9
--- /dev/null
+++ b/test/fixtures/expression_equality/all.a.json
@@ -0,0 +1,17 @@
+[
+ "all",
+ [
+ "boolean",
+ [
+ "get",
+ "x"
+ ]
+ ],
+ [
+ "boolean",
+ [
+ "get",
+ "y"
+ ]
+ ]
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/all.b.json b/test/fixtures/expression_equality/all.b.json
new file mode 100644
index 0000000000..8eab839bb0
--- /dev/null
+++ b/test/fixtures/expression_equality/all.b.json
@@ -0,0 +1,17 @@
+[
+ "all",
+ [
+ "boolean",
+ [
+ "get",
+ "x"
+ ]
+ ],
+ [
+ "boolean",
+ [
+ "get",
+ "y_other"
+ ]
+ ]
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/any.a.json b/test/fixtures/expression_equality/any.a.json
new file mode 100644
index 0000000000..3f044c1f79
--- /dev/null
+++ b/test/fixtures/expression_equality/any.a.json
@@ -0,0 +1,17 @@
+[
+ "any",
+ [
+ "boolean",
+ [
+ "get",
+ "x"
+ ]
+ ],
+ [
+ "boolean",
+ [
+ "get",
+ "y"
+ ]
+ ]
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/any.b.json b/test/fixtures/expression_equality/any.b.json
new file mode 100644
index 0000000000..720662751f
--- /dev/null
+++ b/test/fixtures/expression_equality/any.b.json
@@ -0,0 +1,17 @@
+[
+ "any",
+ [
+ "boolean",
+ [
+ "get",
+ "x"
+ ]
+ ],
+ [
+ "boolean",
+ [
+ "get",
+ "y_other"
+ ]
+ ]
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/array.a.json b/test/fixtures/expression_equality/array.a.json
new file mode 100644
index 0000000000..3c31303ca3
--- /dev/null
+++ b/test/fixtures/expression_equality/array.a.json
@@ -0,0 +1,11 @@
+[
+ "array",
+ [
+ "literal",
+ [
+ 1,
+ 2,
+ 3
+ ]
+ ]
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/array.b.json b/test/fixtures/expression_equality/array.b.json
new file mode 100644
index 0000000000..7606794d56
--- /dev/null
+++ b/test/fixtures/expression_equality/array.b.json
@@ -0,0 +1,11 @@
+[
+ "array",
+ [
+ "literal",
+ [
+ 1,
+ 2,
+ 4
+ ]
+ ]
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/asin.a.json b/test/fixtures/expression_equality/asin.a.json
new file mode 100644
index 0000000000..3cd730ccbf
--- /dev/null
+++ b/test/fixtures/expression_equality/asin.a.json
@@ -0,0 +1,4 @@
+[
+ "asin",
+ 0.5
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/asin.b.json b/test/fixtures/expression_equality/asin.b.json
new file mode 100644
index 0000000000..2c862c8cbe
--- /dev/null
+++ b/test/fixtures/expression_equality/asin.b.json
@@ -0,0 +1,4 @@
+[
+ "asin",
+ 1.5
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/at.a.json b/test/fixtures/expression_equality/at.a.json
new file mode 100644
index 0000000000..c69b0d933b
--- /dev/null
+++ b/test/fixtures/expression_equality/at.a.json
@@ -0,0 +1,20 @@
+[
+ "number",
+ [
+ "at",
+ [
+ "number",
+ [
+ "get",
+ "i"
+ ]
+ ],
+ [
+ "array",
+ [
+ "get",
+ "arr"
+ ]
+ ]
+ ]
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/at.b.json b/test/fixtures/expression_equality/at.b.json
new file mode 100644
index 0000000000..6e19c28606
--- /dev/null
+++ b/test/fixtures/expression_equality/at.b.json
@@ -0,0 +1,20 @@
+[
+ "number",
+ [
+ "at",
+ [
+ "number",
+ [
+ "get",
+ "i"
+ ]
+ ],
+ [
+ "array",
+ [
+ "get",
+ "arr_other"
+ ]
+ ]
+ ]
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/atan.a.json b/test/fixtures/expression_equality/atan.a.json
new file mode 100644
index 0000000000..b76406bc44
--- /dev/null
+++ b/test/fixtures/expression_equality/atan.a.json
@@ -0,0 +1,4 @@
+[
+ "atan",
+ 1
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/atan.b.json b/test/fixtures/expression_equality/atan.b.json
new file mode 100644
index 0000000000..aafbbb0594
--- /dev/null
+++ b/test/fixtures/expression_equality/atan.b.json
@@ -0,0 +1,4 @@
+[
+ "atan",
+ 2
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/boolean.a.json b/test/fixtures/expression_equality/boolean.a.json
new file mode 100644
index 0000000000..1230a2a926
--- /dev/null
+++ b/test/fixtures/expression_equality/boolean.a.json
@@ -0,0 +1,7 @@
+[
+ "boolean",
+ [
+ "get",
+ "x"
+ ]
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/boolean.b.json b/test/fixtures/expression_equality/boolean.b.json
new file mode 100644
index 0000000000..1ae91ef60c
--- /dev/null
+++ b/test/fixtures/expression_equality/boolean.b.json
@@ -0,0 +1,7 @@
+[
+ "boolean",
+ [
+ "get",
+ "x_other"
+ ]
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/case.a.json b/test/fixtures/expression_equality/case.a.json
new file mode 100644
index 0000000000..84049294f5
--- /dev/null
+++ b/test/fixtures/expression_equality/case.a.json
@@ -0,0 +1,14 @@
+[
+ "case",
+ [
+ "get",
+ "x"
+ ],
+ "x",
+ [
+ "get",
+ "y"
+ ],
+ "y",
+ "otherwise"
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/case.b.json b/test/fixtures/expression_equality/case.b.json
new file mode 100644
index 0000000000..038806043f
--- /dev/null
+++ b/test/fixtures/expression_equality/case.b.json
@@ -0,0 +1,14 @@
+[
+ "case",
+ [
+ "get",
+ "x"
+ ],
+ "x",
+ [
+ "get",
+ "y"
+ ],
+ "y",
+ "otherwise_other"
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/coalesce.a.json b/test/fixtures/expression_equality/coalesce.a.json
new file mode 100644
index 0000000000..8fae579e7c
--- /dev/null
+++ b/test/fixtures/expression_equality/coalesce.a.json
@@ -0,0 +1,16 @@
+[
+ "coalesce",
+ [
+ "get",
+ "x"
+ ],
+ [
+ "get",
+ "y"
+ ],
+ [
+ "get",
+ "z"
+ ],
+ 0
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/coalesce.b.json b/test/fixtures/expression_equality/coalesce.b.json
new file mode 100644
index 0000000000..4e0af8baa0
--- /dev/null
+++ b/test/fixtures/expression_equality/coalesce.b.json
@@ -0,0 +1,16 @@
+[
+ "coalesce",
+ [
+ "get",
+ "x"
+ ],
+ [
+ "get",
+ "y"
+ ],
+ [
+ "get",
+ "z"
+ ],
+ 1
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/concat.a.json b/test/fixtures/expression_equality/concat.a.json
new file mode 100644
index 0000000000..08c95d7f49
--- /dev/null
+++ b/test/fixtures/expression_equality/concat.a.json
@@ -0,0 +1,6 @@
+[
+ "concat",
+ "a",
+ "b",
+ "c"
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/concat.b.json b/test/fixtures/expression_equality/concat.b.json
new file mode 100644
index 0000000000..e3396d4fc0
--- /dev/null
+++ b/test/fixtures/expression_equality/concat.b.json
@@ -0,0 +1,6 @@
+[
+ "concat",
+ "a",
+ "b",
+ "c_other"
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/cos.a.json b/test/fixtures/expression_equality/cos.a.json
new file mode 100644
index 0000000000..e41430de53
--- /dev/null
+++ b/test/fixtures/expression_equality/cos.a.json
@@ -0,0 +1,4 @@
+[
+ "cos",
+ 0
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/cos.b.json b/test/fixtures/expression_equality/cos.b.json
new file mode 100644
index 0000000000..5ba4424dae
--- /dev/null
+++ b/test/fixtures/expression_equality/cos.b.json
@@ -0,0 +1,4 @@
+[
+ "cos",
+ 1
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/divide.a.json b/test/fixtures/expression_equality/divide.a.json
new file mode 100644
index 0000000000..40a67a871c
--- /dev/null
+++ b/test/fixtures/expression_equality/divide.a.json
@@ -0,0 +1,5 @@
+[
+ "/",
+ 10,
+ 5
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/divide.b.json b/test/fixtures/expression_equality/divide.b.json
new file mode 100644
index 0000000000..e3f7b155b2
--- /dev/null
+++ b/test/fixtures/expression_equality/divide.b.json
@@ -0,0 +1,5 @@
+[
+ "/",
+ 10,
+ 6
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/downcase.a.json b/test/fixtures/expression_equality/downcase.a.json
new file mode 100644
index 0000000000..ca367218c4
--- /dev/null
+++ b/test/fixtures/expression_equality/downcase.a.json
@@ -0,0 +1,4 @@
+[
+ "downcase",
+ "StRiNg"
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/downcase.b.json b/test/fixtures/expression_equality/downcase.b.json
new file mode 100644
index 0000000000..fd9ea9881d
--- /dev/null
+++ b/test/fixtures/expression_equality/downcase.b.json
@@ -0,0 +1,4 @@
+[
+ "downcase",
+ "StRiNg_other"
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/get.a.json b/test/fixtures/expression_equality/get.a.json
new file mode 100644
index 0000000000..57c3df48e7
--- /dev/null
+++ b/test/fixtures/expression_equality/get.a.json
@@ -0,0 +1,7 @@
+[
+ "number",
+ [
+ "get",
+ "x"
+ ]
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/get.b.json b/test/fixtures/expression_equality/get.b.json
new file mode 100644
index 0000000000..d1843362d3
--- /dev/null
+++ b/test/fixtures/expression_equality/get.b.json
@@ -0,0 +1,7 @@
+[
+ "number",
+ [
+ "get",
+ "x_other"
+ ]
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/has.a.json b/test/fixtures/expression_equality/has.a.json
new file mode 100644
index 0000000000..8326754107
--- /dev/null
+++ b/test/fixtures/expression_equality/has.a.json
@@ -0,0 +1,4 @@
+[
+ "has",
+ "x"
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/has.b.json b/test/fixtures/expression_equality/has.b.json
new file mode 100644
index 0000000000..20b6072303
--- /dev/null
+++ b/test/fixtures/expression_equality/has.b.json
@@ -0,0 +1,4 @@
+[
+ "has",
+ "x_other"
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/heatmap-density.a.json b/test/fixtures/expression_equality/heatmap-density.a.json
new file mode 100644
index 0000000000..90bd396f54
--- /dev/null
+++ b/test/fixtures/expression_equality/heatmap-density.a.json
@@ -0,0 +1,23 @@
+[
+ "interpolate",
+ [
+ "linear"
+ ],
+ [
+ "heatmap-density"
+ ],
+ 0,
+ [
+ "rgb",
+ 0,
+ 0,
+ 255
+ ],
+ 1,
+ [
+ "rgb",
+ 255,
+ 0,
+ 0
+ ]
+]
diff --git a/test/fixtures/expression_equality/heatmap-density.b.json b/test/fixtures/expression_equality/heatmap-density.b.json
new file mode 100644
index 0000000000..bce8ab03a1
--- /dev/null
+++ b/test/fixtures/expression_equality/heatmap-density.b.json
@@ -0,0 +1,23 @@
+[
+ "interpolate",
+ [
+ "linear"
+ ],
+ [
+ "heatmap-density"
+ ],
+ 0,
+ [
+ "rgb",
+ 0,
+ 0,
+ 255
+ ],
+ 1,
+ [
+ "rgb",
+ 255,
+ 255,
+ 255
+ ]
+]
diff --git a/test/fixtures/expression_equality/let.a.json b/test/fixtures/expression_equality/let.a.json
new file mode 100644
index 0000000000..fb24e50cfb
--- /dev/null
+++ b/test/fixtures/expression_equality/let.a.json
@@ -0,0 +1,25 @@
+[
+ "let",
+ "a",
+ 1,
+ "b",
+ 2,
+ [
+ "+",
+ [
+ "+",
+ [
+ "var",
+ "a"
+ ],
+ [
+ "var",
+ "b"
+ ]
+ ],
+ [
+ "var",
+ "a"
+ ]
+ ]
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/let.b.json b/test/fixtures/expression_equality/let.b.json
new file mode 100644
index 0000000000..26813cb6ff
--- /dev/null
+++ b/test/fixtures/expression_equality/let.b.json
@@ -0,0 +1,25 @@
+[
+ "let",
+ "a",
+ 1,
+ "b",
+ 3,
+ [
+ "+",
+ [
+ "+",
+ [
+ "var",
+ "a"
+ ],
+ [
+ "var",
+ "b"
+ ]
+ ],
+ [
+ "var",
+ "b"
+ ]
+ ]
+]
diff --git a/test/fixtures/expression_equality/ln.a.json b/test/fixtures/expression_equality/ln.a.json
new file mode 100644
index 0000000000..30d80f36ae
--- /dev/null
+++ b/test/fixtures/expression_equality/ln.a.json
@@ -0,0 +1,4 @@
+[
+ "ln",
+ 2
+]
diff --git a/test/fixtures/expression_equality/ln.b.json b/test/fixtures/expression_equality/ln.b.json
new file mode 100644
index 0000000000..9bc04ad586
--- /dev/null
+++ b/test/fixtures/expression_equality/ln.b.json
@@ -0,0 +1,6 @@
+[
+ "ln",
+ [
+ "e"
+ ]
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/log10.a.json b/test/fixtures/expression_equality/log10.a.json
new file mode 100644
index 0000000000..32e4c18807
--- /dev/null
+++ b/test/fixtures/expression_equality/log10.a.json
@@ -0,0 +1,4 @@
+[
+ "log10",
+ 100
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/log10.b.json b/test/fixtures/expression_equality/log10.b.json
new file mode 100644
index 0000000000..8f32c204f9
--- /dev/null
+++ b/test/fixtures/expression_equality/log10.b.json
@@ -0,0 +1,4 @@
+[
+ "log10",
+ 101
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/log2.a.json b/test/fixtures/expression_equality/log2.a.json
new file mode 100644
index 0000000000..95cdc15373
--- /dev/null
+++ b/test/fixtures/expression_equality/log2.a.json
@@ -0,0 +1,4 @@
+[
+ "log2",
+ 1024
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/log2.b.json b/test/fixtures/expression_equality/log2.b.json
new file mode 100644
index 0000000000..2fffaeb32a
--- /dev/null
+++ b/test/fixtures/expression_equality/log2.b.json
@@ -0,0 +1,4 @@
+[
+ "log2",
+ 1025
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/match.a.json b/test/fixtures/expression_equality/match.a.json
new file mode 100644
index 0000000000..ba8afc4126
--- /dev/null
+++ b/test/fixtures/expression_equality/match.a.json
@@ -0,0 +1,12 @@
+[
+ "match",
+ [
+ "get",
+ "x"
+ ],
+ "a",
+ "Apple",
+ "b",
+ "Banana",
+ "Kumquat"
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/match.b.json b/test/fixtures/expression_equality/match.b.json
new file mode 100644
index 0000000000..2404b8e2e7
--- /dev/null
+++ b/test/fixtures/expression_equality/match.b.json
@@ -0,0 +1,12 @@
+[
+ "match",
+ [
+ "get",
+ "x"
+ ],
+ "a",
+ "Apple",
+ "b",
+ "Banana",
+ "Kumquat_other"
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/max.a.json b/test/fixtures/expression_equality/max.a.json
new file mode 100644
index 0000000000..09a8f82bd7
--- /dev/null
+++ b/test/fixtures/expression_equality/max.a.json
@@ -0,0 +1,6 @@
+[
+ "max",
+ 0,
+ -1,
+ 100
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/max.b.json b/test/fixtures/expression_equality/max.b.json
new file mode 100644
index 0000000000..1b0beb20d6
--- /dev/null
+++ b/test/fixtures/expression_equality/max.b.json
@@ -0,0 +1,6 @@
+[
+ "max",
+ 0,
+ -1,
+ 101
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/min.a.json b/test/fixtures/expression_equality/min.a.json
new file mode 100644
index 0000000000..38cc90f1cd
--- /dev/null
+++ b/test/fixtures/expression_equality/min.a.json
@@ -0,0 +1,5 @@
+[
+ "min",
+ ["get", "x"],
+ 0
+]
diff --git a/test/fixtures/expression_equality/min.b.json b/test/fixtures/expression_equality/min.b.json
new file mode 100644
index 0000000000..84a5f66842
--- /dev/null
+++ b/test/fixtures/expression_equality/min.b.json
@@ -0,0 +1,5 @@
+[
+ "min",
+ ["get", "x"],
+ 1
+]
diff --git a/test/fixtures/expression_equality/minus.a.json b/test/fixtures/expression_equality/minus.a.json
new file mode 100644
index 0000000000..9eb4f954e7
--- /dev/null
+++ b/test/fixtures/expression_equality/minus.a.json
@@ -0,0 +1,5 @@
+[
+ "-",
+ 5,
+ 7
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/minus.b.json b/test/fixtures/expression_equality/minus.b.json
new file mode 100644
index 0000000000..87042b98ef
--- /dev/null
+++ b/test/fixtures/expression_equality/minus.b.json
@@ -0,0 +1,5 @@
+[
+ "-",
+ 5,
+ 8
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/mod.a.json b/test/fixtures/expression_equality/mod.a.json
new file mode 100644
index 0000000000..8439bafcd1
--- /dev/null
+++ b/test/fixtures/expression_equality/mod.a.json
@@ -0,0 +1,5 @@
+[
+ "%",
+ 18,
+ 12
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/mod.b.json b/test/fixtures/expression_equality/mod.b.json
new file mode 100644
index 0000000000..362e1721c1
--- /dev/null
+++ b/test/fixtures/expression_equality/mod.b.json
@@ -0,0 +1,5 @@
+[
+ "%",
+ 18,
+ 13
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/not.a.json b/test/fixtures/expression_equality/not.a.json
new file mode 100644
index 0000000000..b5f03e0ac0
--- /dev/null
+++ b/test/fixtures/expression_equality/not.a.json
@@ -0,0 +1,10 @@
+[
+ "!",
+ [
+ "boolean",
+ [
+ "get",
+ "x"
+ ]
+ ]
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/not.b.json b/test/fixtures/expression_equality/not.b.json
new file mode 100644
index 0000000000..a4d77adf2e
--- /dev/null
+++ b/test/fixtures/expression_equality/not.b.json
@@ -0,0 +1,10 @@
+[
+ "!",
+ [
+ "boolean",
+ [
+ "get",
+ "x_other"
+ ]
+ ]
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/number.a.json b/test/fixtures/expression_equality/number.a.json
new file mode 100644
index 0000000000..57c3df48e7
--- /dev/null
+++ b/test/fixtures/expression_equality/number.a.json
@@ -0,0 +1,7 @@
+[
+ "number",
+ [
+ "get",
+ "x"
+ ]
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/number.b.json b/test/fixtures/expression_equality/number.b.json
new file mode 100644
index 0000000000..d1843362d3
--- /dev/null
+++ b/test/fixtures/expression_equality/number.b.json
@@ -0,0 +1,7 @@
+[
+ "number",
+ [
+ "get",
+ "x_other"
+ ]
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/object.a.json b/test/fixtures/expression_equality/object.a.json
new file mode 100644
index 0000000000..7551cfdbb2
--- /dev/null
+++ b/test/fixtures/expression_equality/object.a.json
@@ -0,0 +1,7 @@
+[
+ "object",
+ [
+ "get",
+ "x"
+ ]
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/object.b.json b/test/fixtures/expression_equality/object.b.json
new file mode 100644
index 0000000000..8444d40c0e
--- /dev/null
+++ b/test/fixtures/expression_equality/object.b.json
@@ -0,0 +1,7 @@
+[
+ "object",
+ [
+ "get",
+ "x_other"
+ ]
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/plus.a.json b/test/fixtures/expression_equality/plus.a.json
new file mode 100644
index 0000000000..a00c4409fa
--- /dev/null
+++ b/test/fixtures/expression_equality/plus.a.json
@@ -0,0 +1,7 @@
+[
+ "+",
+ 1,
+ 2,
+ 3,
+ 4
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/plus.b.json b/test/fixtures/expression_equality/plus.b.json
new file mode 100644
index 0000000000..87c071123f
--- /dev/null
+++ b/test/fixtures/expression_equality/plus.b.json
@@ -0,0 +1,7 @@
+[
+ "+",
+ 1,
+ 2,
+ 3,
+ 5
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/pow.a.json b/test/fixtures/expression_equality/pow.a.json
new file mode 100644
index 0000000000..c1a1e67f86
--- /dev/null
+++ b/test/fixtures/expression_equality/pow.a.json
@@ -0,0 +1,11 @@
+[
+ "^",
+ 4,
+ [
+ "number",
+ [
+ "get",
+ "x"
+ ]
+ ]
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/pow.b.json b/test/fixtures/expression_equality/pow.b.json
new file mode 100644
index 0000000000..ca5331b92a
--- /dev/null
+++ b/test/fixtures/expression_equality/pow.b.json
@@ -0,0 +1,11 @@
+[
+ "^",
+ 4,
+ [
+ "number",
+ [
+ "get",
+ "x_other"
+ ]
+ ]
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/rgb.a.json b/test/fixtures/expression_equality/rgb.a.json
new file mode 100644
index 0000000000..ce6c5e5dd0
--- /dev/null
+++ b/test/fixtures/expression_equality/rgb.a.json
@@ -0,0 +1,6 @@
+[
+ "rgb",
+ 0,
+ 0,
+ 255
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/rgb.b.json b/test/fixtures/expression_equality/rgb.b.json
new file mode 100644
index 0000000000..577c19748b
--- /dev/null
+++ b/test/fixtures/expression_equality/rgb.b.json
@@ -0,0 +1,6 @@
+[
+ "rgb",
+ 0,
+ 0,
+ 0
+]
diff --git a/test/fixtures/expression_equality/rgba.a.json b/test/fixtures/expression_equality/rgba.a.json
new file mode 100644
index 0000000000..e8ad7326c1
--- /dev/null
+++ b/test/fixtures/expression_equality/rgba.a.json
@@ -0,0 +1,7 @@
+[
+ "rgba",
+ 0,
+ 0,
+ 255,
+ 1
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/rgba.b.json b/test/fixtures/expression_equality/rgba.b.json
new file mode 100644
index 0000000000..81d442eaae
--- /dev/null
+++ b/test/fixtures/expression_equality/rgba.b.json
@@ -0,0 +1,7 @@
+[
+ "rgba",
+ 0,
+ 0,
+ 255,
+ 0.5
+]
diff --git a/test/fixtures/expression_equality/sin.a.json b/test/fixtures/expression_equality/sin.a.json
new file mode 100644
index 0000000000..0f7ae2966f
--- /dev/null
+++ b/test/fixtures/expression_equality/sin.a.json
@@ -0,0 +1,4 @@
+[
+ "sin",
+ 0
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/sin.b.json b/test/fixtures/expression_equality/sin.b.json
new file mode 100644
index 0000000000..90f309b80f
--- /dev/null
+++ b/test/fixtures/expression_equality/sin.b.json
@@ -0,0 +1,4 @@
+[
+ "sin",
+ 1
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/sqrt.a.json b/test/fixtures/expression_equality/sqrt.a.json
new file mode 100644
index 0000000000..56dd85bc1a
--- /dev/null
+++ b/test/fixtures/expression_equality/sqrt.a.json
@@ -0,0 +1,7 @@
+[
+ "sqrt",
+ [
+ "get",
+ "x"
+ ]
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/sqrt.b.json b/test/fixtures/expression_equality/sqrt.b.json
new file mode 100644
index 0000000000..ab05d5084c
--- /dev/null
+++ b/test/fixtures/expression_equality/sqrt.b.json
@@ -0,0 +1,7 @@
+[
+ "sqrt",
+ [
+ "get",
+ "x_other"
+ ]
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/step.a.json b/test/fixtures/expression_equality/step.a.json
new file mode 100644
index 0000000000..4fee85cd03
--- /dev/null
+++ b/test/fixtures/expression_equality/step.a.json
@@ -0,0 +1,18 @@
+[
+ "number",
+ [
+ "step",
+ [
+ "number",
+ [
+ "get",
+ "x"
+ ]
+ ],
+ 11,
+ 0,
+ 111,
+ 1,
+ 1111
+ ]
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/step.b.json b/test/fixtures/expression_equality/step.b.json
new file mode 100644
index 0000000000..0a591a84df
--- /dev/null
+++ b/test/fixtures/expression_equality/step.b.json
@@ -0,0 +1,18 @@
+[
+ "number",
+ [
+ "step",
+ [
+ "number",
+ [
+ "get",
+ "x"
+ ]
+ ],
+ 11,
+ 0,
+ 111,
+ 1,
+ 1112
+ ]
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/string.a.json b/test/fixtures/expression_equality/string.a.json
new file mode 100644
index 0000000000..a79344f338
--- /dev/null
+++ b/test/fixtures/expression_equality/string.a.json
@@ -0,0 +1,7 @@
+[
+ "string",
+ [
+ "get",
+ "x"
+ ]
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/string.b.json b/test/fixtures/expression_equality/string.b.json
new file mode 100644
index 0000000000..6f77f3c3cf
--- /dev/null
+++ b/test/fixtures/expression_equality/string.b.json
@@ -0,0 +1,7 @@
+[
+ "string",
+ [
+ "get",
+ "x_other"
+ ]
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/tan.a.json b/test/fixtures/expression_equality/tan.a.json
new file mode 100644
index 0000000000..c78e47e492
--- /dev/null
+++ b/test/fixtures/expression_equality/tan.a.json
@@ -0,0 +1,4 @@
+[
+ "tan",
+ 0.7853981633974483
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/tan.b.json b/test/fixtures/expression_equality/tan.b.json
new file mode 100644
index 0000000000..c22e64cf8a
--- /dev/null
+++ b/test/fixtures/expression_equality/tan.b.json
@@ -0,0 +1,4 @@
+[
+ "tan",
+ 1.7853981633974483
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/times.a.json b/test/fixtures/expression_equality/times.a.json
new file mode 100644
index 0000000000..ce6d9b46e0
--- /dev/null
+++ b/test/fixtures/expression_equality/times.a.json
@@ -0,0 +1,7 @@
+[
+ "*",
+ 3,
+ 2,
+ 0.5,
+ 2
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/times.b.json b/test/fixtures/expression_equality/times.b.json
new file mode 100644
index 0000000000..147e011172
--- /dev/null
+++ b/test/fixtures/expression_equality/times.b.json
@@ -0,0 +1,7 @@
+[
+ "*",
+ 3,
+ 2,
+ 0.5,
+ 3
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/to-boolean.a.json b/test/fixtures/expression_equality/to-boolean.a.json
new file mode 100644
index 0000000000..ccf48149ec
--- /dev/null
+++ b/test/fixtures/expression_equality/to-boolean.a.json
@@ -0,0 +1,7 @@
+[
+ "to-boolean",
+ [
+ "get",
+ "x"
+ ]
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/to-boolean.b.json b/test/fixtures/expression_equality/to-boolean.b.json
new file mode 100644
index 0000000000..7896261241
--- /dev/null
+++ b/test/fixtures/expression_equality/to-boolean.b.json
@@ -0,0 +1,7 @@
+[
+ "to-boolean",
+ [
+ "get",
+ "x_other"
+ ]
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/to-color.a.json b/test/fixtures/expression_equality/to-color.a.json
new file mode 100644
index 0000000000..de9ab59eec
--- /dev/null
+++ b/test/fixtures/expression_equality/to-color.a.json
@@ -0,0 +1,7 @@
+[
+ "to-color",
+ [
+ "get",
+ "x"
+ ]
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/to-color.b.json b/test/fixtures/expression_equality/to-color.b.json
new file mode 100644
index 0000000000..c0566ef6a7
--- /dev/null
+++ b/test/fixtures/expression_equality/to-color.b.json
@@ -0,0 +1,7 @@
+[
+ "to-color",
+ [
+ "get",
+ "x_other"
+ ]
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/to-number.a.json b/test/fixtures/expression_equality/to-number.a.json
new file mode 100644
index 0000000000..65b2df5014
--- /dev/null
+++ b/test/fixtures/expression_equality/to-number.a.json
@@ -0,0 +1,7 @@
+[
+ "to-number",
+ [
+ "get",
+ "x"
+ ]
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/to-number.b.json b/test/fixtures/expression_equality/to-number.b.json
new file mode 100644
index 0000000000..b38dc5a455
--- /dev/null
+++ b/test/fixtures/expression_equality/to-number.b.json
@@ -0,0 +1,7 @@
+[
+ "to-number",
+ [
+ "get",
+ "x_other"
+ ]
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/to-string.a.json b/test/fixtures/expression_equality/to-string.a.json
new file mode 100644
index 0000000000..66f9a9caa1
--- /dev/null
+++ b/test/fixtures/expression_equality/to-string.a.json
@@ -0,0 +1,7 @@
+[
+ "to-string",
+ [
+ "get",
+ "x"
+ ]
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/to-string.b.json b/test/fixtures/expression_equality/to-string.b.json
new file mode 100644
index 0000000000..977a9d7769
--- /dev/null
+++ b/test/fixtures/expression_equality/to-string.b.json
@@ -0,0 +1,7 @@
+[
+ "to-string",
+ [
+ "get",
+ "x_other"
+ ]
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/typeof.a.json b/test/fixtures/expression_equality/typeof.a.json
new file mode 100644
index 0000000000..7843ff8c7f
--- /dev/null
+++ b/test/fixtures/expression_equality/typeof.a.json
@@ -0,0 +1,7 @@
+[
+ "typeof",
+ [
+ "get",
+ "x"
+ ]
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/typeof.b.json b/test/fixtures/expression_equality/typeof.b.json
new file mode 100644
index 0000000000..412482347a
--- /dev/null
+++ b/test/fixtures/expression_equality/typeof.b.json
@@ -0,0 +1,7 @@
+[
+ "typeof",
+ [
+ "get",
+ "x_other"
+ ]
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/upcase.a.json b/test/fixtures/expression_equality/upcase.a.json
new file mode 100644
index 0000000000..d12ca7b08d
--- /dev/null
+++ b/test/fixtures/expression_equality/upcase.a.json
@@ -0,0 +1,4 @@
+[
+ "upcase",
+ "string"
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/upcase.b.json b/test/fixtures/expression_equality/upcase.b.json
new file mode 100644
index 0000000000..ddeeb0300c
--- /dev/null
+++ b/test/fixtures/expression_equality/upcase.b.json
@@ -0,0 +1,4 @@
+[
+ "upcase",
+ "string_other"
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/zoom.a.json b/test/fixtures/expression_equality/zoom.a.json
new file mode 100644
index 0000000000..fc675721ab
--- /dev/null
+++ b/test/fixtures/expression_equality/zoom.a.json
@@ -0,0 +1,13 @@
+[
+ "interpolate",
+ [
+ "linear"
+ ],
+ [
+ "zoom"
+ ],
+ 0,
+ 0,
+ 30,
+ 30
+] \ No newline at end of file
diff --git a/test/fixtures/expression_equality/zoom.b.json b/test/fixtures/expression_equality/zoom.b.json
new file mode 100644
index 0000000000..6314858a5e
--- /dev/null
+++ b/test/fixtures/expression_equality/zoom.b.json
@@ -0,0 +1,13 @@
+[
+ "interpolate",
+ [
+ "linear"
+ ],
+ [
+ "zoom"
+ ],
+ 0,
+ 0,
+ 30,
+ 31
+] \ No newline at end of file
diff --git a/test/fixtures/local_glyphs/droid/expected.png b/test/fixtures/local_glyphs/droid/expected.png
new file mode 100644
index 0000000000..c0ba43bf11
--- /dev/null
+++ b/test/fixtures/local_glyphs/droid/expected.png
Binary files differ
diff --git a/test/fixtures/local_glyphs/mixed.json b/test/fixtures/local_glyphs/mixed.json
new file mode 100644
index 0000000000..e07d429753
--- /dev/null
+++ b/test/fixtures/local_glyphs/mixed.json
@@ -0,0 +1,196 @@
+{
+ "version": 8,
+ "zoom": 0,
+ "center": [-14.41400, 39.09187],
+ "sources": {
+ "mapbox": {
+ "type": "geojson",
+ "data": {
+ "type": "FeatureCollection",
+ "features": [
+ {
+ "type": "Feature",
+ "properties": {
+ "name": "身什戰 1"
+ },
+ "geometry": {
+ "type": "LineString",
+ "coordinates": [
+ [
+ -14.4195556640625,
+ 39.091699613104595
+ ],
+ [
+ 102.3046875,
+ 39.36827914916014
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "name": "two 身什戰"
+ },
+ "geometry": {
+ "type": "LineString",
+ "coordinates": [
+ [
+ -14.403076171875,
+ 39.10022600175347
+ ],
+ [
+ 103.35937499999999,
+ 65.80277639340238
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "name": "身什戰33"
+ },
+ "geometry": {
+ "type": "LineString",
+ "coordinates": [
+ [
+ -14.414062499999998,
+ 39.091699613104595
+ ],
+ [
+ -14.765625,
+ 82.21421714106776
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "name": "身什戰"
+ },
+ "geometry": {
+ "type": "LineString",
+ "coordinates": [
+ [
+ -14.408569335937498,
+ 39.091699613104595
+ ],
+ [
+ -130.78125,
+ 39.095962936305476
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "name": "身什戰 five"
+ },
+ "geometry": {
+ "type": "LineString",
+ "coordinates": [
+ [
+ -14.414062499999998,
+ 39.095962936305476
+ ],
+ [
+ -16.5234375,
+ -58.81374171570779
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "name": "six 身什戰"
+ },
+ "geometry": {
+ "type": "LineString",
+ "coordinates": [
+ [
+ -14.4195556640625,
+ 39.10022600175347
+ ],
+ [
+ -130.4296875,
+ 64.47279382008166
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "name": "身什戰 seven"
+ },
+ "geometry": {
+ "type": "LineString",
+ "coordinates": [
+ [
+ -14.4195556640625,
+ 39.0831721934762
+ ],
+ [
+ 33.75,
+ 81.87364125482827
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "name": "eight 身什戰"
+ },
+ "geometry": {
+ "type": "LineString",
+ "coordinates": [
+ [
+ -14.447021484374998,
+ 39.104488809440475
+ ],
+ [
+ -66.4453125,
+ 82.26169873683153
+ ]
+ ]
+ }
+ }
+ ]
+ }
+ }
+ },
+ "glyphs": "local://glyphs/{fontstack}/{range}.pbf",
+ "layers": [
+ {
+ "id": "background",
+ "type": "background",
+ "paint": {
+ "background-color": "white"
+ }
+ },
+ {
+ "id": "lines-symbol",
+ "type": "symbol",
+ "source": "mapbox",
+ "layout": {
+ "text-field": "{name}",
+ "symbol-placement": "line",
+ "symbol-spacing": 150,
+ "text-allow-overlap": true,
+ "text-font": [ "NotoCJK" ]
+ }
+ }, {
+ "id": "lines",
+ "type": "line",
+ "source": "mapbox",
+ "paint": {
+ "line-opacity": 0.25
+ }
+ }
+ ]
+}
diff --git a/test/fixtures/local_glyphs/no_local/expected.png b/test/fixtures/local_glyphs/no_local/expected.png
new file mode 100644
index 0000000000..c7b1b828ee
--- /dev/null
+++ b/test/fixtures/local_glyphs/no_local/expected.png
Binary files differ
diff --git a/test/fixtures/local_glyphs/ping_fang/expected.png b/test/fixtures/local_glyphs/ping_fang/expected.png
new file mode 100644
index 0000000000..c0ba43bf11
--- /dev/null
+++ b/test/fixtures/local_glyphs/ping_fang/expected.png
Binary files differ
diff --git a/test/fixtures/map/offline/expected.png b/test/fixtures/map/offline/expected.png
index 973c4102da..0005137ae3 100644
--- a/test/fixtures/map/offline/expected.png
+++ b/test/fixtures/map/offline/expected.png
Binary files differ
diff --git a/test/fixtures/shared_context/expected.png b/test/fixtures/shared_context/expected.png
new file mode 100644
index 0000000000..3b3fd0e315
--- /dev/null
+++ b/test/fixtures/shared_context/expected.png
Binary files differ
diff --git a/test/fixtures/style_parser/expressions.info.json b/test/fixtures/style_parser/expressions.info.json
new file mode 100644
index 0000000000..9e1765ecd0
--- /dev/null
+++ b/test/fixtures/style_parser/expressions.info.json
@@ -0,0 +1,12 @@
+{
+ "default": {
+ "log": [
+ [1, "WARNING", "ParseStyle", "Expected color but found number instead."],
+ [1, "WARNING", "ParseStyle", "[2]: Expected number but found string instead."],
+ [1, "WARNING", "ParseStyle", "\"zoom\" expression may only be used as input to a top-level \"step\" or \"interpolate\" expression."],
+ [1, "WARNING", "ParseStyle", "value must be a string"],
+ [1, "WARNING", "ParseStyle", "property expressions not supported"],
+ [1, "WARNING", "ParseStyle", "Type array<number> is not interpolatable."]
+ ]
+ }
+}
diff --git a/test/fixtures/style_parser/expressions.style.json b/test/fixtures/style_parser/expressions.style.json
new file mode 100644
index 0000000000..b9b4aeac7f
--- /dev/null
+++ b/test/fixtures/style_parser/expressions.style.json
@@ -0,0 +1,74 @@
+{
+ "version": 8,
+ "sources": {
+ "source": {
+ "type": "vector",
+ "url": "mapbox://mapbox.mapbox-streets-v5"
+ }
+ },
+ "layers": [
+ {
+ "id": "valid expression",
+ "type": "fill",
+ "source": "source",
+ "source-layer": "layer",
+ "paint": {
+ "fill-color": ["rgba", 10, ["number", ["get", "x"]], 30, 1]
+ }
+ },
+ {
+ "id": "invalid expression type - color",
+ "type": "fill",
+ "source": "source",
+ "source-layer": "layer",
+ "paint": {
+ "fill-color": ["pi"]
+ }
+ },
+ {
+ "id": "invalid expression - fails type checking",
+ "type": "fill",
+ "source": "source",
+ "source-layer": "layer",
+ "paint": {
+ "fill-color": ["rgba", 1, "should be a number", 0, 1]
+ }
+ },
+ {
+ "id": "invalid expression - nested zoom expression",
+ "type": "fill",
+ "source": "source",
+ "source-layer": "layer",
+ "paint": {
+ "fill-opacity": ["+", 0.5, ["interpolate", ["linear"], ["zoom"], 0, 0, 1, 1]]
+ }
+ },
+ {
+ "id": "invalid expression - not allowed in visibility",
+ "type": "fill",
+ "source": "source",
+ "source-layer": "layer",
+ "layout": {
+ "visibility": ["literal", true]
+ }
+ },
+ {
+ "id": "invalid expression - not a DDS property",
+ "type": "fill-extrusion",
+ "source": "source",
+ "source-layer": "layer",
+ "paint": {
+ "fill-extrusion-opacity": ["get", "opacity"]
+ }
+ },
+ {
+ "id": "invalid expression - line-dasharray must use step interpolation",
+ "type": "line",
+ "source": "source",
+ "source-layer": "layer",
+ "paint": {
+ "line-dasharray": ["interpolate", ["linear"], ["zoom"], 0, ["literal", [1, 2]], 1, ["literal", [3, 4]]]
+ }
+ }
+ ]
+}
diff --git a/test/fixtures/style_parser/font_stacks.json b/test/fixtures/style_parser/font_stacks.json
index 07fe223d46..0ff3e936a8 100644
--- a/test/fixtures/style_parser/font_stacks.json
+++ b/test/fixtures/style_parser/font_stacks.json
@@ -12,6 +12,7 @@
"source": "source",
"source-layer": "source-layer",
"layout": {
+ "text-field": "a",
"text-font": ["a"]
}
}, {
@@ -20,6 +21,7 @@
"source": "source",
"source-layer": "source-layer",
"layout": {
+ "text-field": "a",
"text-font": {
"stops": [[0, ["a", "b"]]]
}
@@ -30,6 +32,7 @@
"source": "source",
"source-layer": "source-layer",
"layout": {
+ "text-field": "a",
"text-font": {
"stops": [[0, ["a", "b"]], [1, ["a", "b", "c"]]]
}
diff --git a/test/fixtures/style_parser/text-font.info.json b/test/fixtures/style_parser/text-font.info.json
new file mode 100644
index 0000000000..0fb9658269
--- /dev/null
+++ b/test/fixtures/style_parser/text-font.info.json
@@ -0,0 +1,14 @@
+{
+ "default": {
+ "log": [
+ [1, "WARNING", "ParseStyle", "Layer 'invalid expression - get' has an invalid value for text-font and will not work offline. Output values must be contained as literals within the expression."],
+ [1, "WARNING", "ParseStyle", "Layer 'invalid expression - case' has an invalid value for text-font and will not work offline. Output values must be contained as literals within the expression."],
+ [1, "WARNING", "ParseStyle", "Layer 'invalid expression - match' has an invalid value for text-font and will not work offline. Output values must be contained as literals within the expression."],
+ [1, "WARNING", "ParseStyle", "Layer 'invalid expression - at' has an invalid value for text-font and will not work offline. Output values must be contained as literals within the expression."],
+ [1, "WARNING", "ParseStyle", "Layer 'invalid expression - coalesce' has an invalid value for text-font and will not work offline. Output values must be contained as literals within the expression."],
+ [1, "WARNING", "ParseStyle", "Layer 'invalid expression - step' has an invalid value for text-font and will not work offline. Output values must be contained as literals within the expression."],
+ [1, "WARNING", "ParseStyle", "Layer 'invalid expression - let/var' has an invalid value for text-font and will not work offline. Output values must be contained as literals within the expression."],
+ [1, "WARNING", "ParseStyle", "Layer 'invalid expression - identity function' has an invalid value for text-font and will not work offline. Output values must be contained as literals within the expression."]
+ ]
+ }
+}
diff --git a/test/fixtures/style_parser/text-font.style.json b/test/fixtures/style_parser/text-font.style.json
new file mode 100644
index 0000000000..215807ca81
--- /dev/null
+++ b/test/fixtures/style_parser/text-font.style.json
@@ -0,0 +1,135 @@
+{
+ "version": 8,
+ "glyphs": "https://example.com/{fontstack}/{range}",
+ "sources": {
+ "vector": {
+ "type": "vector",
+ "url": "mapbox://mapbox.mapbox-streets-v5"
+ }
+ },
+ "layers": [
+ {
+ "id": "minimum",
+ "type": "symbol",
+ "source": "vector",
+ "source-layer": "layer",
+ "layout": {
+ "text-font": ["Helvetica"],
+ "text-field": "{foo}"
+ }
+ },
+ {
+ "id": "valid expression - case",
+ "type": "symbol",
+ "source": "vector",
+ "source-layer": "layer",
+ "layout": {
+ "text-font": ["case", ["==", "a", ["string", ["get", "text-font"]]], ["literal", ["Arial"]], ["literal", ["Helvetica"]]],
+ "text-field": "{foo}"
+ }
+ },
+ {
+ "id": "valid expression - match",
+ "type": "symbol",
+ "source": "vector",
+ "source-layer": "layer",
+ "layout": {
+ "text-font": ["match", ["get", "text-font"], "a", ["literal", ["Arial"]], ["literal", ["Helvetica"]]],
+ "text-field": "{foo}"
+ }
+ },
+ {
+ "id": "valid expression - step",
+ "type": "symbol",
+ "source": "vector",
+ "source-layer": "layer",
+ "layout": {
+ "text-font": ["array", "string", ["step", ["get", "text-font"], ["literal", ["Arial"]], 0, ["literal", ["Helvetica"]]]],
+ "text-field": "{foo}"
+ }
+ },
+ {
+ "id": "invalid expression - get",
+ "type": "symbol",
+ "source": "vector",
+ "source-layer": "layer",
+ "layout": {
+ "text-font": ["array", "string", ["get", "text-font"]],
+ "text-field": "{foo}"
+ }
+ },
+ {
+ "id": "invalid expression - case",
+ "type": "symbol",
+ "source": "vector",
+ "source-layer": "layer",
+ "layout": {
+ "text-font": ["case", ["==", "a", ["string", ["get", "text-font"]]], ["array", "string", ["get", "text-font-a"]], ["literal", ["Helvetica"]]],
+ "text-field": "{foo}"
+ }
+ },
+ {
+ "id": "invalid expression - match",
+ "type": "symbol",
+ "source": "vector",
+ "source-layer": "layer",
+ "layout": {
+ "text-font": ["match", ["get", "text-font"], "a", ["array", "string", ["get", "text-font-a"]], ["literal", ["Helvetica"]]],
+ "text-field": "{foo}"
+ }
+ },
+ {
+ "id": "invalid expression - at",
+ "type": "symbol",
+ "source": "vector",
+ "source-layer": "layer",
+ "layout": {
+ "text-font": ["array", "string", ["at", 0, ["array", ["get", "text-font"]]]],
+ "text-field": "{foo}"
+ }
+ },
+ {
+ "id": "invalid expression - coalesce",
+ "type": "symbol",
+ "source": "vector",
+ "source-layer": "layer",
+ "layout": {
+ "text-font": ["array", "string", ["coalesce", ["get", "text-font"]]],
+ "text-field": "{foo}"
+ }
+ },
+ {
+ "id": "invalid expression - step",
+ "type": "symbol",
+ "source": "vector",
+ "source-layer": "layer",
+ "layout": {
+ "text-font": ["step", ["zoom"], ["array", "string", ["get", "text-font-0"]], 0, ["array", "string", ["get", "text-font-1"]]],
+ "text-field": "{foo}"
+ }
+ },
+ {
+ "id": "invalid expression - let/var",
+ "type": "symbol",
+ "source": "vector",
+ "source-layer": "layer",
+ "layout": {
+ "text-font": ["let", "p", ["array", "string", ["get", "text-font"]], ["var", "p"]],
+ "text-field": "{foo}"
+ }
+ },
+ {
+ "id": "invalid expression - identity function",
+ "type": "symbol",
+ "source": "vector",
+ "source-layer": "layer",
+ "layout": {
+ "text-font": {
+ "type": "identity",
+ "property": "text-font"
+ },
+ "text-field": "{foo}"
+ }
+ }
+ ]
+}
diff --git a/test/geometry/dem_data.test.cpp b/test/geometry/dem_data.test.cpp
new file mode 100644
index 0000000000..3848f028f1
--- /dev/null
+++ b/test/geometry/dem_data.test.cpp
@@ -0,0 +1,149 @@
+#include <mbgl/test/util.hpp>
+
+#include <mbgl/util/image.hpp>
+#include <mbgl/util/tileset.hpp>
+#include <mbgl/geometry/dem_data.hpp>
+
+using namespace mbgl;
+
+auto fakeImage = [](Size s) {
+ PremultipliedImage img = PremultipliedImage(s);
+
+ for (size_t i = 0; i < img.bytes(); i ++) {
+ img.data[i] = (i+1) % 4 == 0 ? 1 : std::rand() % 255;
+ }
+ return img;
+};
+
+TEST(DEMData, ConstructorMapbox) {
+ PremultipliedImage image = fakeImage({16, 16});
+ DEMData demdata(image, Tileset::DEMEncoding::Mapbox);
+
+ EXPECT_EQ(demdata.dim, 16);
+ EXPECT_EQ(demdata.border, 8);
+ EXPECT_EQ(demdata.stride, 32);
+ EXPECT_EQ(demdata.getImage()->bytes(), size_t(32*32*4));
+};
+
+TEST(DEMData, ConstructorTerrarium) {
+ PremultipliedImage image = fakeImage({16, 16});
+ DEMData demdata(image, Tileset::DEMEncoding::Terrarium);
+
+ EXPECT_EQ(demdata.dim, 16);
+ EXPECT_EQ(demdata.border, 8);
+ EXPECT_EQ(demdata.stride, 32);
+ EXPECT_EQ(demdata.getImage()->bytes(), size_t(32*32*4));
+};
+
+TEST(DEMData, RoundTrip) {
+ PremultipliedImage image = fakeImage({16, 16});
+ DEMData demdata(image, Tileset::DEMEncoding::Mapbox);
+
+ demdata.set(4, 6, 255);
+ EXPECT_EQ(demdata.get(4, 6), 255);
+}
+
+TEST(DEMData, InitialBackfill) {
+
+ PremultipliedImage image1 = fakeImage({4, 4});
+ DEMData dem1(image1, Tileset::DEMEncoding::Mapbox);
+
+ bool nonempty = true;
+ // checking that a 1 px border around the fake image has been populated
+ // with a non-empty pixel value
+ for (int x = -1; x < 5; x++){
+ for (int y = -1; y < 5; y ++) {
+ if (dem1.get(x, y) == -65536) {
+ nonempty = false;
+ break;
+ }
+ }
+ }
+ EXPECT_TRUE(nonempty);
+
+ bool verticalBorderMatch = true;
+ int vertx[] = {-1, 4};
+ for (int x : vertx) {
+ for (int y = 0; y < 4; y++) {
+ if (dem1.get(x, y) != dem1.get(x < 0 ? x + 1 : x - 1, y)) {
+ verticalBorderMatch = false;
+ break;
+ }
+ }
+ }
+ // vertical border of DEM data is initially equal to next column of data
+ EXPECT_TRUE(verticalBorderMatch);
+
+ // horizontal borders empty
+ bool horizontalBorderMatch = true;
+ int horiz[] = {-1, 4};
+ for (int y : horiz) {
+ for (int x = 0; x < 4; x++) {
+ if (dem1.get(x, y) != dem1.get(x, y < 0 ? y + 1 : y - 1)) {
+ horizontalBorderMatch = false;
+ break;
+ }
+ }
+ }
+ //horizontal border of DEM data is initially equal to next row of data
+
+ EXPECT_TRUE(horizontalBorderMatch);
+ // -1, 1 corner initially equal to closest corner data
+ EXPECT_TRUE(dem1.get(-1, 4) == dem1.get(0, 3));
+ // 1, 1 corner initially equal to closest corner data
+ EXPECT_TRUE(dem1.get(4, 4) == dem1.get(3, 3));
+ // -1, -1 corner initially equal to closest corner data
+ EXPECT_TRUE(dem1.get(-1, -1) == dem1.get(0, 0));
+ // -1, 1 corner initially equal to closest corner data
+ EXPECT_TRUE(dem1.get(4, -1) == dem1.get(3, 0));
+};
+
+TEST(DEMData, BackfillNeighbor) {
+ PremultipliedImage image1 = fakeImage({4, 4});
+ DEMData dem0(image1, Tileset::DEMEncoding::Mapbox);
+
+ PremultipliedImage image2 = fakeImage({4, 4});
+ DEMData dem1(image2, Tileset::DEMEncoding::Mapbox);
+
+ dem0.backfillBorder(dem1, -1, 0);
+ for (int y = 0; y < 4; y++) {
+ // dx = -1, dy = 0, so the left edge of dem1 should equal the right edge of dem0
+ // backfills Left neighbor
+ EXPECT_TRUE(dem0.get(-1, y) == dem1.get(3, y));
+
+ }
+
+ dem0.backfillBorder(dem1, 0, -1);
+ // backfills TopCenter neighbor
+ for (int x = 0; x < 4; x++) {
+ EXPECT_TRUE(dem0.get(x, -1) == dem1.get(x, 3));
+ }
+
+ dem0.backfillBorder(dem1, 1, 0);
+ // backfills Right neighbor
+ for (int y = 0; y < 4; y++) {
+ EXPECT_TRUE(dem0.get(4, y) == dem1.get(0, y));
+ }
+
+ dem0.backfillBorder(dem1, 0, 1);
+ // backfills BottomCenter neighbor
+ for (int x = 0; x < 4; x++) {
+ EXPECT_TRUE(dem0.get(x, 4) == dem1.get(x, 0));
+ }
+
+ dem0.backfillBorder(dem1, -1, 1);
+ // backfulls TopRight neighbor
+ EXPECT_TRUE(dem0.get(-1, 4) == dem1.get(3, 0));
+
+ dem0.backfillBorder(dem1, 1, 1);
+ // backfulls BottomRight neighbor
+ EXPECT_TRUE(dem0.get(4, 4) == dem1.get(0, 0));
+
+ dem0.backfillBorder(dem1, -1, -1);
+ // backfulls TopLeft neighbor
+ EXPECT_TRUE(dem0.get(-1, -1) == dem1.get(3, 3));
+
+ dem0.backfillBorder(dem1, 1, -1);
+ // backfulls BottomLeft neighbor
+ EXPECT_TRUE(dem0.get(4, -1) == dem1.get(0, 3));
+};
diff --git a/test/gl/bucket.test.cpp b/test/gl/bucket.test.cpp
index 5f9626bc99..8393fd130d 100644
--- a/test/gl/bucket.test.cpp
+++ b/test/gl/bucket.test.cpp
@@ -1,6 +1,7 @@
#include <mbgl/test/util.hpp>
#include <mbgl/test/stub_geometry_tile_feature.hpp>
+#include <mbgl/renderer/backend_scope.hpp>
#include <mbgl/renderer/buckets/circle_bucket.hpp>
#include <mbgl/renderer/buckets/fill_bucket.hpp>
#include <mbgl/renderer/buckets/line_bucket.hpp>
@@ -9,6 +10,7 @@
#include <mbgl/renderer/bucket_parameters.hpp>
#include <mbgl/style/layers/symbol_layer_properties.hpp>
#include <mbgl/gl/context.hpp>
+#include <mbgl/gl/headless_backend.hpp>
#include <mbgl/map/mode.hpp>
@@ -41,8 +43,11 @@ PropertyMap properties;
} // namespace
TEST(Buckets, CircleBucket) {
+ HeadlessBackend backend({ 512, 256 });
+ BackendScope scope { backend };
+
gl::Context context;
- CircleBucket bucket { { {0, 0, 0}, MapMode::Still, 1.0 }, {} };
+ CircleBucket bucket { { {0, 0, 0}, MapMode::Static, 1.0 }, {} };
ASSERT_FALSE(bucket.hasData());
ASSERT_FALSE(bucket.needsUpload());
@@ -57,8 +62,11 @@ TEST(Buckets, CircleBucket) {
}
TEST(Buckets, FillBucket) {
+ HeadlessBackend backend({ 512, 256 });
+ BackendScope scope { backend };
+
gl::Context context;
- FillBucket bucket { { {0, 0, 0}, MapMode::Still, 1.0 }, {} };
+ FillBucket bucket { { {0, 0, 0}, MapMode::Static, 1.0 }, {} };
ASSERT_FALSE(bucket.hasData());
ASSERT_FALSE(bucket.needsUpload());
@@ -72,8 +80,11 @@ TEST(Buckets, FillBucket) {
}
TEST(Buckets, LineBucket) {
+ HeadlessBackend backend({ 512, 256 });
+ BackendScope scope { backend };
+
gl::Context context;
- LineBucket bucket { { {0, 0, 0}, MapMode::Still, 1.0 }, {}, {} };
+ LineBucket bucket { { {0, 0, 0}, MapMode::Static, 1.0 }, {}, {} };
ASSERT_FALSE(bucket.hasData());
ASSERT_FALSE(bucket.needsUpload());
@@ -92,12 +103,17 @@ TEST(Buckets, LineBucket) {
}
TEST(Buckets, SymbolBucket) {
+ HeadlessBackend backend({ 512, 256 });
+ BackendScope scope { backend };
+
style::SymbolLayoutProperties::PossiblyEvaluated layout;
bool sdfIcons = false;
bool iconsNeedLinear = false;
+ bool sortFeaturesByY = false;
+ std::vector<SymbolInstance> symbolInstances;
gl::Context context;
- SymbolBucket bucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear };
+ SymbolBucket bucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(symbolInstances) };
ASSERT_FALSE(bucket.hasIconData());
ASSERT_FALSE(bucket.hasTextData());
ASSERT_FALSE(bucket.hasCollisionBoxData());
@@ -120,6 +136,9 @@ TEST(Buckets, SymbolBucket) {
}
TEST(Buckets, RasterBucket) {
+ HeadlessBackend backend({ 512, 256 });
+ BackendScope scope { backend };
+
gl::Context context;
PremultipliedImage rgba({ 1, 1 });
diff --git a/test/gl/context.test.cpp b/test/gl/context.test.cpp
new file mode 100644
index 0000000000..179ce5de53
--- /dev/null
+++ b/test/gl/context.test.cpp
@@ -0,0 +1,114 @@
+#include <mbgl/test/util.hpp>
+
+#include <mbgl/gl/gl.hpp>
+#include <mbgl/gl/context.hpp>
+#include <mbgl/map/map.hpp>
+#include <mbgl/util/default_thread_pool.hpp>
+#include <mbgl/storage/default_file_source.hpp>
+#include <mbgl/gl/headless_frontend.hpp>
+#include <mbgl/style/style.hpp>
+#include <mbgl/style/layers/custom_layer.hpp>
+#include <mbgl/style/layers/fill_layer.hpp>
+#include <mbgl/style/layers/background_layer.hpp>
+#include <mbgl/util/io.hpp>
+#include <mbgl/util/mat4.hpp>
+#include <mbgl/util/run_loop.hpp>
+
+using namespace mbgl;
+using namespace mbgl::style;
+
+static const GLchar* vertexShaderSource = R"MBGL_SHADER(
+#ifdef GL_ES
+precision mediump float;
+#endif
+attribute vec2 a_pos;
+void main() {
+ gl_Position = vec4(a_pos, 0, 1);
+}
+)MBGL_SHADER";
+
+static const GLchar* fragmentShaderSource = R"MBGL_SHADER(
+#ifdef GL_ES
+precision mediump float;
+#endif
+void main() {
+ gl_FragColor = vec4(0, 1, 0, 1);
+}
+)MBGL_SHADER";
+
+struct Shader {
+ Shader(const GLchar* vertex, const GLchar* fragment) {
+ program = MBGL_CHECK_ERROR(glCreateProgram());
+ vertexShader = MBGL_CHECK_ERROR(glCreateShader(GL_VERTEX_SHADER));
+ fragmentShader = MBGL_CHECK_ERROR(glCreateShader(GL_FRAGMENT_SHADER));
+ MBGL_CHECK_ERROR(glShaderSource(vertexShader, 1, &vertex, nullptr));
+ MBGL_CHECK_ERROR(glCompileShader(vertexShader));
+ MBGL_CHECK_ERROR(glAttachShader(program, vertexShader));
+ MBGL_CHECK_ERROR(glShaderSource(fragmentShader, 1, &fragment, nullptr));
+ MBGL_CHECK_ERROR(glCompileShader(fragmentShader));
+ MBGL_CHECK_ERROR(glAttachShader(program, fragmentShader));
+ MBGL_CHECK_ERROR(glLinkProgram(program));
+ a_pos = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_pos"));
+ }
+
+ ~Shader() {
+ MBGL_CHECK_ERROR(glDetachShader(program, vertexShader));
+ MBGL_CHECK_ERROR(glDetachShader(program, fragmentShader));
+ MBGL_CHECK_ERROR(glDeleteShader(vertexShader));
+ MBGL_CHECK_ERROR(glDeleteShader(fragmentShader));
+ MBGL_CHECK_ERROR(glDeleteProgram(program));
+ }
+
+ GLuint program = 0;
+ GLuint vertexShader = 0;
+ GLuint fragmentShader = 0;
+ GLuint a_pos = 0;
+};
+
+struct Buffer {
+ Buffer(std::vector<GLfloat> data) {
+ MBGL_CHECK_ERROR(glGenBuffers(1, &buffer));
+ MBGL_CHECK_ERROR(glBindBuffer(GL_ARRAY_BUFFER, buffer));
+ MBGL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, data.size() * sizeof(GLfloat), data.data(),
+ GL_STATIC_DRAW));
+ }
+
+ ~Buffer() {
+ MBGL_CHECK_ERROR(glDeleteBuffers(1, &buffer));
+ }
+
+ GLuint buffer = 0;
+};
+
+TEST(GLContextMode, Shared) {
+ util::RunLoop loop;
+
+ DefaultFileSource fileSource(":memory:", "test/fixtures/api/assets");
+ ThreadPool threadPool(4);
+ float pixelRatio { 1 };
+
+ HeadlessFrontend frontend { pixelRatio, fileSource, threadPool, {}, GLContextMode::Shared };
+
+ Map map(frontend, MapObserver::nullObserver(), frontend.getSize(), pixelRatio, fileSource, threadPool, MapMode::Static);
+ map.getStyle().loadJSON(util::read_file("test/fixtures/api/water.json"));
+ map.setLatLngZoom({ 37.8, -122.5 }, 10);
+
+ // Set transparent background layer.
+ map.getStyle().getLayer("background")->as<BackgroundLayer>()->setBackgroundColor( { { 1.0f, 0.0f, 0.0f, 0.5f } } );
+
+ {
+ // Custom rendering outside of GL Native render loop.
+ BackendScope scope { *frontend.getBackend() };
+ frontend.getBackend()->bind();
+
+ Shader paintShader(vertexShaderSource, fragmentShaderSource);
+ Buffer triangleBuffer({ 0, 0.5, 0.5, -0.5, -0.5, -0.5 });
+ MBGL_CHECK_ERROR(glUseProgram(paintShader.program));
+ MBGL_CHECK_ERROR(glBindBuffer(GL_ARRAY_BUFFER, triangleBuffer.buffer));
+ MBGL_CHECK_ERROR(glEnableVertexAttribArray(paintShader.a_pos));
+ MBGL_CHECK_ERROR(glVertexAttribPointer(paintShader.a_pos, 2, GL_FLOAT, GL_FALSE, 0, nullptr));
+ MBGL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, 3));
+ }
+
+ test::checkImage("test/fixtures/shared_context", frontend.render(map), 0.5, 0.1);
+}
diff --git a/test/map/map.test.cpp b/test/map/map.test.cpp
index 9358175297..9b34ea89b0 100644
--- a/test/map/map.test.cpp
+++ b/test/map/map.test.cpp
@@ -72,14 +72,14 @@ public:
HeadlessFrontend frontend;
Map map;
- MapTest(float pixelRatio = 1, MapMode mode = MapMode::Still)
+ MapTest(float pixelRatio = 1, MapMode mode = MapMode::Static)
: frontend(pixelRatio, fileSource, threadPool)
, map(frontend, observer, frontend.getSize(), pixelRatio, fileSource, threadPool, mode) {
}
template <typename T = FileSource>
MapTest(const std::string& cachePath, const std::string& assetRoot,
- float pixelRatio = 1, MapMode mode = MapMode::Still,
+ float pixelRatio = 1, MapMode mode = MapMode::Static,
typename std::enable_if<std::is_same<T, DefaultFileSource>::value>::type* = 0)
: fileSource { cachePath, assetRoot }
, frontend(pixelRatio, fileSource, threadPool)
diff --git a/test/map/prefetch.test.cpp b/test/map/prefetch.test.cpp
index 5c9a22d1bf..4c82b2c965 100644
--- a/test/map/prefetch.test.cpp
+++ b/test/map/prefetch.test.cpp
@@ -23,7 +23,7 @@ TEST(Map, PrefetchTiles) {
ThreadPool threadPool(4);
StubFileSource fileSource;
HeadlessFrontend frontend { { 512, 512 }, 1, fileSource, threadPool };
- Map map(frontend, MapObserver::nullObserver(), frontend.getSize(), 1, fileSource, threadPool, MapMode::Still);
+ Map map(frontend, MapObserver::nullObserver(), frontend.getSize(), 1, fileSource, threadPool, MapMode::Static);
std::vector<int> tiles;
diff --git a/test/renderer/backend_scope.test.cpp b/test/renderer/backend_scope.test.cpp
index 66cf88a9c6..05b82695b2 100644
--- a/test/renderer/backend_scope.test.cpp
+++ b/test/renderer/backend_scope.test.cpp
@@ -28,18 +28,13 @@ public:
if (updateAssumedStateFunction) updateAssumedStateFunction();
}
- gl::ProcAddress initializeExtension(const char* ext) override {
- if (initializeExtensionFunction) {
- return initializeExtensionFunction(ext);
- } else {
- return {};
- }
+ gl::ProcAddress getExtensionFunctionPointer(const char*) override {
+ return {};
}
std::function<void ()> activateFunction;
std::function<void ()> deactivateFunction;
std::function<void ()> updateAssumedStateFunction;
- std::function<gl::ProcAddress (const char*)> initializeExtensionFunction;
};
// A scope should activate on construction
diff --git a/test/src/app-info.plist b/test/src/app-info.plist
index 107e4058f9..6ee059b3b4 100644
--- a/test/src/app-info.plist
+++ b/test/src/app-info.plist
@@ -25,7 +25,7 @@
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSHumanReadableCopyright</key>
- <string>© 2014–2017 Mapbox</string>
+ <string>© 2014–2018 Mapbox</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>The map will ALWAYS display the user's location.</string>
<key>NSLocationWhenInUseUsageDescription</key>
diff --git a/test/src/mbgl/test/conversion_stubs.hpp b/test/src/mbgl/test/conversion_stubs.hpp
deleted file mode 100644
index 30395ddb97..0000000000
--- a/test/src/mbgl/test/conversion_stubs.hpp
+++ /dev/null
@@ -1,124 +0,0 @@
-#pragma once
-
-#include <mbgl/style/conversion.hpp>
-#include <mbgl/util/feature.hpp>
-#include <mbgl/util/optional.hpp>
-#include <mbgl/util/variant.hpp>
-
-#include <string>
-#include <unordered_map>
-
-namespace mbgl {
-namespace style {
-namespace conversion {
-
-class Value;
-using ValueMap = std::unordered_map<std::string, Value>;
-using ValueVector = std::vector<Value>;
-class Value : public mbgl::variant<std::string,
- float,
- double,
- bool,
- mapbox::util::recursive_wrapper<ValueMap>,
- mapbox::util::recursive_wrapper<ValueVector>> {
- using variant::variant;
-};
-
-inline bool isUndefined(const Value&) {
- // Variant is always intialized
- return false;
-}
-
-inline bool isArray(const Value& value) {
- return value.is<mapbox::util::recursive_wrapper<ValueVector>>();
-}
-
-inline std::size_t arrayLength(const Value& value) {
- return value.get<mapbox::util::recursive_wrapper<ValueVector>>().get().size();
-}
-
-inline Value arrayMember(const Value& value, std::size_t i) {
- return value.get<mapbox::util::recursive_wrapper<ValueVector>>().get()[i];
-}
-
-inline bool isObject(const Value& value) {
- return value.is<mapbox::util::recursive_wrapper<ValueMap>>();
-}
-
-inline optional<Value> objectMember(const Value& value, const char* key) {
- auto map = value.get<ValueMap>();
- auto iter = map.find(key);
-
- if (iter != map.end()) {
- return iter->second;
- } else {
- return {};
- }
-}
-
-using EachMemberFn = std::function<optional<Error>(const std::string&, const Value&)>;
-
-optional<Error> eachMember(const Value& value, EachMemberFn&& fn) {
- auto map = value.get<ValueMap>();
- auto iter = map.begin();
-
- while (iter != map.end()) {
- optional<Error> result = fn(iter->first, iter->second);
- if (result) {
- return result;
- }
-
- ++iter;
- }
-
- return {};
-}
-
-inline optional<bool> toBool(const Value& value) {
- if (value.is<bool>()) {
- return value.get<bool>();
- } else {
- return {};
- }
-}
-
-inline optional<float> toNumber(const Value& value) {
- if (value.is<float>()) {
- return value.get<float>();
- } else {
- return {};
- }
- return {};
-}
-
-
-inline optional<double> toDouble(const Value& value) {
- if (value.is<double>()) {
- return value.get<double>();
- }
- return {};
-}
-
-inline optional<std::string> toString(const Value& value) {
- if (value.is<std::string>()) {
- return value.get<std::string>();
- } else {
- return {};
- }
-}
-
-inline optional<mbgl::Value> toValue(const Value& value) {
- if (value.is<bool>()) {
- return { value.get<bool>() };
- } else if (value.is<std::string>()) {
- return { value.get<std::string>() };
- } else if (value.is<float>()) {
- return { double(value.get<float>()) };
- } else {
- return {};
- }
-}
-
-} // namespace conversion
-} // namespace style
-} // namespace mbgl
diff --git a/test/src/mbgl/test/stub_file_source.cpp b/test/src/mbgl/test/stub_file_source.cpp
index 7891d5d907..0bbff84ff3 100644
--- a/test/src/mbgl/test/stub_file_source.cpp
+++ b/test/src/mbgl/test/stub_file_source.cpp
@@ -17,7 +17,12 @@ public:
StubFileSource& fileSource;
};
-StubFileSource::StubFileSource() {
+StubFileSource::StubFileSource(ResponseType type_)
+ : type(type_) {
+ if (type == ResponseType::Synchronous) {
+ return;
+ }
+
timer.start(1ms, 1ms, [this] {
// Explicit copy to avoid iterator invalidation if ~StubFileRequest gets called within the loop.
auto pending_ = pending;
@@ -46,7 +51,14 @@ StubFileSource::~StubFileSource() = default;
std::unique_ptr<AsyncRequest> StubFileSource::request(const Resource& resource, Callback callback) {
auto req = std::make_unique<StubFileRequest>(*this);
- pending.emplace(req.get(), std::make_tuple(resource, response, callback));
+ if (type == ResponseType::Synchronous) {
+ optional<Response> res = response(resource);
+ if (res) {
+ callback(*res);
+ }
+ } else {
+ pending.emplace(req.get(), std::make_tuple(resource, response, callback));
+ }
return std::move(req);
}
diff --git a/test/src/mbgl/test/stub_file_source.hpp b/test/src/mbgl/test/stub_file_source.hpp
index 85118e1a77..6cee8377c6 100644
--- a/test/src/mbgl/test/stub_file_source.hpp
+++ b/test/src/mbgl/test/stub_file_source.hpp
@@ -9,7 +9,12 @@ namespace mbgl {
class StubFileSource : public FileSource {
public:
- StubFileSource();
+ enum class ResponseType {
+ Asynchronous = 0,
+ Synchronous
+ };
+
+ StubFileSource(ResponseType = ResponseType::Asynchronous);
~StubFileSource() override;
std::unique_ptr<AsyncRequest> request(const Resource&, Callback) override;
@@ -36,6 +41,7 @@ private:
optional<Response> defaultResponse(const Resource&);
std::unordered_map<AsyncRequest*, std::tuple<Resource, ResponseFunction, Callback>> pending;
+ ResponseType type;
util::Timer timer;
};
diff --git a/test/storage/asset_file_source.test.cpp b/test/storage/asset_file_source.test.cpp
index a39d2963d2..978a41a306 100644
--- a/test/storage/asset_file_source.test.cpp
+++ b/test/storage/asset_file_source.test.cpp
@@ -69,6 +69,15 @@ TEST(AssetFileSource, Load) {
loop.run();
}
+TEST(AssetFileSource, AcceptsURL) {
+ EXPECT_TRUE(AssetFileSource::acceptsURL("asset://empty"));
+ EXPECT_TRUE(AssetFileSource::acceptsURL("asset:///test"));
+ EXPECT_FALSE(AssetFileSource::acceptsURL("assds://foo"));
+ EXPECT_FALSE(AssetFileSource::acceptsURL("asset:"));
+ EXPECT_FALSE(AssetFileSource::acceptsURL("style.json"));
+ EXPECT_FALSE(AssetFileSource::acceptsURL(""));
+}
+
TEST(AssetFileSource, EmptyFile) {
util::RunLoop loop;
@@ -118,6 +127,23 @@ TEST(AssetFileSource, NonExistentFile) {
loop.run();
}
+TEST(AssetFileSource, InvalidURL) {
+ util::RunLoop loop;
+
+ AssetFileSource fs("test/fixtures/storage/assets");
+
+ std::unique_ptr<AsyncRequest> req = fs.request({ Resource::Unknown, "test://wrong-scheme" }, [&](Response res) {
+ req.reset();
+ ASSERT_NE(nullptr, res.error);
+ EXPECT_EQ(Response::Error::Reason::Other, res.error->reason);
+ EXPECT_EQ("Invalid asset URL", res.error->message);
+ ASSERT_FALSE(res.data.get());
+ loop.stop();
+ });
+
+ loop.run();
+}
+
TEST(AssetFileSource, ReadDirectory) {
util::RunLoop loop;
diff --git a/test/storage/local_file_source.test.cpp b/test/storage/local_file_source.test.cpp
index 4d509e6c7d..e1756f8e7d 100644
--- a/test/storage/local_file_source.test.cpp
+++ b/test/storage/local_file_source.test.cpp
@@ -20,6 +20,15 @@ std::string toAbsoluteURL(const std::string& fileName) {
using namespace mbgl;
+TEST(LocalFileSource, AcceptsURL) {
+ EXPECT_TRUE(LocalFileSource::acceptsURL("file://empty"));
+ EXPECT_TRUE(LocalFileSource::acceptsURL("file:///test"));
+ EXPECT_FALSE(LocalFileSource::acceptsURL("flie://foo"));
+ EXPECT_FALSE(LocalFileSource::acceptsURL("file:"));
+ EXPECT_FALSE(LocalFileSource::acceptsURL("style.json"));
+ EXPECT_FALSE(LocalFileSource::acceptsURL(""));
+}
+
TEST(LocalFileSource, EmptyFile) {
util::RunLoop loop;
@@ -69,6 +78,23 @@ TEST(LocalFileSource, NonExistentFile) {
loop.run();
}
+TEST(LocalFileSource, InvalidURL) {
+ util::RunLoop loop;
+
+ LocalFileSource fs;
+
+ std::unique_ptr<AsyncRequest> req = fs.request({ Resource::Unknown, "test://wrong-scheme" }, [&](Response res) {
+ req.reset();
+ ASSERT_NE(nullptr, res.error);
+ EXPECT_EQ(Response::Error::Reason::Other, res.error->reason);
+ EXPECT_EQ("Invalid file URL", res.error->message);
+ ASSERT_FALSE(res.data.get());
+ loop.stop();
+ });
+
+ loop.run();
+}
+
TEST(LocalFileSource, ReadDirectory) {
util::RunLoop loop;
diff --git a/test/storage/offline.test.cpp b/test/storage/offline.test.cpp
index e7ebe5199f..59aebebaba 100644
--- a/test/storage/offline.test.cpp
+++ b/test/storage/offline.test.cpp
@@ -4,6 +4,7 @@
#include <gtest/gtest.h>
using namespace mbgl;
+using SourceType = mbgl::style::SourceType;
static const LatLngBounds sanFrancisco =
LatLngBounds::hull({ 37.6609, -122.5744 }, { 37.8271, -122.3204 });
diff --git a/test/storage/offline_database.test.cpp b/test/storage/offline_database.test.cpp
index d99c1f946f..94daf59c02 100644
--- a/test/storage/offline_database.test.cpp
+++ b/test/storage/offline_database.test.cpp
@@ -9,7 +9,6 @@
#include <gtest/gtest.h>
#include <sqlite3.hpp>
-#include <sqlite3.h>
#include <thread>
#include <random>
@@ -39,45 +38,6 @@ void writeFile(const char* name, const std::string& data) {
mbgl::util::write_file(name, data);
}
-class FileLock {
-public:
- FileLock(const std::string& path) {
- const int err = sqlite3_open_v2(path.c_str(), &db, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, nullptr);
- if (err != SQLITE_OK) {
- throw std::runtime_error("Could not open db");
- }
- lock();
- }
-
- void lock() {
- assert(!locked);
- const int err = sqlite3_exec(db, "begin exclusive transaction", nullptr, nullptr, nullptr);
- if (err != SQLITE_OK) {
- throw std::runtime_error("Could not lock db");
- }
- locked = true;
- }
-
- void unlock() {
- assert(locked);
- const int err = sqlite3_exec(db, "commit", nullptr, nullptr, nullptr);
- if (err != SQLITE_OK) {
- throw std::runtime_error("Could not unlock db");
- }
- locked = false;
- }
-
- ~FileLock() {
- if (locked) {
- unlock();
- }
- }
-
-private:
- sqlite3* db = nullptr;
- bool locked = false;
-};
-
} // namespace
TEST(OfflineDatabase, TEST_REQUIRES_WRITE(Create)) {
@@ -102,10 +62,8 @@ TEST(OfflineDatabase, TEST_REQUIRES_WRITE(SchemaVersion)) {
std::string path("test/fixtures/offline_database/offline.db");
{
- sqlite3* db = nullptr;
- sqlite3_open_v2(path.c_str(), &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr);
- sqlite3_exec(db, "PRAGMA user_version = 1", nullptr, nullptr, nullptr);
- sqlite3_close_v2(db);
+ mapbox::sqlite::Database db(path, mapbox::sqlite::Create | mapbox::sqlite::ReadWrite);
+ db.exec("PRAGMA user_version = 1");
}
Log::setObserver(std::make_unique<FixtureLogObserver>());
diff --git a/test/style/conversion/function.test.cpp b/test/style/conversion/function.test.cpp
index 1eff94d939..a48be2c075 100644
--- a/test/style/conversion/function.test.cpp
+++ b/test/style/conversion/function.test.cpp
@@ -1,9 +1,9 @@
#include <mbgl/test/util.hpp>
-#include <mbgl/style/conversion.hpp>
-#include <mbgl/style/rapidjson_conversion.hpp>
+#include <mbgl/style/conversion/json.hpp>
#include <mbgl/style/conversion/constant.hpp>
#include <mbgl/style/conversion/function.hpp>
+#include <mbgl/style/conversion/data_driven_property_value.hpp>
#include <mbgl/util/rapidjson.hpp>
using namespace mbgl;
@@ -13,10 +13,8 @@ using namespace mbgl::style::conversion;
TEST(StyleConversion, Function) {
Error error;
- auto parseFunction = [&](const std::string& src) {
- JSDocument doc;
- doc.Parse<0>(src);
- return convert<CameraFunction<float>, JSValue>(doc, error);
+ auto parseFunction = [&](const std::string& json) {
+ return convertJSON<CameraFunction<float>>(json, error);
};
auto fn1 = parseFunction(R"({"stops":[]})");
@@ -54,3 +52,29 @@ TEST(StyleConversion, Function) {
ASSERT_FALSE(fn9);
ASSERT_EQ("function base must be a number", error.message);
}
+
+TEST(StyleConversion, CompositeFunctionExpression) {
+ Error error;
+
+ auto parseFunction = [&](const std::string& src) {
+ JSDocument doc;
+ doc.Parse<0>(src);
+ return convert<DataDrivenPropertyValue<float>>(doc, error);
+ };
+
+ auto fn1 = parseFunction(R"(["interpolate", ["linear"], ["zoom"], 0, ["number", ["get", "x"]], 10, 10])");
+ ASSERT_TRUE(fn1);
+
+ auto fn2 = parseFunction(R"(["coalesce", ["interpolate", ["linear"], ["zoom"], 0, ["number", ["get", "x"]], 10, 10], 0])");
+ ASSERT_TRUE(fn2);
+
+ auto fn3 = parseFunction(R"(["let", "a", 0, ["interpolate", ["linear"], ["zoom"], 0, ["number", ["get", "x"]], 10, 10] ])");
+ ASSERT_TRUE(fn3);
+
+ auto fn4 = parseFunction(R"(["coalesce", ["let", "a", 0, ["interpolate", ["linear"], ["zoom"], 0, ["number", ["get", "x"]], 10, 10], 0 ])");
+ ASSERT_TRUE(fn4);
+
+ auto fn5 = parseFunction(R"(["coalesce", ["interpolate", ["linear"], ["number", ["get", "x"]], 0, ["zoom"], 10, 10], 0])");
+ ASSERT_FALSE(fn5);
+ ASSERT_EQ(R"("zoom" expression may only be used as input to a top-level "step" or "interpolate" expression.)", error.message);
+}
diff --git a/test/style/conversion/geojson_options.test.cpp b/test/style/conversion/geojson_options.test.cpp
index a798ad6559..4c5a0c9aa4 100644
--- a/test/style/conversion/geojson_options.test.cpp
+++ b/test/style/conversion/geojson_options.test.cpp
@@ -1,8 +1,7 @@
#include <mbgl/test/util.hpp>
-#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/json.hpp>
#include <mbgl/style/conversion/geojson_options.hpp>
-#include <mbgl/test/conversion_stubs.hpp>
#include <mbgl/util/logging.hpp>
@@ -10,26 +9,22 @@ using namespace mbgl::style;
using namespace mbgl::style::conversion;
TEST(GeoJSONOptions, Basic) {
- ValueMap map;
- Value raw(map);
Error error;
- mbgl::optional<GeoJSONOptions> converted = convert<GeoJSONOptions>(raw, error);
+ mbgl::optional<GeoJSONOptions> converted = convertJSON<GeoJSONOptions>("{}", error);
ASSERT_TRUE((bool) converted);
}
TEST(GeoJSONOptions, ErrorHandling) {
- ValueMap map {{"maxzoom", std::string{"should not be a string"}}};
- Value raw(map);
Error error;
- mbgl::optional<GeoJSONOptions> converted = convert<GeoJSONOptions>(raw, error);
+ mbgl::optional<GeoJSONOptions> converted = convertJSON<GeoJSONOptions>(R"JSON({
+ "maxzoom": "should not be a string"
+ })JSON", error);
ASSERT_FALSE((bool) converted);
}
TEST(GeoJSONOptions, RetainsDefaults) {
- ValueMap map;
- Value raw(map);
Error error;
- GeoJSONOptions converted = *convert<GeoJSONOptions>(raw, error);
+ GeoJSONOptions converted = *convertJSON<GeoJSONOptions>("{}", error);
GeoJSONOptions defaults;
// GeoJSON-VT
@@ -44,22 +39,16 @@ TEST(GeoJSONOptions, RetainsDefaults) {
ASSERT_EQ(converted.clusterMaxZoom, defaults.clusterMaxZoom);
}
-
TEST(GeoJSONOptions, FullConversion) {
- ValueMap map {
- // GeoJSON-VT
- {"maxzoom", 1.0f},
- {"buffer", 2.0f},
- {"tolerance", 3.0f},
-
- // Supercluster
- {"cluster", true},
- {"clusterRadius", 4.0f},
- {"clusterMaxZoom", 5.0f}
- };
- Value raw(map);
Error error;
- GeoJSONOptions converted = *convert<GeoJSONOptions>(raw, error);
+ GeoJSONOptions converted = *convertJSON<GeoJSONOptions>(R"JSON({
+ "maxzoom": 1,
+ "buffer": 2,
+ "tolerance": 3,
+ "cluster": true,
+ "clusterRadius": 4,
+ "clusterMaxZoom": 5
+ })JSON", error);
// GeoJSON-VT
ASSERT_EQ(converted.minzoom, 0);
diff --git a/test/style/conversion/layer.test.cpp b/test/style/conversion/layer.test.cpp
index d51d7d33e2..33cd329999 100644
--- a/test/style/conversion/layer.test.cpp
+++ b/test/style/conversion/layer.test.cpp
@@ -1,10 +1,8 @@
#include <mbgl/test/util.hpp>
-#include <mbgl/style/conversion.hpp>
-#include <mbgl/style/rapidjson_conversion.hpp>
+#include <mbgl/style/conversion/json.hpp>
#include <mbgl/style/conversion/layer.hpp>
#include <mbgl/style/layers/background_layer_impl.hpp>
-#include <mbgl/util/rapidjson.hpp>
using namespace mbgl;
using namespace mbgl::style;
@@ -12,10 +10,8 @@ using namespace mbgl::style::conversion;
using namespace std::literals::chrono_literals;
std::unique_ptr<Layer> parseLayer(const std::string& src) {
- JSDocument doc;
- doc.Parse<0>(src);
Error error;
- return std::move(*convert<std::unique_ptr<Layer>, JSValue>(doc, error));
+ return std::move(*convertJSON<std::unique_ptr<Layer>>(src, error));
}
TEST(StyleConversion, LayerTransition) {
diff --git a/test/style/conversion/light.test.cpp b/test/style/conversion/light.test.cpp
index 28e22b3550..67e48c942e 100644
--- a/test/style/conversion/light.test.cpp
+++ b/test/style/conversion/light.test.cpp
@@ -1,11 +1,10 @@
#include <mbgl/test/util.hpp>
#include <mbgl/style/conversion.hpp>
-#include <mbgl/style/rapidjson_conversion.hpp>
+#include <mbgl/style/conversion/json.hpp>
#include <mbgl/style/conversion/constant.hpp>
#include <mbgl/style/conversion/light.hpp>
#include <mbgl/style/position.hpp>
-#include <mbgl/util/rapidjson.hpp>
#include <mbgl/util/color.hpp>
#include <mbgl/util/chrono.hpp>
@@ -19,9 +18,7 @@ TEST(StyleConversion, Light) {
Error error;
auto parseLight = [&](const std::string& src) {
- JSDocument doc;
- doc.Parse<0>(src);
- return convert<Light>(doc, error);
+ return convertJSON<Light>(src, error);
};
{
diff --git a/test/style/expression/expression.test.cpp b/test/style/expression/expression.test.cpp
new file mode 100644
index 0000000000..694569695c
--- /dev/null
+++ b/test/style/expression/expression.test.cpp
@@ -0,0 +1,91 @@
+#include <mbgl/test/util.hpp>
+#include <mbgl/util/io.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/util/rapidjson.hpp>
+#include <mbgl/style/rapidjson_conversion.hpp>
+#include <mbgl/style/expression/is_expression.hpp>
+
+#include <rapidjson/document.h>
+
+#include <iostream>
+#include <fstream>
+#include <dirent.h>
+
+
+using namespace mbgl;
+using namespace mbgl::style;
+
+TEST(Expression, IsExpression) {
+ rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> spec;
+ spec.Parse<0>(util::read_file("mapbox-gl-js/src/style-spec/reference/v8.json").c_str());
+ ASSERT_FALSE(spec.HasParseError());
+ ASSERT_TRUE(spec.IsObject() &&
+ spec.HasMember("expression_name") &&
+ spec["expression_name"].IsObject() &&
+ spec["expression_name"].HasMember("values") &&
+ spec["expression_name"]["values"].IsObject());
+
+ const auto& allExpressions = spec["expression_name"]["values"];
+
+ for(auto& entry : allExpressions.GetObject()) {
+ const std::string name { entry.name.GetString(), entry.name.GetStringLength() };
+ JSDocument document;
+ document.Parse<0>(R"([")" + name + R"("])");
+
+ const JSValue* expression = &document;
+ EXPECT_TRUE(expression::isExpression(conversion::Convertible(expression))) << name;
+ }
+}
+
+class ExpressionEqualityTest : public ::testing::TestWithParam<std::string> {};
+
+TEST_P(ExpressionEqualityTest, ExpressionEquality) {
+ const std::string base = std::string("test/fixtures/expression_equality/") + GetParam();
+
+ std::string error;
+ auto parse = [&](std::string filename, std::string& error_) -> std::unique_ptr<expression::Expression> {
+ rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> document;
+ document.Parse<0>(util::read_file(filename).c_str());
+ assert(!document.HasParseError());
+ const JSValue* expression = &document;
+ expression::ParsingContext ctx;
+ expression::ParseResult parsed = ctx.parse(conversion::Convertible(expression));
+ if (!parsed) {
+ error_ = ctx.getErrors().size() > 0 ? ctx.getErrors()[0].message : "failed to parse";
+ };
+ return std::move(*parsed);
+ };
+
+ std::unique_ptr<expression::Expression> expression_a1 = parse(base + ".a.json", error);
+ ASSERT_TRUE(expression_a1) << GetParam() << ": " << error;
+
+ std::unique_ptr<expression::Expression> expression_a2 = parse(base + ".a.json", error);
+ ASSERT_TRUE(expression_a2) << GetParam() << ": " << error;
+
+ std::unique_ptr<expression::Expression> expression_b = parse(base + ".b.json", error);
+ ASSERT_TRUE(expression_b) << GetParam() << ": " << error;
+
+
+ EXPECT_TRUE(*expression_a1 == *expression_a2);
+ EXPECT_TRUE(*expression_a1 != *expression_b);
+}
+
+INSTANTIATE_TEST_CASE_P(Expression, ExpressionEqualityTest, ::testing::ValuesIn([] {
+ std::vector<std::string> names;
+ const std::string ending = ".a.json";
+
+ const std::string style_directory = "test/fixtures/expression_equality";
+ DIR *dir = opendir(style_directory.c_str());
+ if (dir != nullptr) {
+ for (dirent *dp = nullptr; (dp = readdir(dir)) != nullptr;) {
+ const std::string name = dp->d_name;
+ if (name.length() >= ending.length() && name.compare(name.length() - ending.length(), ending.length(), ending) == 0) {
+ names.push_back(name.substr(0, name.length() - ending.length()));
+ }
+ }
+ closedir(dir);
+ }
+
+ EXPECT_GT(names.size(), 0u);
+ return names;
+}()));
diff --git a/test/style/expression/util.test.cpp b/test/style/expression/util.test.cpp
new file mode 100644
index 0000000000..0337cd871f
--- /dev/null
+++ b/test/style/expression/util.test.cpp
@@ -0,0 +1,23 @@
+#include <mbgl/test/util.hpp>
+#include <mbgl/style/expression/util.hpp>
+
+using namespace mbgl;
+using namespace mbgl::style::expression;
+
+TEST(Expression, Util_rgba) {
+ Result<Color> valid = rgba(0, 0, 0, 0);
+ ASSERT_TRUE(valid);
+ ASSERT_EQ(valid->r, 0);
+ ASSERT_EQ(valid->g, 0);
+ ASSERT_EQ(valid->b, 0);
+ ASSERT_EQ(valid->a, 0);
+
+ ASSERT_FALSE(rgba(0, 0, 0, -0.1));
+ ASSERT_FALSE(rgba(0, 0, 0, 1.1));
+ ASSERT_FALSE(rgba(0, 0, -1, 1));
+ ASSERT_FALSE(rgba(0, 0, 256, 1));
+ ASSERT_FALSE(rgba(0, -1, 0, 1));
+ ASSERT_FALSE(rgba(0, 256, 0, 1));
+ ASSERT_FALSE(rgba(-1, 1, 0, 1));
+ ASSERT_FALSE(rgba(-256, 1, 0, 1));
+}
diff --git a/test/style/filter.test.cpp b/test/style/filter.test.cpp
index 96de125945..73f8e7626d 100644
--- a/test/style/filter.test.cpp
+++ b/test/style/filter.test.cpp
@@ -4,20 +4,15 @@
#include <mbgl/style/filter.hpp>
#include <mbgl/style/filter_evaluator.hpp>
-#include <mbgl/style/rapidjson_conversion.hpp>
-#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/json.hpp>
#include <mbgl/style/conversion/filter.hpp>
-#include <rapidjson/document.h>
-
using namespace mbgl;
using namespace mbgl::style;
Filter parse(const char * expression) {
- rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> doc;
- doc.Parse<0>(expression);
conversion::Error error;
- optional<Filter> filter = conversion::convert<Filter, JSValue>(doc, error);
+ optional<Filter> filter = conversion::convertJSON<Filter>(expression, error);
EXPECT_TRUE(bool(filter));
return *filter;
}
diff --git a/test/style/source.test.cpp b/test/style/source.test.cpp
index 919260ffe9..6cb01146f6 100644
--- a/test/style/source.test.cpp
+++ b/test/style/source.test.cpp
@@ -6,13 +6,17 @@
#include <mbgl/style/style.hpp>
#include <mbgl/style/source_impl.hpp>
#include <mbgl/style/sources/raster_source.hpp>
+#include <mbgl/style/sources/raster_dem_source.hpp>
#include <mbgl/style/sources/vector_source.hpp>
#include <mbgl/style/sources/geojson_source.hpp>
#include <mbgl/style/sources/image_source.hpp>
+#include <mbgl/style/sources/custom_geometry_source.hpp>
+#include <mbgl/style/layers/hillshade_layer.cpp>
#include <mbgl/style/layers/raster_layer.cpp>
#include <mbgl/style/layers/line_layer.hpp>
#include <mbgl/renderer/sources/render_raster_source.hpp>
+#include <mbgl/renderer/sources/render_raster_dem_source.hpp>
#include <mbgl/renderer/sources/render_vector_source.hpp>
#include <mbgl/renderer/sources/render_geojson_source.hpp>
#include <mbgl/renderer/tile_parameters.hpp>
@@ -24,7 +28,7 @@
#include <mbgl/util/image.hpp>
#include <mbgl/util/tileset.hpp>
-#include <mbgl/util/default_thread_pool.hpp>
+#include <mbgl/util/shared_thread_pool.hpp>
#include <mbgl/util/logging.hpp>
#include <mbgl/util/optional.hpp>
#include <mbgl/util/range.hpp>
@@ -38,6 +42,7 @@
#include <cstdint>
using namespace mbgl;
+using SourceType = mbgl::style::SourceType;
class SourceTest {
public:
@@ -171,6 +176,44 @@ TEST(Source, RasterTileEmpty) {
test.run();
}
+TEST(Source, RasterDEMTileEmpty) {
+ SourceTest test;
+
+ test.fileSource.tileResponse = [&] (const Resource&) {
+ Response response;
+ response.noContent = true;
+ return response;
+ };
+
+ HillshadeLayer layer("id", "source");
+ std::vector<Immutable<Layer::Impl>> layers {{ layer.baseImpl }};
+
+ Tileset tileset;
+ tileset.tiles = { "tiles" };
+
+ RasterDEMSource source("source", tileset, 512);
+ source.loadDescription(test.fileSource);
+
+ test.renderSourceObserver.tileChanged = [&] (RenderSource& source_, const OverscaledTileID&) {
+ EXPECT_EQ("source", source_.baseImpl->id);
+ test.end();
+ };
+
+ test.renderSourceObserver.tileError = [&] (RenderSource&, const OverscaledTileID&, std::exception_ptr) {
+ FAIL() << "Should never be called";
+ };
+
+ auto renderSource = RenderSource::create(source.baseImpl);
+ renderSource->setObserver(&test.renderSourceObserver);
+ renderSource->update(source.baseImpl,
+ layers,
+ true,
+ true,
+ test.tileParameters);
+
+ test.run();
+}
+
TEST(Source, VectorTileEmpty) {
SourceTest test;
@@ -249,6 +292,44 @@ TEST(Source, RasterTileFail) {
test.run();
}
+TEST(Source, RasterDEMTileFail) {
+ SourceTest test;
+
+ test.fileSource.tileResponse = [&] (const Resource&) {
+ Response response;
+ response.error = std::make_unique<Response::Error>(
+ Response::Error::Reason::Other,
+ "Failed by the test case");
+ return response;
+ };
+
+ HillshadeLayer layer("id", "source");
+ std::vector<Immutable<Layer::Impl>> layers {{ layer.baseImpl }};
+
+ Tileset tileset;
+ tileset.tiles = { "tiles" };
+
+ RasterDEMSource source("source", tileset, 512);
+ source.loadDescription(test.fileSource);
+
+ test.renderSourceObserver.tileError = [&] (RenderSource& source_, const OverscaledTileID& tileID, std::exception_ptr error) {
+ EXPECT_EQ(SourceType::RasterDEM, source_.baseImpl->type);
+ EXPECT_EQ(OverscaledTileID(0, 0, 0), tileID);
+ EXPECT_EQ("Failed by the test case", util::toString(error));
+ test.end();
+ };
+
+ auto renderSource = RenderSource::create(source.baseImpl);
+ renderSource->setObserver(&test.renderSourceObserver);
+ renderSource->update(source.baseImpl,
+ layers,
+ true,
+ true,
+ test.tileParameters);
+
+ test.run();
+}
+
TEST(Source, VectorTileFail) {
SourceTest test;
@@ -326,6 +407,43 @@ TEST(Source, RasterTileCorrupt) {
test.run();
}
+TEST(Source, RasterDEMTileCorrupt) {
+ SourceTest test;
+
+ test.fileSource.tileResponse = [&] (const Resource&) {
+ Response response;
+ response.data = std::make_unique<std::string>("CORRUPTED");
+ return response;
+ };
+
+ HillshadeLayer layer("id", "source");
+ std::vector<Immutable<Layer::Impl>> layers {{ layer.baseImpl }};
+
+ Tileset tileset;
+ tileset.tiles = { "tiles" };
+
+ RasterDEMSource source("source", tileset, 512);
+ source.loadDescription(test.fileSource);
+
+ test.renderSourceObserver.tileError = [&] (RenderSource& source_, const OverscaledTileID& tileID, std::exception_ptr error) {
+ EXPECT_EQ(source_.baseImpl->type, SourceType::RasterDEM);
+ EXPECT_EQ(OverscaledTileID(0, 0, 0), tileID);
+ EXPECT_TRUE(bool(error));
+ // Not asserting on platform-specific error text.
+ test.end();
+ };
+
+ auto renderSource = RenderSource::create(source.baseImpl);
+ renderSource->setObserver(&test.renderSourceObserver);
+ renderSource->update(source.baseImpl,
+ layers,
+ true,
+ true,
+ test.tileParameters);
+
+ test.run();
+}
+
TEST(Source, VectorTileCorrupt) {
SourceTest test;
@@ -400,6 +518,42 @@ TEST(Source, RasterTileCancel) {
test.run();
}
+TEST(Source, RasterDEMTileCancel) {
+ SourceTest test;
+
+ test.fileSource.tileResponse = [&] (const Resource&) {
+ test.end();
+ return optional<Response>();
+ };
+
+ HillshadeLayer layer("id", "source");
+ std::vector<Immutable<Layer::Impl>> layers {{ layer.baseImpl }};
+
+ Tileset tileset;
+ tileset.tiles = { "tiles" };
+
+ RasterDEMSource source("source", tileset, 512);
+ source.loadDescription(test.fileSource);
+
+ test.renderSourceObserver.tileChanged = [&] (RenderSource&, const OverscaledTileID&) {
+ FAIL() << "Should never be called";
+ };
+
+ test.renderSourceObserver.tileError = [&] (RenderSource&, const OverscaledTileID&, std::exception_ptr) {
+ FAIL() << "Should never be called";
+ };
+
+ auto renderSource = RenderSource::create(source.baseImpl);
+ renderSource->setObserver(&test.renderSourceObserver);
+ renderSource->update(source.baseImpl,
+ layers,
+ true,
+ true,
+ test.tileParameters);
+
+ test.run();
+}
+
TEST(Source, VectorTileCancel) {
SourceTest test;
@@ -482,6 +636,48 @@ TEST(Source, RasterTileAttribution) {
test.run();
}
+TEST(Source, RasterDEMTileAttribution) {
+ SourceTest test;
+
+ HillshadeLayer layer("id", "source");
+ std::vector<Immutable<Layer::Impl>> layers {{ layer.baseImpl }};
+
+ std::string mapbox = ("<a href='https://www.mapbox.com/about/maps/' target='_blank'>&copy; Mapbox</a> ");
+
+ test.fileSource.tileResponse = [&] (const Resource&) {
+ Response response;
+ response.noContent = true;
+ return response;
+ };
+
+ test.fileSource.sourceResponse = [&] (const Resource& resource) {
+ EXPECT_EQ("url", resource.url);
+ Response response;
+ response.data = std::make_unique<std::string>(R"TILEJSON({ "tilejson": "2.1.0", "attribution": ")TILEJSON" +
+ mapbox +
+ R"TILEJSON(", "tiles": [ "tiles" ] })TILEJSON");
+ return response;
+ };
+
+ test.styleObserver.sourceChanged = [&] (Source& source) {
+ EXPECT_EQ(mapbox, source.getAttribution());
+ test.end();
+ };
+
+ RasterDEMSource source("source", "url", 512);
+ source.setObserver(&test.styleObserver);
+ source.loadDescription(test.fileSource);
+
+ auto renderSource = RenderSource::create(source.baseImpl);
+ renderSource->update(source.baseImpl,
+ layers,
+ true,
+ true,
+ test.tileParameters);
+
+ test.run();
+}
+
TEST(Source, GeoJSonSourceUrlUpdate) {
SourceTest test;
@@ -547,3 +743,39 @@ TEST(Source, ImageSourceImageUpdate) {
test.run();
}
+
+TEST(Source, CustomGeometrySourceSetTileData) {
+ SourceTest test;
+ std::shared_ptr<ThreadPool> threadPool = sharedThreadPool();
+ CustomGeometrySource source("source", CustomGeometrySource::Options());
+ source.loadDescription(test.fileSource);
+
+ LineLayer layer("id", "source");
+ layer.setSourceLayer("water");
+ std::vector<Immutable<Layer::Impl>> layers {{ layer.baseImpl }};
+
+ test.renderSourceObserver.tileChanged = [&] (RenderSource& source_, const OverscaledTileID&) {
+ EXPECT_EQ("source", source_.baseImpl->id);
+ test.end();
+ };
+
+ test.renderSourceObserver.tileError = [&] (RenderSource&, const OverscaledTileID&, std::exception_ptr) {
+ FAIL() << "Should never be called";
+ };
+
+ auto renderSource = RenderSource::create(source.baseImpl);
+ renderSource->setObserver(&test.renderSourceObserver);
+ renderSource->update(source.baseImpl,
+ layers,
+ true,
+ true,
+ test.tileParameters);
+
+ test.loop.invoke([&] () {
+ // Set Tile Data
+ source.setTileData(CanonicalTileID(0, 0, 0), GeoJSON{ FeatureCollection{} });
+ });
+
+ test.run();
+}
+
diff --git a/test/style/style_parser.test.cpp b/test/style/style_parser.test.cpp
index 5fa81b47e9..43b982c3b9 100644
--- a/test/style/style_parser.test.cpp
+++ b/test/style/style_parser.test.cpp
@@ -102,3 +102,99 @@ TEST(StyleParser, FontStacks) {
ASSERT_EQ(FontStack({"a", "b"}), result[1]);
ASSERT_EQ(FontStack({"a", "b", "c"}), result[2]);
}
+
+TEST(StyleParser, FontStacksNoTextField) {
+ style::Parser parser;
+ parser.parse(R"({
+ "version": 8,
+ "layers": [{
+ "id": "symbol",
+ "type": "symbol",
+ "source": "vector",
+ "layout": {
+ "text-font": ["a"]
+ }
+ }]
+ })");
+ auto result = parser.fontStacks();
+ ASSERT_EQ(0u, result.size());
+}
+
+TEST(StyleParser, FontStacksCaseExpression) {
+ style::Parser parser;
+ parser.parse(R"({
+ "version": 8,
+ "layers": [{
+ "id": "symbol",
+ "type": "symbol",
+ "source": "vector",
+ "layout": {
+ "text-field": "a",
+ "text-font": ["case", ["==", "a", ["string", ["get", "text-font"]]], ["literal", ["Arial"]], ["literal", ["Helvetica"]]]
+ }
+ }]
+ })");
+ auto result = parser.fontStacks();
+ ASSERT_EQ(2u, result.size());
+ ASSERT_EQ(FontStack({"Arial"}), result[0]);
+ ASSERT_EQ(FontStack({"Helvetica"}), result[1]);
+}
+
+TEST(StyleParser, FontStacksMatchExpression) {
+ style::Parser parser;
+ parser.parse(R"({
+ "version": 8,
+ "layers": [{
+ "id": "symbol",
+ "type": "symbol",
+ "source": "vector",
+ "layout": {
+ "text-field": "a",
+ "text-font": ["match", ["get", "text-font"], "a", ["literal", ["Arial"]], ["literal", ["Helvetica"]]]
+ }
+ }]
+ })");
+ auto result = parser.fontStacks();
+ ASSERT_EQ(2u, result.size());
+ ASSERT_EQ(FontStack({"Arial"}), result[0]);
+ ASSERT_EQ(FontStack({"Helvetica"}), result[1]);
+}
+
+TEST(StyleParser, FontStacksStepExpression) {
+ style::Parser parser;
+ parser.parse(R"({
+ "version": 8,
+ "layers": [{
+ "id": "symbol",
+ "type": "symbol",
+ "source": "vector",
+ "layout": {
+ "text-field": "a",
+ "text-font": ["array", "string", ["step", ["get", "text-font"], ["literal", ["Arial"]], 0, ["literal", ["Helvetica"]]]]
+ }
+ }]
+ })");
+ auto result = parser.fontStacks();
+ ASSERT_EQ(2u, result.size());
+ ASSERT_EQ(FontStack({"Arial"}), result[0]);
+ ASSERT_EQ(FontStack({"Helvetica"}), result[1]);
+}
+
+TEST(StyleParser, FontStacksGetExpression) {
+ // Invalid style, but not currently validated.
+ style::Parser parser;
+ parser.parse(R"({
+ "version": 8,
+ "layers": [{
+ "id": "symbol",
+ "type": "symbol",
+ "source": "vector",
+ "layout": {
+ "text-field": "a",
+ "text-font": ["array", "string", ["get", "text-font"]]
+ }
+ }]
+ })");
+ auto result = parser.fontStacks();
+ ASSERT_EQ(0u, result.size());
+}
diff --git a/test/text/cross_tile_symbol_index.test.cpp b/test/text/cross_tile_symbol_index.test.cpp
new file mode 100644
index 0000000000..794c2b59a1
--- /dev/null
+++ b/test/text/cross_tile_symbol_index.test.cpp
@@ -0,0 +1,203 @@
+#include <mbgl/text/cross_tile_symbol_index.hpp>
+#include <mbgl/renderer/buckets/symbol_bucket.hpp>
+#include <mbgl/test/util.hpp>
+
+using namespace mbgl;
+
+SymbolInstance makeSymbolInstance(float x, float y, std::u16string key) {
+ GeometryCoordinates line;
+ GlyphPositionMap gpm;
+ const std::pair<Shaping, Shaping> shaping(Shaping{}, Shaping{});
+ style::SymbolLayoutProperties::Evaluated layout_;
+ IndexedSubfeature subfeature(0, "", "", 0);
+ Anchor anchor(x, y, 0, 0);
+ return {anchor, line, shaping, {}, layout_, 0, 0, 0, 0, style::SymbolPlacementType::Point, {{0, 0}}, 0, 0, {{0, 0}}, gpm, subfeature, 0, key, 0 };
+}
+
+
+TEST(CrossTileSymbolLayerIndex, addBucket) {
+
+ uint32_t maxCrossTileID = 0;
+ uint32_t maxBucketInstanceId = 0;
+ CrossTileSymbolLayerIndex index;
+
+ style::SymbolLayoutProperties::PossiblyEvaluated layout;
+ bool sdfIcons = false;
+ bool iconsNeedLinear = false;
+ bool sortFeaturesByY = false;
+
+ OverscaledTileID mainID(6, 0, 6, 8, 8);
+ std::vector<SymbolInstance> mainInstances;
+ mainInstances.push_back(makeSymbolInstance(1000, 1000, u"Detroit"));
+ mainInstances.push_back(makeSymbolInstance(2000, 2000, u"Toronto"));
+ SymbolBucket mainBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(mainInstances) };
+ mainBucket.bucketInstanceId = ++maxBucketInstanceId;
+ index.addBucket(mainID, mainBucket, maxCrossTileID);
+
+ // Assigned new IDs
+ ASSERT_EQ(mainBucket.symbolInstances.at(0).crossTileID, 1u);
+ ASSERT_EQ(mainBucket.symbolInstances.at(1).crossTileID, 2u);
+
+
+ OverscaledTileID childID(7, 0, 7, 16, 16);
+ std::vector<SymbolInstance> childInstances;
+ childInstances.push_back(makeSymbolInstance(2000, 2000, u"Detroit"));
+ childInstances.push_back(makeSymbolInstance(2000, 2000, u"Windsor"));
+ childInstances.push_back(makeSymbolInstance(3000, 3000, u"Toronto"));
+ childInstances.push_back(makeSymbolInstance(4001, 4001, u"Toronto"));
+ SymbolBucket childBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(childInstances) };
+ childBucket.bucketInstanceId = ++maxBucketInstanceId;
+ index.addBucket(childID, childBucket, maxCrossTileID);
+
+ // matched parent tile
+ ASSERT_EQ(childBucket.symbolInstances.at(0).crossTileID, 1u);
+ // does not match because of different key
+ ASSERT_EQ(childBucket.symbolInstances.at(1).crossTileID, 3u);
+ // does not match because of different location
+ ASSERT_EQ(childBucket.symbolInstances.at(2).crossTileID, 4u);
+ // matches with a slightly different location
+ ASSERT_EQ(childBucket.symbolInstances.at(3).crossTileID, 2u);
+
+ OverscaledTileID parentID(5, 0, 5, 4, 4);
+ std::vector<SymbolInstance> parentInstances;
+ parentInstances.push_back(makeSymbolInstance(500, 500, u"Detroit"));
+ SymbolBucket parentBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(parentInstances) };
+ parentBucket.bucketInstanceId = ++maxBucketInstanceId;
+ index.addBucket(parentID, parentBucket, maxCrossTileID);
+
+ // matched child tile
+ ASSERT_EQ(parentBucket.symbolInstances.at(0).crossTileID, 1u);
+
+ std::unordered_set<uint32_t> currentIDs;
+ currentIDs.insert(mainBucket.bucketInstanceId);
+ index.removeStaleBuckets(currentIDs);
+
+ // grandchild
+ OverscaledTileID grandchildID(8, 0, 8, 32, 32);
+ std::vector<SymbolInstance> grandchildInstances;
+ grandchildInstances.push_back(makeSymbolInstance(4000, 4000, u"Detroit"));
+ grandchildInstances.push_back(makeSymbolInstance(4000, 4000, u"Windsor"));
+ SymbolBucket grandchildBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(grandchildInstances) };
+ grandchildBucket.bucketInstanceId = ++maxBucketInstanceId;
+ index.addBucket(grandchildID, grandchildBucket, maxCrossTileID);
+
+ // Matches the symbol in `mainBucket`
+ ASSERT_EQ(grandchildBucket.symbolInstances.at(0).crossTileID, 1u);
+ // Does not match the previous value for Windsor because that tile was removed
+ ASSERT_EQ(grandchildBucket.symbolInstances.at(1).crossTileID, 5u);
+
+}
+
+TEST(CrossTileSymbolLayerIndex, resetIDs) {
+
+ uint32_t maxCrossTileID = 0;
+ uint32_t maxBucketInstanceId = 0;
+ CrossTileSymbolLayerIndex index;
+
+ style::SymbolLayoutProperties::PossiblyEvaluated layout;
+ bool sdfIcons = false;
+ bool iconsNeedLinear = false;
+ bool sortFeaturesByY = false;
+
+ OverscaledTileID mainID(6, 0, 6, 8, 8);
+ std::vector<SymbolInstance> mainInstances;
+ mainInstances.push_back(makeSymbolInstance(1000, 1000, u"Detroit"));
+ SymbolBucket mainBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(mainInstances) };
+ mainBucket.bucketInstanceId = ++maxBucketInstanceId;
+
+ OverscaledTileID childID(7, 0, 7, 16, 16);
+ std::vector<SymbolInstance> childInstances;
+ childInstances.push_back(makeSymbolInstance(2000, 2000, u"Detroit"));
+ SymbolBucket childBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(childInstances) };
+ childBucket.bucketInstanceId = ++maxBucketInstanceId;
+
+ // assigns a new id
+ index.addBucket(mainID, mainBucket, maxCrossTileID);
+ ASSERT_EQ(mainBucket.symbolInstances.at(0).crossTileID, 1u);
+
+ // removes the tile
+ std::unordered_set<uint32_t> currentIDs;
+ index.removeStaleBuckets(currentIDs);
+
+ // assigns a new id
+ index.addBucket(childID, childBucket, maxCrossTileID);
+ ASSERT_EQ(childBucket.symbolInstances.at(0).crossTileID, 2u);
+
+ // overwrites the old id to match the already-added tile
+ index.addBucket(mainID, mainBucket, maxCrossTileID);
+ ASSERT_EQ(mainBucket.symbolInstances.at(0).crossTileID, 2u);
+}
+
+TEST(CrossTileSymbolLayerIndex, noDuplicatesWithinZoomLevel) {
+ uint32_t maxCrossTileID = 0;
+ uint32_t maxBucketInstanceId = 0;
+ CrossTileSymbolLayerIndex index;
+
+ style::SymbolLayoutProperties::PossiblyEvaluated layout;
+ bool sdfIcons = false;
+ bool iconsNeedLinear = false;
+ bool sortFeaturesByY = false;
+
+ OverscaledTileID mainID(6, 0, 6, 8, 8);
+ std::vector<SymbolInstance> mainInstances;
+ mainInstances.push_back(makeSymbolInstance(1000, 1000, u"")); // A
+ mainInstances.push_back(makeSymbolInstance(1000, 1000, u"")); // B
+ SymbolBucket mainBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(mainInstances) };
+ mainBucket.bucketInstanceId = ++maxBucketInstanceId;
+
+ OverscaledTileID childID(7, 0, 7, 16, 16);
+ std::vector<SymbolInstance> childInstances;
+ childInstances.push_back(makeSymbolInstance(2000, 2000, u"")); // A'
+ childInstances.push_back(makeSymbolInstance(2000, 2000, u"")); // B'
+ childInstances.push_back(makeSymbolInstance(2000, 2000, u"")); // C'
+ SymbolBucket childBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(childInstances) };
+ childBucket.bucketInstanceId = ++maxBucketInstanceId;
+
+ // assigns new ids
+ index.addBucket(mainID, mainBucket, maxCrossTileID);
+ ASSERT_EQ(mainBucket.symbolInstances.at(0).crossTileID, 1u);
+ ASSERT_EQ(mainBucket.symbolInstances.at(1).crossTileID, 2u);
+
+ // copies parent ids without duplicate ids in this tile
+ index.addBucket(childID, childBucket, maxCrossTileID);
+ ASSERT_EQ(childBucket.symbolInstances.at(0).crossTileID, 1u); // A' copies from A
+ ASSERT_EQ(childBucket.symbolInstances.at(1).crossTileID, 2u); // B' copies from B
+ ASSERT_EQ(childBucket.symbolInstances.at(2).crossTileID, 3u); // C' gets new ID
+}
+
+TEST(CrossTileSymbolLayerIndex, bucketReplacement) {
+ uint32_t maxCrossTileID = 0;
+ uint32_t maxBucketInstanceId = 0;
+ CrossTileSymbolLayerIndex index;
+
+ style::SymbolLayoutProperties::PossiblyEvaluated layout;
+ bool sdfIcons = false;
+ bool iconsNeedLinear = false;
+ bool sortFeaturesByY = false;
+
+ OverscaledTileID tileID(6, 0, 6, 8, 8);
+ std::vector<SymbolInstance> firstInstances;
+ firstInstances.push_back(makeSymbolInstance(1000, 1000, u"")); // A
+ firstInstances.push_back(makeSymbolInstance(1000, 1000, u"")); // B
+ SymbolBucket firstBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(firstInstances) };
+ firstBucket.bucketInstanceId = ++maxBucketInstanceId;
+
+ std::vector<SymbolInstance> secondInstances;
+ secondInstances.push_back(makeSymbolInstance(1000, 1000, u"")); // A'
+ secondInstances.push_back(makeSymbolInstance(1000, 1000, u"")); // B'
+ secondInstances.push_back(makeSymbolInstance(1000, 1000, u"")); // C'
+ SymbolBucket secondBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(secondInstances) };
+ secondBucket.bucketInstanceId = ++maxBucketInstanceId;
+
+ // assigns new ids
+ index.addBucket(tileID, firstBucket, maxCrossTileID);
+ ASSERT_EQ(firstBucket.symbolInstances.at(0).crossTileID, 1u);
+ ASSERT_EQ(firstBucket.symbolInstances.at(1).crossTileID, 2u);
+
+ // copies parent ids without duplicate ids in this tile
+ index.addBucket(tileID, secondBucket, maxCrossTileID);
+ ASSERT_EQ(secondBucket.symbolInstances.at(0).crossTileID, 1u); // A' copies from A
+ ASSERT_EQ(secondBucket.symbolInstances.at(1).crossTileID, 2u); // B' copies from B
+ ASSERT_EQ(secondBucket.symbolInstances.at(2).crossTileID, 3u); // C' gets new ID
+}
+
diff --git a/test/text/glyph_loader.test.cpp b/test/text/glyph_loader.test.cpp
deleted file mode 100644
index be197ebb46..0000000000
--- a/test/text/glyph_loader.test.cpp
+++ /dev/null
@@ -1,216 +0,0 @@
-#include <mbgl/test/util.hpp>
-#include <mbgl/test/stub_file_source.hpp>
-
-#include <mbgl/text/glyph_manager.hpp>
-#include <mbgl/util/run_loop.hpp>
-#include <mbgl/util/string.hpp>
-#include <mbgl/util/io.hpp>
-#include <mbgl/util/logging.hpp>
-
-using namespace mbgl;
-
-class StubGlyphManagerObserver : public GlyphManagerObserver {
-public:
- void onGlyphsLoaded(const FontStack& fontStack, const GlyphRange& glyphRange) override {
- if (glyphsLoaded) glyphsLoaded(fontStack, glyphRange);
- }
-
- void onGlyphsError(const FontStack& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) override {
- if (glyphsError) glyphsError(fontStack, glyphRange, error);
- }
-
- std::function<void (const FontStack&, const GlyphRange&)> glyphsLoaded;
- std::function<void (const FontStack&, const GlyphRange&, std::exception_ptr)> glyphsError;
-};
-
-class StubGlyphRequestor : public GlyphRequestor {
-public:
- void onGlyphsAvailable(GlyphMap glyphs) override {
- if (glyphsAvailable) glyphsAvailable(std::move(glyphs));
- }
-
- std::function<void (GlyphMap)> glyphsAvailable;
-};
-
-class GlyphManagerTest {
-public:
- util::RunLoop loop;
- StubFileSource fileSource;
- StubGlyphManagerObserver observer;
- StubGlyphRequestor requestor;
- GlyphManager glyphManager { fileSource };
-
- void run(const std::string& url, GlyphDependencies dependencies) {
- // Squelch logging.
- Log::setObserver(std::make_unique<Log::NullObserver>());
-
- glyphManager.setURL(url);
- glyphManager.setObserver(&observer);
- glyphManager.getGlyphs(requestor, std::move(dependencies));
-
- loop.run();
- }
-
- void end() {
- loop.stop();
- }
-};
-
-TEST(GlyphManager, LoadingSuccess) {
- GlyphManagerTest test;
-
- test.fileSource.glyphsResponse = [&] (const Resource& resource) {
- EXPECT_EQ(Resource::Kind::Glyphs, resource.kind);
- Response response;
- response.data = std::make_shared<std::string>(util::read_file("test/fixtures/resources/glyphs.pbf"));
- return response;
- };
-
- test.observer.glyphsError = [&] (const FontStack&, const GlyphRange&, std::exception_ptr) {
- FAIL();
- test.end();
- };
-
- test.observer.glyphsLoaded = [&] (const FontStack& fontStack, const GlyphRange& range) {
- ASSERT_EQ(fontStack, FontStack {{"Test Stack"}});
- ASSERT_EQ(range, GlyphRange(0, 255));
- };
-
- test.requestor.glyphsAvailable = [&] (GlyphMap glyphs) {
- const auto& testPositions = glyphs.at({{"Test Stack"}});
-
- ASSERT_EQ(testPositions.size(), 3u);
- ASSERT_EQ(testPositions.count(u'a'), 1u);
- ASSERT_EQ(testPositions.count(u'å'), 1u);
- ASSERT_EQ(testPositions.count(u' '), 1u);
- ASSERT_TRUE(bool(testPositions.at(u' ')));
-
- test.end();
- };
-
- test.run(
- "test/fixtures/resources/glyphs.pbf",
- GlyphDependencies {
- {{{"Test Stack"}}, {u'a', u'å', u' '}}
- });
-}
-
-TEST(GlyphManager, LoadingFail) {
- GlyphManagerTest test;
-
- test.fileSource.glyphsResponse = [&] (const Resource&) {
- Response response;
- response.error = std::make_unique<Response::Error>(
- Response::Error::Reason::Other,
- "Failed by the test case");
- return response;
- };
-
- test.observer.glyphsError = [&] (const FontStack& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) {
- EXPECT_EQ(fontStack, FontStack({"Test Stack"}));
- EXPECT_EQ(glyphRange, GlyphRange(0, 255));
-
- EXPECT_TRUE(error != nullptr);
- EXPECT_EQ(util::toString(error), "Failed by the test case");
-
- test.end();
- };
-
- test.requestor.glyphsAvailable = [&] (GlyphMap) {
- FAIL();
- test.end();
- };
-
- test.run(
- "test/fixtures/resources/glyphs.pbf",
- GlyphDependencies {
- {{{"Test Stack"}}, {u'a', u'å'}}
- });
-}
-
-TEST(GlyphManager, LoadingCorrupted) {
- GlyphManagerTest test;
-
- test.fileSource.glyphsResponse = [&] (const Resource&) {
- Response response;
- response.data = std::make_unique<std::string>("CORRUPTED");
- return response;
- };
-
- test.observer.glyphsError = [&] (const FontStack& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) {
- EXPECT_EQ(fontStack, FontStack({"Test Stack"}));
- EXPECT_EQ(glyphRange, GlyphRange(0, 255));
-
- EXPECT_TRUE(error != nullptr);
- EXPECT_EQ(util::toString(error), "unknown pbf field type exception");
-
- test.end();
- };
-
- test.requestor.glyphsAvailable = [&] (GlyphMap) {
- FAIL();
- test.end();
- };
-
- test.run(
- "test/fixtures/resources/glyphs.pbf",
- GlyphDependencies {
- {{{"Test Stack"}}, {u'a', u'å'}}
- });
-}
-
-TEST(GlyphManager, LoadingCancel) {
- GlyphManagerTest test;
-
- test.fileSource.glyphsResponse = [&] (const Resource&) {
- test.end();
- return optional<Response>();
- };
-
- test.observer.glyphsLoaded = [&] (const FontStack&, const GlyphRange&) {
- FAIL() << "Should never be called";
- };
-
- test.run(
- "test/fixtures/resources/glyphs.pbf",
- GlyphDependencies {
- {{{"Test Stack"}}, {u'a', u'å'}}
- });
-}
-
-TEST(GlyphManager, LoadingInvalid) {
- GlyphManagerTest test;
-
- test.fileSource.glyphsResponse = [&] (const Resource& resource) {
- EXPECT_EQ(Resource::Kind::Glyphs, resource.kind);
- Response response;
- response.data = std::make_shared<std::string>(util::read_file("test/fixtures/resources/fake_glyphs-0-255.pbf"));
- return response;
- };
-
- test.observer.glyphsError = [&] (const FontStack&, const GlyphRange&, std::exception_ptr) {
- FAIL();
- test.end();
- };
-
- test.observer.glyphsLoaded = [&] (const FontStack& fontStack, const GlyphRange& range) {
- ASSERT_EQ(fontStack, FontStack {{"Test Stack"}});
- ASSERT_EQ(range, GlyphRange(0, 255));
- };
-
- test.requestor.glyphsAvailable = [&] (GlyphMap glyphs) {
- const auto& testPositions = glyphs.at({{"Test Stack"}});
-
- ASSERT_EQ(testPositions.size(), 2u);
- ASSERT_FALSE(bool(testPositions.at(u'A')));
- ASSERT_TRUE(bool(testPositions.at(u'E')));
-
- test.end();
- };
-
- test.run(
- "test/fixtures/resources/glyphs.pbf",
- GlyphDependencies {
- {{{"Test Stack"}}, {u'A', u'E'}}
- });
-}
diff --git a/test/text/glyph_manager.test.cpp b/test/text/glyph_manager.test.cpp
new file mode 100644
index 0000000000..a96e1b970c
--- /dev/null
+++ b/test/text/glyph_manager.test.cpp
@@ -0,0 +1,341 @@
+#include <mbgl/test/util.hpp>
+#include <mbgl/test/stub_file_source.hpp>
+
+#include <mbgl/text/glyph_manager.hpp>
+#include <mbgl/util/run_loop.hpp>
+#include <mbgl/util/string.hpp>
+#include <mbgl/util/i18n.hpp>
+#include <mbgl/util/io.hpp>
+#include <mbgl/util/logging.hpp>
+
+using namespace mbgl;
+
+// Alpha channel rendering of '中'
+static constexpr const uint8_t stubBitmap[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 95, 82, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 255, 233, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 255, 233, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 255, 227, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 22, 18, 18, 18, 18, 18, 15, 55, 255, 255, 42, 14, 18, 18, 18, 18, 18, 22, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 145, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 167, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 149, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 178, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 255, 120, 8, 15, 15, 15, 11, 51, 255, 233, 32, 11, 15, 15, 15, 8, 93, 255, 178, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 255, 100, 0, 0, 0, 0, 0, 37, 255, 233, 17, 0, 0, 0, 0, 0, 84, 255, 178, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 255, 107, 0, 0, 0, 0, 0, 37, 255, 233, 17, 0, 0, 0, 0, 0, 84, 255, 178, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 255, 107, 0, 0, 0, 0, 0, 37, 255, 233, 17, 0, 0, 0, 0, 0, 84, 255, 178, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 255, 100, 0, 0, 0, 0, 0, 32, 255, 227, 10, 0, 0, 0, 0, 0, 78, 255, 178, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 255, 170, 101, 101, 101, 101, 101, 129, 255, 255, 133, 100, 101, 101, 101, 96, 191, 255, 178, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 105, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 178, 2, 0, 0, 0, 0, 0, 0, 0, 0, 1, 156, 255, 172, 110, 116, 116, 116, 115, 140, 255, 242, 127, 116, 116, 116, 116, 110, 163, 255, 169, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 145, 255, 108, 0, 0, 0, 0, 0, 32, 255, 227, 10, 0, 0, 0, 0, 0, 89, 255, 194, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 27, 16, 0, 0, 0, 0, 0, 37, 255, 233, 17, 0, 0, 0, 0, 0, 14, 27, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 255, 233, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 255, 233, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 255, 233, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 255, 233, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 255, 233, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 205, 167, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+// SDF transformation of '中'
+static constexpr const uint8_t sdfBitmap[] = {0, 0, 0, 0, 0, 0, 0, 0, 19, 48, 76, 101, 119, 127, 127, 127, 126, 118, 100, 75, 48, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 19, 28, 31, 31, 31, 31, 59, 90, 119, 145, 158, 159, 159, 156, 144, 118, 89, 59, 31, 31, 31, 31, 28, 19, 5, 0, 0, 0, 10, 31, 48, 59, 63, 63, 63, 63, 63, 95, 127, 158, 182, 187, 186, 176, 156, 126, 94, 63, 63, 63, 63, 63, 59, 48, 31, 10, 0, 5, 31, 55, 75, 89, 94, 95, 95, 95, 95, 95, 127, 158, 181, 223, 204, 177, 157, 126, 95, 95, 95, 95, 95, 95, 89, 75, 55, 31, 10, 19, 48, 75, 100, 118, 126, 126, 126, 126, 126, 126, 127, 157, 180, 223, 204, 177, 157, 126, 126, 126, 126, 126, 126, 126, 119, 100, 75, 55, 31, 28, 59, 89, 118, 144, 156, 157, 157, 157, 157, 157, 157, 157, 180, 223, 204, 177, 156, 157, 157, 157, 157, 157, 157, 156, 144, 119, 100, 75, 48, 31, 63, 94, 126, 156, 176, 178, 178, 178, 178, 178, 178, 177, 182, 223, 223, 181, 177, 178, 178, 178, 178, 178, 178, 177, 156, 144, 118, 89, 59, 32, 64, 96, 128, 159, 193, 223, 223, 223, 223, 223, 223, 223, 223, 236, 236, 223, 223, 223, 223, 223, 223, 223, 223, 196, 176, 156, 126, 94, 63, 32, 64, 96, 128, 159, 194, 223, 223, 223, 223, 223, 223, 223, 223, 236, 226, 223, 223, 223, 223, 223, 223, 223, 224, 198, 176, 156, 126, 94, 63, 32, 64, 96, 127, 159, 187, 223, 190, 176, 177, 177, 177, 177, 182, 223, 204, 179, 177, 177, 177, 177, 176, 187, 223, 198, 176, 156, 126, 94, 63, 32, 64, 95, 127, 159, 186, 223, 188, 159, 156, 156, 156, 157, 180, 223, 204, 177, 157, 156, 156, 156, 159, 186, 223, 198, 176, 156, 126, 94, 63, 32, 64, 95, 127, 159, 186, 223, 189, 159, 127, 126, 127, 157, 180, 223, 204, 177, 157, 126, 126, 127, 159, 186, 223, 198, 176, 156, 126, 94, 63, 32, 64, 95, 127, 159, 186, 223, 189, 159, 127, 127, 127, 157, 180, 223, 204, 177, 157, 127, 127, 127, 159, 186, 223, 198, 176, 156, 126, 94, 63, 32, 64, 95, 127, 159, 186, 223, 188, 159, 159, 159, 159, 159, 179, 223, 204, 177, 159, 159, 159, 159, 159, 185, 223, 198, 176, 156, 126, 94, 63, 48, 75, 100, 127, 159, 186, 223, 197, 188, 188, 188, 188, 188, 191, 223, 223, 192, 188, 188, 188, 188, 187, 199, 224, 198, 176, 156, 126, 94, 63, 59, 89, 118, 143, 159, 188, 223, 224, 223, 223, 223, 223, 223, 223, 236, 226, 223, 223, 223, 223, 223, 223, 223, 224, 198, 176, 156, 126, 94, 63, 63, 94, 126, 156, 175, 195, 223, 197, 189, 190, 190, 190, 190, 193, 223, 206, 191, 190, 190, 190, 190, 189, 196, 223, 196, 176, 156, 126, 94, 63, 59, 89, 118, 143, 159, 193, 223, 189, 159, 159, 159, 159, 159, 179, 223, 204, 177, 159, 159, 159, 159, 159, 186, 223, 200, 176, 156, 126, 94, 63, 48, 75, 100, 126, 156, 176, 179, 177, 156, 127, 127, 127, 157, 180, 223, 204, 177, 157, 127, 127, 127, 156, 177, 179, 178, 157, 144, 118, 89, 59, 31, 59, 89, 118, 144, 156, 157, 156, 144, 119, 96, 127, 157, 180, 223, 204, 177, 157, 126, 96, 119, 144, 156, 157, 157, 144, 119, 100, 75, 48, 19, 48, 75, 100, 118, 126, 126, 126, 119, 100, 95, 127, 157, 180, 223, 204, 177, 157, 126, 95, 100, 119, 126, 126, 126, 119, 100, 76, 55, 31, 5, 31, 55, 75, 89, 94, 95, 95, 89, 75, 95, 127, 157, 180, 223, 204, 177, 157, 126, 95, 75, 89, 95, 95, 95, 90, 76, 55, 31, 10, 0, 10, 31, 48, 59, 63, 63, 63, 59, 63, 95, 127, 157, 180, 223, 204, 177, 157, 126, 95, 63, 59, 63, 63, 63, 59, 48, 31, 10, 0, 0, 0, 5, 19, 28, 31, 31, 31, 31, 63, 95, 127, 157, 180, 223, 204, 177, 157, 126, 95, 63, 31, 31, 31, 31, 28, 19, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 64, 95, 127, 159, 184, 201, 196, 176, 156, 126, 94, 63, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 60, 90, 120, 146, 159, 159, 159, 156, 144, 118, 89, 59, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 49, 76, 101, 120, 127, 128, 128, 126, 118, 100, 75, 48, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 32, 56, 76, 90, 95, 96, 96, 94, 89, 75, 55, 31, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 32, 49, 60, 64, 64, 64, 63, 59, 48, 31, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 19, 29, 32, 32, 32, 31, 28, 19, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+static constexpr const size_t stubBitmapLength = 900;
+
+class StubLocalGlyphRasterizer : public LocalGlyphRasterizer {
+public:
+ bool canRasterizeGlyph(const FontStack&, GlyphID glyphID) {
+ return util::i18n::allowsIdeographicBreaking(glyphID);
+ }
+
+ Glyph rasterizeGlyph(const FontStack&, GlyphID glyphID) {
+ Glyph stub;
+ stub.id = glyphID;
+
+ stub.metrics.width = 24;
+ stub.metrics.height = 24;
+ stub.metrics.left = 0;
+ stub.metrics.top = -8;
+ stub.metrics.advance = 24;
+
+ stub.bitmap = AlphaImage(Size(30, 30), stubBitmap, stubBitmapLength);
+
+ return stub;
+ }
+};
+
+class StubGlyphManagerObserver : public GlyphManagerObserver {
+public:
+ void onGlyphsLoaded(const FontStack& fontStack, const GlyphRange& glyphRange) override {
+ if (glyphsLoaded) glyphsLoaded(fontStack, glyphRange);
+ }
+
+ void onGlyphsError(const FontStack& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) override {
+ if (glyphsError) glyphsError(fontStack, glyphRange, error);
+ }
+
+ std::function<void (const FontStack&, const GlyphRange&)> glyphsLoaded;
+ std::function<void (const FontStack&, const GlyphRange&, std::exception_ptr)> glyphsError;
+};
+
+class StubGlyphRequestor : public GlyphRequestor {
+public:
+ void onGlyphsAvailable(GlyphMap glyphs) override {
+ if (glyphsAvailable) glyphsAvailable(std::move(glyphs));
+ }
+
+ std::function<void (GlyphMap)> glyphsAvailable;
+};
+
+class GlyphManagerTest {
+public:
+ util::RunLoop loop;
+ StubFileSource fileSource;
+ StubGlyphManagerObserver observer;
+ StubGlyphRequestor requestor;
+ GlyphManager glyphManager{ fileSource, std::make_unique<StubLocalGlyphRasterizer>() };
+
+ void run(const std::string& url, GlyphDependencies dependencies) {
+ // Squelch logging.
+ Log::setObserver(std::make_unique<Log::NullObserver>());
+
+ glyphManager.setURL(url);
+ glyphManager.setObserver(&observer);
+ glyphManager.getGlyphs(requestor, std::move(dependencies));
+
+ loop.run();
+ }
+
+ void end() {
+ loop.stop();
+ }
+};
+
+TEST(GlyphManager, LoadingSuccess) {
+ GlyphManagerTest test;
+
+ test.fileSource.glyphsResponse = [&] (const Resource& resource) {
+ EXPECT_EQ(Resource::Kind::Glyphs, resource.kind);
+ Response response;
+ response.data = std::make_shared<std::string>(util::read_file("test/fixtures/resources/glyphs.pbf"));
+ return response;
+ };
+
+ test.observer.glyphsError = [&] (const FontStack&, const GlyphRange&, std::exception_ptr) {
+ FAIL();
+ test.end();
+ };
+
+ test.observer.glyphsLoaded = [&] (const FontStack& fontStack, const GlyphRange& range) {
+ ASSERT_EQ(fontStack, FontStack {{"Test Stack"}});
+ ASSERT_EQ(range, GlyphRange(0, 255));
+ };
+
+ test.requestor.glyphsAvailable = [&] (GlyphMap glyphs) {
+ const auto& testPositions = glyphs.at({{"Test Stack"}});
+
+ ASSERT_EQ(testPositions.size(), 3u);
+ ASSERT_EQ(testPositions.count(u'a'), 1u);
+ ASSERT_EQ(testPositions.count(u'å'), 1u);
+ ASSERT_EQ(testPositions.count(u' '), 1u);
+ ASSERT_TRUE(bool(testPositions.at(u' ')));
+
+ test.end();
+ };
+
+ test.run(
+ "test/fixtures/resources/glyphs.pbf",
+ GlyphDependencies {
+ {{{"Test Stack"}}, {u'a', u'å', u' '}}
+ });
+}
+
+TEST(GlyphManager, LoadingFail) {
+ GlyphManagerTest test;
+
+ test.fileSource.glyphsResponse = [&] (const Resource&) {
+ Response response;
+ response.error = std::make_unique<Response::Error>(
+ Response::Error::Reason::Other,
+ "Failed by the test case");
+ return response;
+ };
+
+ test.observer.glyphsError = [&] (const FontStack& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) {
+ EXPECT_EQ(fontStack, FontStack({"Test Stack"}));
+ EXPECT_EQ(glyphRange, GlyphRange(0, 255));
+
+ EXPECT_TRUE(error != nullptr);
+ EXPECT_EQ(util::toString(error), "Failed by the test case");
+
+ test.end();
+ };
+
+ test.requestor.glyphsAvailable = [&] (GlyphMap) {
+ FAIL();
+ test.end();
+ };
+
+ test.run(
+ "test/fixtures/resources/glyphs.pbf",
+ GlyphDependencies {
+ {{{"Test Stack"}}, {u'a', u'å'}}
+ });
+}
+
+TEST(GlyphManager, LoadingCorrupted) {
+ GlyphManagerTest test;
+
+ test.fileSource.glyphsResponse = [&] (const Resource&) {
+ Response response;
+ response.data = std::make_unique<std::string>("CORRUPTED");
+ return response;
+ };
+
+ test.observer.glyphsError = [&] (const FontStack& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) {
+ EXPECT_EQ(fontStack, FontStack({"Test Stack"}));
+ EXPECT_EQ(glyphRange, GlyphRange(0, 255));
+
+ EXPECT_TRUE(error != nullptr);
+ EXPECT_EQ(util::toString(error), "unknown pbf field type exception");
+
+ test.end();
+ };
+
+ test.requestor.glyphsAvailable = [&] (GlyphMap) {
+ FAIL();
+ test.end();
+ };
+
+ test.run(
+ "test/fixtures/resources/glyphs.pbf",
+ GlyphDependencies {
+ {{{"Test Stack"}}, {u'a', u'å'}}
+ });
+}
+
+TEST(GlyphManager, LoadingCancel) {
+ GlyphManagerTest test;
+
+ test.fileSource.glyphsResponse = [&] (const Resource&) {
+ test.end();
+ return optional<Response>();
+ };
+
+ test.observer.glyphsLoaded = [&] (const FontStack&, const GlyphRange&) {
+ FAIL() << "Should never be called";
+ };
+
+ test.run(
+ "test/fixtures/resources/glyphs.pbf",
+ GlyphDependencies {
+ {{{"Test Stack"}}, {u'a', u'å'}}
+ });
+}
+
+TEST(GlyphManager, LoadLocalCJKGlyph) {
+ GlyphManagerTest test;
+ int glyphResponses = 0;
+
+ test.fileSource.glyphsResponse = [&] (const Resource&) {
+ glyphResponses++;
+ return optional<Response>();
+ };
+
+ test.observer.glyphsLoaded = [&] (const FontStack&, const GlyphRange&) {
+ glyphResponses++;
+ };
+
+ test.requestor.glyphsAvailable = [&] (GlyphMap glyphs) {
+ EXPECT_EQ(glyphResponses, 0); // Local generation should prevent requesting any glyphs
+
+ const auto& testPositions = glyphs.at({{"Test Stack"}});
+
+ ASSERT_EQ(testPositions.size(), 1u);
+ ASSERT_EQ(testPositions.count(u'中'), 1u);
+
+ Immutable<Glyph> glyph = *testPositions.at(u'中');
+ EXPECT_EQ(glyph->id, u'中');
+ EXPECT_EQ(glyph->metrics.width, 24ul);
+ EXPECT_EQ(glyph->metrics.height, 24ul);
+ EXPECT_EQ(glyph->metrics.left, 0);
+ EXPECT_EQ(glyph->metrics.top, -8);
+ EXPECT_EQ(glyph->metrics.advance, 24ul);
+ EXPECT_EQ(glyph->bitmap.size, Size(30, 30));
+
+ size_t pixelCount = glyph->bitmap.size.width * glyph->bitmap.size.height;
+ for (size_t i = 0; i < pixelCount; i++) {
+ EXPECT_EQ(glyph->bitmap.data[i], sdfBitmap[i]);
+ }
+
+ test.end();
+ };
+
+ test.run(
+ "test/fixtures/resources/glyphs.pbf",
+ GlyphDependencies {
+ {{{"Test Stack"}}, {u'中'}}
+ });
+}
+
+
+TEST(GlyphManager, LoadingInvalid) {
+ GlyphManagerTest test;
+
+ test.fileSource.glyphsResponse = [&] (const Resource& resource) {
+ EXPECT_EQ(Resource::Kind::Glyphs, resource.kind);
+ Response response;
+ response.data = std::make_shared<std::string>(util::read_file("test/fixtures/resources/fake_glyphs-0-255.pbf"));
+ return response;
+ };
+
+ test.observer.glyphsError = [&] (const FontStack&, const GlyphRange&, std::exception_ptr) {
+ FAIL();
+ test.end();
+ };
+
+ test.observer.glyphsLoaded = [&] (const FontStack& fontStack, const GlyphRange& range) {
+ ASSERT_EQ(fontStack, FontStack {{"Test Stack"}});
+ ASSERT_EQ(range, GlyphRange(0, 255));
+ };
+
+ test.requestor.glyphsAvailable = [&] (GlyphMap glyphs) {
+ const auto& testPositions = glyphs.at({{"Test Stack"}});
+
+ ASSERT_EQ(testPositions.size(), 2u);
+ ASSERT_FALSE(bool(testPositions.at(u'A')));
+ ASSERT_TRUE(bool(testPositions.at(u'E')));
+
+ test.end();
+ };
+
+ test.run(
+ "test/fixtures/resources/glyphs.pbf",
+ GlyphDependencies {
+ {{{"Test Stack"}}, {u'A', u'E'}}
+ });
+}
+
+TEST(GlyphManager, ImmediateFileSource) {
+ class GlyphManagerTestSynchronous {
+ public:
+ util::RunLoop loop;
+ StubFileSource fileSource = { StubFileSource::ResponseType::Synchronous };
+ StubGlyphManagerObserver observer;
+ StubGlyphRequestor requestor;
+ GlyphManager glyphManager { fileSource };
+
+ void run(const std::string& url, GlyphDependencies dependencies) {
+ // Squelch logging.
+ Log::setObserver(std::make_unique<Log::NullObserver>());
+
+ glyphManager.setURL(url);
+ glyphManager.setObserver(&observer);
+ glyphManager.getGlyphs(requestor, std::move(dependencies));
+
+ loop.run();
+ }
+
+ void end() {
+ loop.stop();
+ }
+ };
+
+ GlyphManagerTestSynchronous test;
+
+ test.fileSource.glyphsResponse = [&] (const Resource&) {
+ Response response;
+ response.data = std::make_shared<std::string>(util::read_file("test/fixtures/resources/glyphs.pbf"));
+ return response;
+ };
+
+ test.observer.glyphsError = [&] (const FontStack&, const GlyphRange&, std::exception_ptr) {
+ FAIL();
+ test.end();
+ };
+
+ test.requestor.glyphsAvailable = [&] (GlyphMap) {
+ test.end();
+ };
+
+ test.run(
+ "test/fixtures/resources/glyphs.pbf",
+ GlyphDependencies {
+ {{{"Test Stack"}}, {u'a', u'å', u' '}}
+ });
+}
diff --git a/test/text/local_glyph_rasterizer.test.cpp b/test/text/local_glyph_rasterizer.test.cpp
new file mode 100644
index 0000000000..84c685d66f
--- /dev/null
+++ b/test/text/local_glyph_rasterizer.test.cpp
@@ -0,0 +1,86 @@
+#include <mbgl/test/util.hpp>
+#include <mbgl/test/stub_file_source.hpp>
+#include <mbgl/map/map.hpp>
+#include <mbgl/util/io.hpp>
+#include <mbgl/util/run_loop.hpp>
+#include <mbgl/util/color.hpp>
+#include <mbgl/renderer/renderer.hpp>
+#include <mbgl/gl/headless_frontend.hpp>
+#include <mbgl/util/default_thread_pool.hpp>
+#include <mbgl/style/style.hpp>
+
+/*
+ LoadLocalCJKGlyph in glyph_manager.test.cpp exercises the platform-independent
+ part of LocalGlyphRasterizer. This test actually exercises platform-dependent
+ font loading code for whatever platform it runs on. Different platforms have
+ different default fonts, so adding a new platform requires new "expected"
+ fixtures.
+
+ At the time of writing, we don't run `mbgl-test` on iOS or Android, so the only
+ supported test platform is macOS. Supporting Android would require adding a new
+ test case (probably using the "Droid" font family). iOS should theoretically
+ work -- the "PingFang" font family used below is expected to be available on
+ all iOS devices, and we use a relatively high image diff tolerance (0.05) to
+ account for small changes between the many possible variants of the PingFang
+ family.
+*/
+
+using namespace mbgl;
+
+namespace {
+
+class LocalGlyphRasterizerTest {
+public:
+ LocalGlyphRasterizerTest(const optional<std::string> fontFamily)
+ : frontend(pixelRatio, fileSource, threadPool, optional<std::string>(), GLContextMode::Unique, fontFamily)
+ {
+ }
+
+ util::RunLoop loop;
+ StubFileSource fileSource;
+ ThreadPool threadPool { 4 };
+ float pixelRatio { 1 };
+ HeadlessFrontend frontend;
+ Map map { frontend, MapObserver::nullObserver(), frontend.getSize(), pixelRatio, fileSource,
+ threadPool, MapMode::Static};
+
+ void checkRendering(const char * name) {
+ test::checkImage(std::string("test/fixtures/local_glyphs/") + name,
+ frontend.render(map), 0.05, 0.1);
+ }
+};
+
+} // end namespace
+
+#ifdef __APPLE__
+
+TEST(LocalGlyphRasterizer, PingFang) {
+ LocalGlyphRasterizerTest test(std::string("PingFang"));
+
+ test.fileSource.glyphsResponse = [&] (const Resource& resource) {
+ EXPECT_EQ(Resource::Kind::Glyphs, resource.kind);
+ Response response;
+ response.data = std::make_shared<std::string>(util::read_file("test/fixtures/resources/glyphs.pbf"));
+ return response;
+ };
+ test.map.getStyle().loadJSON(util::read_file("test/fixtures/local_glyphs/mixed.json"));
+ test.checkRendering("ping_fang");
+}
+
+#endif
+
+TEST(LocalGlyphRasterizer, NoLocal) {
+ // Expectation: without any local fonts set, and without any CJK glyphs provided,
+ // the output should just contain basic latin characters.
+ LocalGlyphRasterizerTest test({});
+
+ test.fileSource.glyphsResponse = [&] (const Resource& resource) {
+ EXPECT_EQ(Resource::Kind::Glyphs, resource.kind);
+ Response response;
+ response.data = std::make_shared<std::string>(util::read_file("test/fixtures/resources/glyphs.pbf"));
+ return response;
+ };
+ test.map.getStyle().loadJSON(util::read_file("test/fixtures/local_glyphs/mixed.json"));
+ test.checkRendering("no_local");
+}
+
diff --git a/test/tile/annotation_tile.test.cpp b/test/tile/annotation_tile.test.cpp
deleted file mode 100644
index 8f3f903925..0000000000
--- a/test/tile/annotation_tile.test.cpp
+++ /dev/null
@@ -1,95 +0,0 @@
-#include <mbgl/test/util.hpp>
-#include <mbgl/test/fake_file_source.hpp>
-
-#include <mbgl/util/default_thread_pool.hpp>
-#include <mbgl/util/run_loop.hpp>
-#include <mbgl/map/transform.hpp>
-#include <mbgl/renderer/tile_parameters.hpp>
-#include <mbgl/renderer/query.hpp>
-#include <mbgl/text/collision_tile.hpp>
-#include <mbgl/geometry/feature_index.hpp>
-#include <mbgl/annotation/annotation_manager.hpp>
-#include <mbgl/annotation/annotation_tile.hpp>
-#include <mbgl/renderer/image_manager.hpp>
-#include <mbgl/text/glyph_manager.hpp>
-#include <mbgl/renderer/backend_scope.hpp>
-#include <mbgl/gl/headless_backend.hpp>
-#include <mbgl/style/style.hpp>
-
-#include <memory>
-
-using namespace mbgl;
-
-class AnnotationTileTest {
-public:
- FakeFileSource fileSource;
- TransformState transformState;
- util::RunLoop loop;
- ThreadPool threadPool { 1 };
- style::Style style { loop, fileSource, 1 };
- AnnotationManager annotationManager { style };
- HeadlessBackend backend;
- BackendScope scope { backend };
- ImageManager imageManager;
- GlyphManager glyphManager { fileSource };
-
- TileParameters tileParameters {
- 1.0,
- MapDebugOptions(),
- transformState,
- threadPool,
- fileSource,
- MapMode::Continuous,
- annotationManager,
- imageManager,
- glyphManager,
- 0
- };
-};
-
-// Don't query stale collision tile
-TEST(AnnotationTile, Issue8289) {
- AnnotationTileTest test;
- AnnotationTile tile(OverscaledTileID(0, 0, 0), test.tileParameters);
-
- auto data = std::make_unique<AnnotationTileData>();
- data->addLayer("test")->addFeature(0, FeatureType::Point, GeometryCollection());
-
- // Simulate layout and placement of a symbol layer.
- tile.onLayout(GeometryTile::LayoutResult {
- std::unordered_map<std::string, std::shared_ptr<Bucket>>(),
- std::make_unique<FeatureIndex>(),
- std::move(data),
- }, 0);
-
- auto collisionTile = std::make_unique<CollisionTile>(PlacementConfig());
-
- IndexedSubfeature subfeature { 0, "", "", 0 };
- CollisionFeature feature(GeometryCoordinates(), Anchor(0, 0, 0, 0), -5, 5, -5, 5, 1, 0, style::SymbolPlacementType::Point, subfeature, CollisionFeature::AlignmentType::Curved);
- collisionTile->insertFeature(feature, 0, true);
- collisionTile->placeFeature(feature, false, false);
-
- tile.onPlacement(GeometryTile::PlacementResult {
- std::unordered_map<std::string, std::shared_ptr<Bucket>>(),
- std::move(collisionTile),
- {},
- {},
- }, 0);
-
- // Simulate a second layout with empty data.
- tile.onLayout(GeometryTile::LayoutResult {
- std::unordered_map<std::string, std::shared_ptr<Bucket>>(),
- std::make_unique<FeatureIndex>(),
- std::make_unique<AnnotationTileData>(),
- }, 0);
-
- std::unordered_map<std::string, std::vector<Feature>> result;
- GeometryCoordinates queryGeometry {{ Point<int16_t>(0, 0) }};
- TransformState transformState;
- RenderedQueryOptions options;
-
- tile.queryRenderedFeatures(result, queryGeometry, transformState, {}, options);
-
- EXPECT_TRUE(result.empty());
-}
-
diff --git a/test/tile/custom_geometry_tile.test.cpp b/test/tile/custom_geometry_tile.test.cpp
new file mode 100644
index 0000000000..21a3dd7953
--- /dev/null
+++ b/test/tile/custom_geometry_tile.test.cpp
@@ -0,0 +1,130 @@
+#include <mbgl/test/util.hpp>
+#include <mbgl/test/fake_file_source.hpp>
+#include <mbgl/test/stub_tile_observer.hpp>
+#include <mbgl/style/sources/custom_geometry_source.hpp>
+#include <mbgl/tile/custom_geometry_tile.hpp>
+#include <mbgl/style/custom_tile_loader.hpp>
+
+#include <mbgl/util/default_thread_pool.hpp>
+#include <mbgl/util/run_loop.hpp>
+#include <mbgl/map/transform.hpp>
+#include <mbgl/renderer/tile_parameters.hpp>
+#include <mbgl/style/style.hpp>
+#include <mbgl/style/layers/circle_layer.hpp>
+#include <mbgl/annotation/annotation_manager.hpp>
+#include <mbgl/renderer/image_manager.hpp>
+#include <mbgl/text/glyph_manager.hpp>
+
+#include <memory>
+
+using namespace mbgl;
+using namespace mbgl::style;
+
+class CustomTileTest {
+public:
+ FakeFileSource fileSource;
+ TransformState transformState;
+ util::RunLoop loop;
+ ThreadPool threadPool { 1 };
+ style::Style style { loop, fileSource, 1 };
+ AnnotationManager annotationManager { style };
+ ImageManager imageManager;
+ GlyphManager glyphManager { fileSource };
+
+ TileParameters tileParameters {
+ 1.0,
+ MapDebugOptions(),
+ transformState,
+ threadPool,
+ fileSource,
+ MapMode::Continuous,
+ annotationManager,
+ imageManager,
+ glyphManager,
+ 0
+ };
+};
+
+TEST(CustomGeometryTile, InvokeFetchTile) {
+ CustomTileTest test;
+
+ CircleLayer layer("circle", "source");
+
+ mapbox::geometry::feature_collection<double> features;
+ features.push_back(mapbox::geometry::feature<double> {
+ mapbox::geometry::point<double>(0, 0)
+ });
+ CustomTileLoader loader([&](const CanonicalTileID& tileId) {
+ EXPECT_EQ(tileId, CanonicalTileID(0,0,0));
+ test.loop.stop();
+ }, [&](const CanonicalTileID&) {
+
+ });
+ auto mb =std::make_shared<Mailbox>(*Scheduler::GetCurrent());
+ ActorRef<CustomTileLoader> loaderActor(loader, mb);
+
+ CustomGeometryTile tile(OverscaledTileID(0, 0, 0), "source", test.tileParameters, CustomGeometrySource::TileOptions(),
+ loaderActor);
+
+ tile.setNecessity(TileNecessity::Required);
+
+ test.loop.run();
+}
+
+TEST(CustomGeometryTile, InvokeCancelTile) {
+ CustomTileTest test;
+
+ CircleLayer layer("circle", "source");
+
+ mapbox::geometry::feature_collection<double> features;
+ features.push_back(mapbox::geometry::feature<double> {
+ mapbox::geometry::point<double>(0, 0)
+ });
+
+ CustomTileLoader loader([&](const CanonicalTileID&) { }, [&](const CanonicalTileID& tileId) {
+ EXPECT_EQ(tileId, CanonicalTileID(0,0,0));
+ test.loop.stop();
+ });
+ auto mb =std::make_shared<Mailbox>(*Scheduler::GetCurrent());
+ ActorRef<CustomTileLoader> loaderActor(loader, mb);
+
+ CustomGeometryTile tile(OverscaledTileID(0, 0, 0), "source", test.tileParameters, CustomGeometrySource::TileOptions(),
+ loaderActor);
+
+ tile.setNecessity(TileNecessity::Required);
+ tile.setNecessity(TileNecessity::Optional);
+ test.loop.run();
+}
+
+TEST(CustomGeometryTile, InvokeTileChanged) {
+ CustomTileTest test;
+
+ CircleLayer layer("circle", "source");
+
+ mapbox::geometry::feature_collection<double> features;
+ features.push_back(mapbox::geometry::feature<double> {
+ mapbox::geometry::point<double>(0, 0)
+ });
+
+ CustomTileLoader loader(nullptr, nullptr);
+ auto mb =std::make_shared<Mailbox>(*Scheduler::GetCurrent());
+ ActorRef<CustomTileLoader> loaderActor(loader, mb);
+
+ CustomGeometryTile tile(OverscaledTileID(0, 0, 0), "source", test.tileParameters, CustomGeometrySource::TileOptions(),
+ loaderActor);
+
+ StubTileObserver observer;
+ observer.tileChanged = [&] (const Tile&) {
+ // Once present, the bucket should never "disappear", which would cause
+ // flickering.
+ ASSERT_NE(nullptr, tile.getBucket(*layer.baseImpl));
+ };
+
+ tile.setLayers({{ layer.baseImpl }});
+ tile.setObserver(&observer);
+ tile.setTileData(features);
+
+ while (!tile.isComplete()) {
+ test.loop.runOnce();
+ }
+}
diff --git a/test/tile/geojson_tile.test.cpp b/test/tile/geojson_tile.test.cpp
index 953c3b8a5f..c05e04bc8d 100644
--- a/test/tile/geojson_tile.test.cpp
+++ b/test/tile/geojson_tile.test.cpp
@@ -66,7 +66,6 @@ TEST(GeoJSONTile, Issue7648) {
tile.setLayers({{ layer.baseImpl }});
tile.setObserver(&observer);
- tile.setPlacementConfig({});
while (!tile.isComplete()) {
test.loop.runOnce();
@@ -93,7 +92,6 @@ TEST(GeoJSONTile, Issue9927) {
GeoJSONTile tile(OverscaledTileID(0, 0, 0), "source", test.tileParameters, features);
tile.setLayers({{ layer.baseImpl }});
- tile.setPlacementConfig({});
while (!tile.isComplete()) {
test.loop.runOnce();
diff --git a/test/tile/raster_dem_tile.test.cpp b/test/tile/raster_dem_tile.test.cpp
new file mode 100644
index 0000000000..5a6f5a8c9a
--- /dev/null
+++ b/test/tile/raster_dem_tile.test.cpp
@@ -0,0 +1,93 @@
+#include <mbgl/test/util.hpp>
+#include <mbgl/test/fake_file_source.hpp>
+#include <mbgl/tile/raster_dem_tile.hpp>
+#include <mbgl/tile/tile_loader_impl.hpp>
+
+#include <mbgl/style/style.hpp>
+#include <mbgl/util/default_thread_pool.hpp>
+#include <mbgl/util/run_loop.hpp>
+#include <mbgl/map/transform.hpp>
+#include <mbgl/annotation/annotation_manager.hpp>
+#include <mbgl/renderer/tile_parameters.hpp>
+#include <mbgl/renderer/buckets/hillshade_bucket.hpp>
+#include <mbgl/renderer/image_manager.hpp>
+#include <mbgl/text/glyph_manager.hpp>
+
+using namespace mbgl;
+
+class RasterDEMTileTest {
+public:
+ FakeFileSource fileSource;
+ TransformState transformState;
+ util::RunLoop loop;
+ ThreadPool threadPool { 1 };
+ style::Style style { loop, fileSource, 1 };
+ AnnotationManager annotationManager { style };
+ ImageManager imageManager;
+ GlyphManager glyphManager { fileSource };
+ Tileset tileset { { "https://example.com" }, { 0, 22 }, "none" };
+
+ TileParameters tileParameters {
+ 1.0,
+ MapDebugOptions(),
+ transformState,
+ threadPool,
+ fileSource,
+ MapMode::Continuous,
+ annotationManager,
+ imageManager,
+ glyphManager,
+ 0
+ };
+};
+
+TEST(RasterDEMTile, setError) {
+ RasterDEMTileTest test;
+ RasterDEMTile tile(OverscaledTileID(0, 0, 0), test.tileParameters, test.tileset);
+ tile.setError(std::make_exception_ptr(std::runtime_error("test")));
+ EXPECT_FALSE(tile.isRenderable());
+ EXPECT_TRUE(tile.isLoaded());
+ EXPECT_TRUE(tile.isComplete());
+}
+
+TEST(RasterDEMTile, onError) {
+ RasterDEMTileTest test;
+ RasterDEMTile tile(OverscaledTileID(0, 0, 0), test.tileParameters, test.tileset);
+ tile.onError(std::make_exception_ptr(std::runtime_error("test")), 0);
+ EXPECT_FALSE(tile.isRenderable());
+ EXPECT_TRUE(tile.isLoaded());
+ EXPECT_TRUE(tile.isComplete());
+}
+
+TEST(RasterDEMTile, onParsed) {
+ RasterDEMTileTest test;
+ RasterDEMTile tile(OverscaledTileID(0, 0, 0), test.tileParameters, test.tileset);
+ tile.onParsed(std::make_unique<HillshadeBucket>(PremultipliedImage({16, 16}), Tileset::DEMEncoding::Mapbox), 0);
+ EXPECT_TRUE(tile.isRenderable());
+ EXPECT_TRUE(tile.isLoaded());
+ EXPECT_TRUE(tile.isComplete());
+
+ // Make sure that once we've had a renderable tile and then receive erroneous data, we retain
+ // the previously rendered data and keep the tile renderable.
+ tile.setError(std::make_exception_ptr(std::runtime_error("Connection offline")));
+ EXPECT_TRUE(tile.isRenderable());
+ EXPECT_TRUE(tile.isLoaded());
+ EXPECT_TRUE(tile.isComplete());
+
+ // Then simulate a parsing failure and make sure that we keep it renderable in this situation
+ // as well.
+ tile.onError(std::make_exception_ptr(std::runtime_error("Parse error")), 0);
+ ASSERT_TRUE(tile.isRenderable());
+ EXPECT_TRUE(tile.isLoaded());
+ EXPECT_TRUE(tile.isComplete());
+}
+
+TEST(RasterDEMTile, onParsedEmpty) {
+ RasterDEMTileTest test;
+ RasterDEMTile tile(OverscaledTileID(0, 0, 0), test.tileParameters, test.tileset);
+ tile.onParsed(nullptr, 0);
+ EXPECT_FALSE(tile.isRenderable());
+ EXPECT_TRUE(tile.isLoaded());
+ EXPECT_TRUE(tile.isComplete());
+}
+
diff --git a/test/tile/vector_tile.test.cpp b/test/tile/vector_tile.test.cpp
index 7e8b659b7a..9d42f7ae74 100644
--- a/test/tile/vector_tile.test.cpp
+++ b/test/tile/vector_tile.test.cpp
@@ -11,7 +11,6 @@
#include <mbgl/renderer/tile_parameters.hpp>
#include <mbgl/renderer/buckets/symbol_bucket.hpp>
#include <mbgl/renderer/query.hpp>
-#include <mbgl/text/collision_tile.hpp>
#include <mbgl/geometry/feature_index.hpp>
#include <mbgl/annotation/annotation_manager.hpp>
#include <mbgl/renderer/image_manager.hpp>
@@ -71,12 +70,13 @@ TEST(VectorTile, Issue7615) {
VectorTile tile(OverscaledTileID(0, 0, 0), "source", test.tileParameters, test.tileset);
style::SymbolLayer symbolLayer("symbol", "source");
+ std::vector<SymbolInstance> symbolInstances;
auto symbolBucket = std::make_shared<SymbolBucket>(
style::SymbolLayoutProperties::PossiblyEvaluated(),
std::map<
std::string,
std::pair<style::IconPaintProperties::PossiblyEvaluated, style::TextPaintProperties::PossiblyEvaluated>>(),
- 16.0f, 1.0f, 0.0f, false, false);
+ 16.0f, 1.0f, 0.0f, false, false, false, std::move(symbolInstances));
// Simulate placement of a symbol layer.
tile.onPlacement(GeometryTile::PlacementResult {
@@ -84,7 +84,6 @@ TEST(VectorTile, Issue7615) {
symbolLayer.getID(),
symbolBucket
}},
- nullptr,
{},
{},
}, 0);
diff --git a/test/util/geo.test.cpp b/test/util/geo.test.cpp
index d0d01b6f88..6832ba3486 100644
--- a/test/util/geo.test.cpp
+++ b/test/util/geo.test.cpp
@@ -220,3 +220,128 @@ TEST(LatLngBounds, FromTileID) {
ASSERT_DOUBLE_EQ(util::LATITUDE_MAX, bounds.north());
}
}
+
+TEST(LatLngBounds, ContainsPoint) {
+ auto bounds = LatLngBounds::hull({50.0, -100.0},{-50.0, 100.0});
+
+ EXPECT_FALSE(bounds.contains(LatLng{0.0, 170.0}));
+ EXPECT_FALSE(bounds.contains(LatLng{0.0, -170.0}));
+ EXPECT_TRUE(bounds.contains(LatLng{0.0, -100.0}));
+ EXPECT_TRUE(bounds.contains(LatLng{-50.0, 100.0}));
+ EXPECT_FALSE(bounds.contains(LatLng{0.0, 365.0}));
+}
+
+TEST(LatLngBounds, ContainsPoint_Wrapped) {
+ auto bounds = LatLngBounds::hull({50.0, -160.0}, {-50.0, 160.0});
+ EXPECT_FALSE(bounds.contains(LatLng{0.0, 170.0}));
+ EXPECT_FALSE(bounds.contains(LatLng{0.0, -170.0}));
+
+ bounds = LatLngBounds::hull({50.0, -200}, {-50.0, -160.0});
+ EXPECT_FALSE(bounds.contains(LatLng{0.0, 170.0}));
+ EXPECT_TRUE(bounds.contains(LatLng{0.0, 170.0}, LatLng::Wrapped));
+ EXPECT_TRUE(bounds.contains(LatLng{0.0, -170.0}));
+ EXPECT_TRUE(bounds.contains(LatLng{0.0, -170.0}, LatLng::Wrapped));
+ EXPECT_FALSE(bounds.contains(LatLng{0.0, 190.0}));
+ EXPECT_TRUE(bounds.contains(LatLng{0.0, 190.0}, LatLng::Wrapped));
+ EXPECT_FALSE(bounds.contains(LatLng{0.0, 541.0}));
+ EXPECT_TRUE(bounds.contains(LatLng{0.0, 541.0}, LatLng::Wrapped));
+}
+
+TEST(LatLngBounds, ContainsBounds) {
+ auto bounds = LatLngBounds::hull({ 50.0, -160.0 }, {-50.0, 160.0});
+ EXPECT_TRUE(bounds.contains(bounds));
+
+ auto innerBounds = LatLngBounds::hull({10.0, -180.0}, {-10.0, -170.0});
+ EXPECT_FALSE(bounds.contains(innerBounds));
+ EXPECT_FALSE(innerBounds.contains(bounds));
+
+ innerBounds = LatLngBounds::hull({10, 120.0}, {-60, 125.0});
+ EXPECT_FALSE(bounds.contains(innerBounds));
+ EXPECT_FALSE(innerBounds.contains(bounds));
+
+ innerBounds = LatLngBounds::hull({10, 120.0}, {-10, 125.0});
+ EXPECT_TRUE(bounds.contains(innerBounds));
+ EXPECT_FALSE(innerBounds.contains(bounds));
+
+}
+
+TEST(LatLngBounds, ContainsBounds_Wrapped) {
+ auto bounds = LatLngBounds::hull({50.0, -200}, {-50.0, -160.0});
+
+ auto inner = LatLngBounds::hull({10.0, -180.0}, {-10.0, -170.0});
+ EXPECT_TRUE(bounds.contains(inner));
+ EXPECT_TRUE(bounds.contains(inner, LatLng::Wrapped));
+
+ inner = LatLngBounds::hull({10.0, 180.0}, {-10.0, 190.0});
+ EXPECT_FALSE(bounds.contains(inner));
+ EXPECT_TRUE(bounds.contains(inner, LatLng::Wrapped));
+
+ inner = LatLngBounds::hull({10.0, 190.0}, {-10.0, 220.0});
+ EXPECT_FALSE(bounds.contains(inner));
+ EXPECT_FALSE(bounds.contains(inner, LatLng::Wrapped));
+
+ auto unwrapped = LatLngBounds::hull({10.0, 170.0}, { -10.0, -175.0});
+ EXPECT_FALSE(bounds.contains(unwrapped));
+ EXPECT_FALSE(bounds.contains(unwrapped, LatLng::Wrapped));
+
+ unwrapped = LatLngBounds::hull({10.0, 0.0} , {-10.0, -10.0});
+ EXPECT_FALSE(bounds.contains(unwrapped));
+ EXPECT_FALSE(bounds.contains(unwrapped, LatLng::Wrapped));
+
+ unwrapped = LatLngBounds::hull({10.0, -165.0}, {-10.0, -180.0});
+ EXPECT_TRUE(bounds.contains(unwrapped));
+ EXPECT_TRUE(bounds.contains(unwrapped, LatLng::Wrapped));
+
+ unwrapped = LatLngBounds::hull({10.0, 180.0}, {-10.0, 160.0});
+ EXPECT_FALSE(bounds.contains(unwrapped));
+ EXPECT_TRUE(bounds.contains(unwrapped, LatLng::Wrapped));
+
+ unwrapped = LatLngBounds::hull({10.0, 540.0}, {-10.0, 560.0});
+ EXPECT_FALSE(bounds.contains(unwrapped));
+ EXPECT_TRUE(bounds.contains(unwrapped, LatLng::Wrapped));
+}
+
+TEST(LatLngBounds, ContainsTileIDs) {
+ LatLngBounds bounds(CanonicalTileID(4,2,6));
+ LatLngBounds innerBounds(CanonicalTileID(9,82,197));
+ EXPECT_TRUE(bounds.contains(innerBounds));
+ EXPECT_FALSE(bounds.contains(LatLngBounds{ CanonicalTileID(3, 1, 0) }));
+}
+
+TEST(LatLngBounds, Intersects) {
+ auto bounds = LatLngBounds::hull({ 50.0, -160.0 }, { -50.0, 160.0 });
+ EXPECT_TRUE(bounds.intersects(bounds));
+
+ auto other = LatLngBounds::hull({50.0, -160.0}, {10, 160.0});
+ EXPECT_TRUE(bounds.intersects(other));
+ EXPECT_TRUE(other.intersects(bounds));
+}
+
+TEST(LatLngBounds, Intersects_Wrapped) {
+ auto bounds = LatLngBounds::hull({50.0, -200.0}, {-50.0, -160.0});
+ EXPECT_TRUE(bounds.intersects(bounds));
+
+ auto other = LatLngBounds::hull({50.0, -150.0}, {10, 160.0});
+ EXPECT_FALSE(bounds.intersects(other));
+ EXPECT_FALSE(other.intersects(bounds));
+ EXPECT_FALSE(bounds.intersects(other, LatLng::Wrapped));
+ EXPECT_FALSE(other.intersects(bounds, LatLng::Wrapped));
+
+ other = LatLngBounds::hull({10.0, -150.0}, {-10.0, -210.0});
+ EXPECT_TRUE(bounds.intersects(other));
+ EXPECT_TRUE(bounds.intersects(other, LatLng::Wrapped));
+ EXPECT_TRUE(other.intersects(bounds));
+ EXPECT_TRUE(other.intersects(bounds, LatLng::Wrapped));
+
+ other = LatLngBounds::hull({10.0, 150.0}, {-10.0, 210.0});
+ EXPECT_FALSE(bounds.intersects(other));
+ EXPECT_FALSE(other.intersects(bounds));
+ EXPECT_TRUE(bounds.intersects(other, LatLng::Wrapped));
+ EXPECT_TRUE(other.intersects(bounds, LatLng::Wrapped));
+
+ other = LatLngBounds::hull({10.0, 195.0}, {-10.0, 300.0});
+ EXPECT_FALSE(bounds.intersects(other));
+ EXPECT_FALSE(other.intersects(bounds));
+ EXPECT_TRUE(bounds.intersects(other, LatLng::Wrapped));
+ EXPECT_TRUE(other.intersects(bounds, LatLng::Wrapped));
+}
diff --git a/test/util/grid_index.test.cpp b/test/util/grid_index.test.cpp
new file mode 100644
index 0000000000..b0a4e581a3
--- /dev/null
+++ b/test/util/grid_index.test.cpp
@@ -0,0 +1,53 @@
+#include <mbgl/util/grid_index.hpp>
+#include <mbgl/util/grid_index.cpp>
+
+#include <mbgl/test/util.hpp>
+
+using namespace mbgl;
+
+TEST(GridIndex, IndexesFeatures) {
+ GridIndex<int16_t> grid(100, 100, 10);
+ grid.insert(0, {{4, 10}, {6, 30}});
+ grid.insert(1, {{4, 10}, {30, 12}});
+ grid.insert(2, {{-10, 30}, {5, 35}});
+
+ EXPECT_EQ(grid.query({{4, 10}, {5, 11}}), (std::vector<int16_t>{0, 1}));
+ EXPECT_EQ(grid.query({{24, 10}, {25, 11}}), (std::vector<int16_t>{1}));
+ EXPECT_EQ(grid.query({{40, 40}, {100, 100}}), (std::vector<int16_t>{}));
+ EXPECT_EQ(grid.query({{-6, 0}, {3, 100}}), (std::vector<int16_t>{2}));
+ EXPECT_EQ(grid.query({{-1000, -1000}, {1000, 1000}}), (std::vector<int16_t>{0, 1, 2}));
+}
+
+TEST(GridIndex, DuplicateKeys) {
+ GridIndex<int16_t> grid(100, 100, 10);
+ #define KEY 123
+ grid.insert(KEY, {{3, 4}, {4, 4}});
+ grid.insert(KEY, {{13, 13}, {14, 14}});
+ grid.insert(KEY, {{23, 23}, {24, 24}});
+
+ EXPECT_EQ(grid.query({{0, 0}, {30, 30}}), (std::vector<int16_t>{KEY, KEY, KEY}));
+}
+
+TEST(GridIndex, CircleCircle) {
+ GridIndex<int16_t> grid(100, 100, 10);
+ grid.insert(0, {{50, 50}, 10});
+ grid.insert(1, {{60, 60}, 15});
+ grid.insert(2, {{-10, 110}, 20});
+
+ EXPECT_TRUE(grid.hitTest({{55, 55}, 2}));
+ EXPECT_FALSE(grid.hitTest({{10, 10}, 10}));
+ EXPECT_TRUE(grid.hitTest({{0, 100}, 10}));
+ EXPECT_TRUE(grid.hitTest({{80, 60}, 10}));
+}
+
+TEST(GridIndex, CircleBox) {
+ GridIndex<int16_t> grid(100, 100, 10);
+ grid.insert(0, {{50, 50}, 10});
+ grid.insert(1, {{60, 60}, 15});
+ grid.insert(2, {{-10, 110}, 20});
+
+ EXPECT_EQ(grid.query({{45, 45}, {55, 55}}), (std::vector<int16_t>{0, 1}));
+ EXPECT_EQ(grid.query({{0, 0}, {30, 30}}), (std::vector<int16_t>{}));
+ EXPECT_EQ(grid.query({{0, 80}, {20, 100}}), (std::vector<int16_t>{2}));
+}
+
diff --git a/test/util/mapbox.test.cpp b/test/util/mapbox.test.cpp
index cdbd85118f..301475dae4 100644
--- a/test/util/mapbox.test.cpp
+++ b/test/util/mapbox.test.cpp
@@ -7,6 +7,7 @@
#include <stdexcept>
using namespace mbgl;
+using SourceType = mbgl::style::SourceType;
TEST(Mapbox, SourceURL) {
EXPECT_EQ(
diff --git a/test/util/memory.test.cpp b/test/util/memory.test.cpp
index 54763cd9db..6befb521f0 100644
--- a/test/util/memory.test.cpp
+++ b/test/util/memory.test.cpp
@@ -72,7 +72,7 @@ TEST(Memory, Vector) {
HeadlessFrontend frontend { { 256, 256 }, ratio, test.fileSource, test.threadPool };
Map map(frontend, MapObserver::nullObserver(), frontend.getSize(), ratio, test.fileSource,
- test.threadPool, MapMode::Still);
+ test.threadPool, MapMode::Static);
map.setZoom(16); // more map features
map.getStyle().loadURL("mapbox://streets");
@@ -85,7 +85,7 @@ TEST(Memory, Raster) {
HeadlessFrontend frontend { { 256, 256 }, ratio, test.fileSource, test.threadPool };
Map map(frontend, MapObserver::nullObserver(), frontend.getSize(), ratio, test.fileSource,
- test.threadPool, MapMode::Still);
+ test.threadPool, MapMode::Static);
map.getStyle().loadURL("mapbox://satellite");
frontend.render(map);
@@ -122,7 +122,7 @@ TEST(Memory, Footprint) {
public:
FrontendAndMap(MemoryTest& test_, const char* style)
: frontend(Size{ 256, 256 }, 2, test_.fileSource, test_.threadPool)
- , map(frontend, MapObserver::nullObserver(), frontend.getSize(), 2, test_.fileSource, test_.threadPool, MapMode::Still) {
+ , map(frontend, MapObserver::nullObserver(), frontend.getSize(), 2, test_.fileSource, test_.threadPool, MapMode::Static) {
map.setZoom(16);
map.getStyle().loadURL(style);
frontend.render(map);
diff --git a/test/util/run_loop.test.cpp b/test/util/run_loop.test.cpp
index 57bc613f9e..4d2c704421 100644
--- a/test/util/run_loop.test.cpp
+++ b/test/util/run_loop.test.cpp
@@ -50,3 +50,17 @@ TEST(RunLoop, MultipleRun) {
EXPECT_TRUE(secondTimeout);
}
+
+TEST(RunLoop, Priorities) {
+ std::vector<int> order;
+
+ RunLoop loop(RunLoop::Type::New);
+ loop.invoke([&] { order.push_back(1); });
+ loop.invoke(RunLoop::Priority::High, [&] { order.push_back(2); });
+ loop.invoke([&] { order.push_back(3); });
+ loop.invoke(RunLoop::Priority::High, [&] { order.push_back(4); });
+ loop.invoke(RunLoop::Priority::Default, [&] { loop.stop(); });
+ loop.run();
+
+ EXPECT_EQ((std::vector<int>{ 2, 4, 1, 3 }), order);
+}
diff --git a/test/util/unique_any.test.cpp b/test/util/unique_any.test.cpp
new file mode 100644
index 0000000000..9357b9c0ec
--- /dev/null
+++ b/test/util/unique_any.test.cpp
@@ -0,0 +1,186 @@
+#include <mbgl/test/util.hpp>
+
+#include <mbgl/util/unique_any.hpp>
+
+using namespace mbgl::util;
+
+class TestType {
+public:
+ TestType() : i1(0), i2(1) {
+ str[0] = 'a';
+ }
+
+ TestType(unique_any& p) : TestType() {
+ p = std::unique_ptr<TestType>(this);
+ }
+
+ //Detect moves
+ TestType(TestType&& t): i1(t.i1+1), i2(t.i2+2) {
+ str[0] = t.str[0]+1;
+ }
+
+ int i1;
+ int i2;
+ char str[256];
+};
+
+bool IsStackAllocated (const unique_any& a, const void* obj1) {
+ uintptr_t a_ptr = (uintptr_t)(&a);
+ uintptr_t obj = (uintptr_t)(obj1);
+ return (obj >= a_ptr && obj < a_ptr + sizeof(unique_any));
+};
+
+TEST(UniqueAny, Empty) {
+ EXPECT_FALSE(unique_any().has_value());
+ EXPECT_TRUE(unique_any().type() == typeid(void));
+ EXPECT_THROW(any_cast<int>(unique_any()), bad_any_cast);
+}
+
+TEST(UniqueAny, BasicTypes) {
+ unique_any i = 3;
+ EXPECT_TRUE(i.has_value());
+ EXPECT_TRUE(i.type() == typeid(int));
+ EXPECT_TRUE(IsStackAllocated(i, any_cast<int>(&i)));
+
+ auto iValue = any_cast<int>(i);
+ EXPECT_TRUE(iValue == 3);
+
+ EXPECT_TRUE(unique_any(4).has_value());
+ EXPECT_TRUE(unique_any(4).type() == typeid(int));
+
+ unique_any f = 6.2f;
+ EXPECT_TRUE(f.has_value());
+ EXPECT_TRUE(f.type() == typeid(float));
+ EXPECT_TRUE(IsStackAllocated(f, any_cast<float>(&f)));
+
+ const float fValue = any_cast<const float>(f);
+ EXPECT_TRUE(fValue == 6.2f);
+
+ EXPECT_TRUE(unique_any(1.0f).has_value());
+ EXPECT_TRUE(unique_any(1.0f).type() == typeid(float));
+
+ unique_any c = 'z';
+ EXPECT_TRUE(c.has_value());
+ EXPECT_TRUE(c.type() == typeid(char));
+ EXPECT_TRUE(IsStackAllocated(c, any_cast<char>(&c)));
+
+ EXPECT_THROW(any_cast<float>(c), bad_any_cast);
+
+ EXPECT_TRUE(unique_any('4').has_value());
+ EXPECT_TRUE(unique_any('4').type() == typeid(char));
+}
+
+TEST(UniqueAny, BasicTypes_Move) {
+ unique_any i = 3;
+ EXPECT_TRUE(i.has_value());
+ EXPECT_TRUE(i.type() == typeid(int));
+
+ unique_any f = 6.2f;
+ EXPECT_TRUE(f.has_value());
+ EXPECT_TRUE(f.type() == typeid(float));
+
+ f = std::move(i);
+ EXPECT_FALSE(i.has_value());
+ EXPECT_TRUE(i.type() == typeid(void));
+
+ EXPECT_TRUE(f.has_value());
+ EXPECT_TRUE(f.type() == typeid(int));
+
+}
+
+TEST(UniqueAny, LargeType) {
+ TestType t1;
+ unique_any u1 = unique_any(std::move(t1));
+ EXPECT_TRUE(u1.has_value());
+ EXPECT_TRUE(u1.type() == typeid(TestType));
+ EXPECT_FALSE(IsStackAllocated(u1, any_cast<TestType>(&u1)));
+
+ //TestType should be moved into owning unique_any
+ EXPECT_EQ(any_cast<TestType>(&u1)->i1, 1);
+
+ auto u2(std::move(u1));
+ EXPECT_TRUE(u2.type() == typeid(TestType));
+ EXPECT_TRUE(u1.type() == typeid(void));
+
+ //TestType should not be moved when owning unique_any is moved;
+ EXPECT_EQ(any_cast<TestType>(&u2)->i1, 1);
+
+ //TestType should be moved out of owning unique_any
+ // Note: two moves are involved in returning the moved value
+ // First out of the unique_any, and then in the return statement
+ auto t2 = any_cast<TestType>(std::move(u2));
+ EXPECT_EQ(t2.i1, 3);
+ EXPECT_TRUE(u2.type() == typeid(void));
+}
+
+TEST(UniqueAny, Pointer) {
+ auto t1 = new TestType();
+
+ auto u1 = unique_any(std::move(t1));
+ EXPECT_TRUE(u1.has_value());
+ EXPECT_TRUE(u1.type() == typeid(TestType *));
+ EXPECT_TRUE(IsStackAllocated(u1, any_cast<TestType *>(&u1)));
+
+ //Only the pointer should be moved
+ TestType * t2 = *any_cast<TestType *>(&u1);
+ EXPECT_EQ(t2->i1, 0);
+
+ unique_any u2(4);
+ std::swap(u2, u1);
+
+ EXPECT_TRUE(u1.has_value());
+ EXPECT_TRUE(u1.type() == typeid(int));
+
+ EXPECT_TRUE(u2.has_value());
+ EXPECT_TRUE(u2.type() == typeid(TestType *));
+
+ t2 = *any_cast<TestType *>(&u2);
+ EXPECT_EQ(t2->i1, 0);
+ delete t2;
+}
+
+
+TEST(UniqueAny, UniquePtr) {
+ auto t1 = std::make_unique<TestType>();
+ auto u1 = unique_any(std::move(t1));
+
+ EXPECT_EQ(t1.get(), nullptr);
+ EXPECT_TRUE(u1.has_value());
+ EXPECT_TRUE(u1.type() == typeid(std::unique_ptr<TestType>));
+
+ EXPECT_TRUE(IsStackAllocated(u1, any_cast<std::unique_ptr<TestType>>(&u1)));
+
+ auto t2 = any_cast<std::unique_ptr<TestType> >(std::move(u1));
+ EXPECT_FALSE(u1.has_value());
+
+ unique_any u2;
+ TestType * t3 = new TestType();
+ u2 = std::unique_ptr<TestType>(t3);
+ EXPECT_TRUE(u2.has_value());
+ EXPECT_TRUE(any_cast<std::unique_ptr<TestType>>(&u2)->get() == t3);
+}
+
+TEST(UniqueAny, SharedPtr) {
+
+ std::shared_ptr<int> shared(new int(3));
+ std::weak_ptr<int> weak = shared;
+ unique_any u1 = 0;
+
+ EXPECT_THROW(any_cast<float>(u1), bad_any_cast);
+
+ EXPECT_EQ(weak.use_count(), 1);
+ unique_any u2 = shared;
+ EXPECT_EQ(weak.use_count(), 2);
+
+ EXPECT_EQ(any_cast<std::unique_ptr<int>>(&u1), nullptr);
+ EXPECT_FALSE(IsStackAllocated(u1, any_cast<std::shared_ptr<TestType>>(&u1)));
+
+ u1 = std::move(u2);
+ EXPECT_EQ(weak.use_count(), 2);
+ u2.swap(u1);
+ EXPECT_EQ(weak.use_count(), 2);
+ u2 = 0;
+ EXPECT_EQ(weak.use_count(), 1);
+ shared = nullptr;
+ EXPECT_EQ(weak.use_count(), 0);
+}